├── README.md
├── css
└── main.css
├── index.html
└── js
├── adapter-latest.js
└── main.js
/README.md:
--------------------------------------------------------------------------------
1 | # Media Recorder API Demo
2 | A demo implementation of the (new) [Media Recorder API](http://w3c.github.io/mediacapture-record/MediaRecorder.html) (also known as MediaStream Recording API).
3 |
4 | Works on:
5 | * Firefox 30+
6 | * Chrome 47,48 (video only, enable experimental Web Platform features at chrome://flags)
7 | * Chrome 49+
8 |
9 | Containers & codecs:
10 | * Chrome 52+ : webm, VP8/VP9/H.264, Opus @ 48kHz
11 | * Chrome 49+ : webm, VP8/VP9, Opus @ 48kHz
12 | * Firefox 30+: webm,VP8, Vorbis @ 44.1 kHz
13 |
14 | Issues:
15 | * Pause does not stop audio recording on Chrome 49,50
16 |
17 |
18 | Links:
19 | * [Live demo of this code](https://addpipe.com/media-recorder-api-demo/)
20 |
21 | * [Article: HTML5’s Media Recorder API in Action on Chrome and Firefox](https://blog.addpipe.com/mediarecorder-api/)
22 |
23 | * [W3C Draft (Latest published version)](https://www.w3.org/TR/mediastream-recording/)
24 |
25 | * [Media Recorder API at 65% penetration thanks to Chrome](https://addpipe.com/media-recorder-api-demo/)
26 |
--------------------------------------------------------------------------------
/css/main.css:
--------------------------------------------------------------------------------
1 | body {
2 | line-height: 1.5;
3 | font-family: sans-serif;
4 | word-wrap: break-word;
5 | overflow-wrap: break-word;
6 | color:black;
7 | margin:2em;
8 | }
9 |
10 | h1 {
11 | text-decoration: underline red;
12 | text-decoration-thickness: 3px;
13 | text-underline-offset: 6px;
14 | font-size: 220%;
15 | font-weight: bold;
16 | }
17 |
18 | h2 {
19 | font-weight: bold;
20 | color: #005A9C;
21 | font-size: 140%;
22 | text-transform: uppercase;
23 | }
24 |
25 | p {
26 | margin: 1em 0;
27 | }
28 |
29 | pre {
30 | padding: 0px;
31 | border:0px;
32 | border-radius: 0px;
33 | }
34 |
35 | red {
36 | color: red;
37 | }
38 |
39 | a {
40 | color: #4183c4;
41 | font-weight: 300;
42 | text-decoration: none;
43 | }
44 |
45 | a:hover {
46 | color: #3d85c6;
47 | text-decoration: underline;
48 | }
49 |
50 | a#downloadLink {
51 | display: block;
52 | margin: 0 0 1em 0;
53 | min-height: 1.2em;
54 | }
55 |
56 | div#container {
57 | margin: 0 auto 0 auto;
58 | max-width: 720px;
59 | padding: 1em 1.5em 1.3em 1.5em;
60 | }
61 |
62 | h3 {
63 | border-top: 1px solid #eee;
64 | color: #666;
65 | font-size: 0.9em;
66 | font-weight: 500;
67 | margin: 20px 0 10px 0;
68 | padding: 10px 0 0 0;
69 | white-space: nowrap;
70 | }
71 |
72 | p#data {
73 | border-top: 1px dotted #666;
74 | font-family: Courier New, monospace;
75 | line-height: 1.3em;
76 | min-height: 6em;
77 | max-height: 1000px;
78 | overflow-y: auto;
79 | padding: 1em 0 0 0;
80 | }
81 |
82 | video {
83 | background: #222;
84 | margin: 10px auto;
85 | width: 320px;
86 | height: 240px;
87 | }
88 |
89 | #controls {
90 | display: flex;
91 | margin-top: 2rem;
92 | max-width: 28em;
93 | }
94 |
95 | button {
96 | flex-grow: 1;
97 | height: 3rem;
98 | min-width: 10rem;
99 | border: none;
100 | border-radius: 0.15rem;
101 | background: #ed341d;
102 | margin-left: 2px;
103 | box-shadow: inset 0 -0.15rem 0 rgba(0, 0, 0, 0.2);
104 | cursor: pointer;
105 | display: flex;
106 | justify-content: center;
107 | align-items: center;
108 | color:#ffffff;
109 | font-weight: bold;
110 | font-size: 1rem;
111 | }
112 | button:hover, button:focus {
113 | outline: none;
114 | background: #c72d1c;
115 | }
116 | button::-moz-focus-inner {
117 | border: 0;
118 | }
119 | button:active {
120 | box-shadow: inset 0 1px 0 rgba(0, 0, 0, 0.2);
121 | line-height: 3rem;
122 | }
123 | button:disabled {
124 | pointer-events: none;
125 | background: lightgray;
126 | }
127 | button:first-child {
128 | margin-left: 0;
129 | }
130 | video{
131 | width: 1280px;
132 | height: 720px;
133 | }
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Media Recorder API Demo - addpipe.com
8 |
9 |
10 |
11 |
12 |
13 |
Media Recorder API Demo
14 |
Made by the Pipe Recording Platform
15 |
The demo below uses the MediaStream Recording API and getUserMedia() to record a 1280x720 video file. For more info check out our article: Media Recorder API in Action on Chrome and Firefox.
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 | Works on:
31 | - Firefox 30 and up
- Chrome 47,48 (video only, enable experimental Web Platform features at chrome://flags)
- Chrome 49+
- Safari 14.0.2+ on macOS
- Safari on iOS 14.3+
32 | Issues:
33 | - Pause does not stop audio recording on Chrome 49,50
34 | Containers & codecs:
35 |
36 |
37 |
38 | | Chrome 47 & 48 | Chrome 49+ | Chrome 52+ | Firefox 30+ | Safari 14.0.2+ on macOS | Safari on iOS 14.3+ |
39 |
40 |
41 |
42 |
43 | Container | webm | webm | webm | webm | mp4 | mp4 |
44 |
45 |
46 | Video | VP8 | VP8/VP9 | VP8/VP9/H264 | VP8 | H264 | H264 |
47 |
48 |
49 | Audio | none | Opus @ 48kHz | Opus @ 48kHz | Vorbis @ 44.1 kHz | Stereo AAC @ 48kHz | Stereo AAC @ 44.1kHz or 48kHz |
50 |
51 |
52 |
53 | Links:
54 |
62 |
63 |
--------------------------------------------------------------------------------
/js/adapter-latest.js:
--------------------------------------------------------------------------------
1 | (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 0 && arguments[0] !== undefined ? arguments[0] : {},
62 | window = _ref.window;
63 |
64 | var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {
65 | shimChrome: true,
66 | shimFirefox: true,
67 | shimSafari: true
68 | };
69 |
70 | // Utils.
71 | var logging = utils.log;
72 | var browserDetails = utils.detectBrowser(window);
73 |
74 | var adapter = {
75 | browserDetails: browserDetails,
76 | commonShim: commonShim,
77 | extractVersion: utils.extractVersion,
78 | disableLog: utils.disableLog,
79 | disableWarnings: utils.disableWarnings,
80 | // Expose sdp as a convenience. For production apps include directly.
81 | sdp: sdp
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 | // Must be called before shimPeerConnection.
100 | commonShim.shimAddIceCandidateNullOrEmpty(window, browserDetails);
101 | commonShim.shimParameterlessSetLocalDescription(window, browserDetails);
102 |
103 | chromeShim.shimGetUserMedia(window, browserDetails);
104 | chromeShim.shimMediaStream(window, browserDetails);
105 | chromeShim.shimPeerConnection(window, browserDetails);
106 | chromeShim.shimOnTrack(window, browserDetails);
107 | chromeShim.shimAddTrackRemoveTrack(window, browserDetails);
108 | chromeShim.shimGetSendersWithDtmf(window, browserDetails);
109 | chromeShim.shimGetStats(window, browserDetails);
110 | chromeShim.shimSenderReceiverGetStats(window, browserDetails);
111 | chromeShim.fixNegotiationNeeded(window, browserDetails);
112 |
113 | commonShim.shimRTCIceCandidate(window, browserDetails);
114 | commonShim.shimConnectionState(window, browserDetails);
115 | commonShim.shimMaxMessageSize(window, browserDetails);
116 | commonShim.shimSendThrowTypeError(window, browserDetails);
117 | commonShim.removeExtmapAllowMixed(window, browserDetails);
118 | break;
119 | case 'firefox':
120 | if (!firefoxShim || !firefoxShim.shimPeerConnection || !options.shimFirefox) {
121 | logging('Firefox shim is not included in this adapter release.');
122 | return adapter;
123 | }
124 | logging('adapter.js shimming firefox.');
125 | // Export to the adapter global object visible in the browser.
126 | adapter.browserShim = firefoxShim;
127 |
128 | // Must be called before shimPeerConnection.
129 | commonShim.shimAddIceCandidateNullOrEmpty(window, browserDetails);
130 | commonShim.shimParameterlessSetLocalDescription(window, browserDetails);
131 |
132 | firefoxShim.shimGetUserMedia(window, browserDetails);
133 | firefoxShim.shimPeerConnection(window, browserDetails);
134 | firefoxShim.shimOnTrack(window, browserDetails);
135 | firefoxShim.shimRemoveStream(window, browserDetails);
136 | firefoxShim.shimSenderGetStats(window, browserDetails);
137 | firefoxShim.shimReceiverGetStats(window, browserDetails);
138 | firefoxShim.shimRTCDataChannel(window, browserDetails);
139 | firefoxShim.shimAddTransceiver(window, browserDetails);
140 | firefoxShim.shimGetParameters(window, browserDetails);
141 | firefoxShim.shimCreateOffer(window, browserDetails);
142 | firefoxShim.shimCreateAnswer(window, browserDetails);
143 |
144 | commonShim.shimRTCIceCandidate(window, browserDetails);
145 | commonShim.shimConnectionState(window, browserDetails);
146 | commonShim.shimMaxMessageSize(window, browserDetails);
147 | commonShim.shimSendThrowTypeError(window, browserDetails);
148 | break;
149 | case 'safari':
150 | if (!safariShim || !options.shimSafari) {
151 | logging('Safari shim is not included in this adapter release.');
152 | return adapter;
153 | }
154 | logging('adapter.js shimming safari.');
155 | // Export to the adapter global object visible in the browser.
156 | adapter.browserShim = safariShim;
157 |
158 | // Must be called before shimCallbackAPI.
159 | commonShim.shimAddIceCandidateNullOrEmpty(window, browserDetails);
160 | commonShim.shimParameterlessSetLocalDescription(window, browserDetails);
161 |
162 | safariShim.shimRTCIceServerUrls(window, browserDetails);
163 | safariShim.shimCreateOfferLegacy(window, browserDetails);
164 | safariShim.shimCallbacksAPI(window, browserDetails);
165 | safariShim.shimLocalStreamsAPI(window, browserDetails);
166 | safariShim.shimRemoteStreamsAPI(window, browserDetails);
167 | safariShim.shimTrackEventTransceiver(window, browserDetails);
168 | safariShim.shimGetUserMedia(window, browserDetails);
169 | safariShim.shimAudioContext(window, browserDetails);
170 |
171 | commonShim.shimRTCIceCandidate(window, browserDetails);
172 | commonShim.shimMaxMessageSize(window, browserDetails);
173 | commonShim.shimSendThrowTypeError(window, browserDetails);
174 | commonShim.removeExtmapAllowMixed(window, browserDetails);
175 | break;
176 | default:
177 | logging('Unsupported browser!');
178 | break;
179 | }
180 |
181 | return adapter;
182 | }
183 |
184 | // Browser shims.
185 |
186 | },{"./chrome/chrome_shim":3,"./common_shim":6,"./firefox/firefox_shim":7,"./safari/safari_shim":10,"./utils":11,"sdp":12}],3:[function(require,module,exports){
187 | /*
188 | * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
189 | *
190 | * Use of this source code is governed by a BSD-style license
191 | * that can be found in the LICENSE file in the root of the source
192 | * tree.
193 | */
194 | /* eslint-env node */
195 | 'use strict';
196 |
197 | Object.defineProperty(exports, "__esModule", {
198 | value: true
199 | });
200 | exports.shimGetDisplayMedia = exports.shimGetUserMedia = undefined;
201 |
202 | 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; };
203 |
204 | var _getusermedia = require('./getusermedia');
205 |
206 | Object.defineProperty(exports, 'shimGetUserMedia', {
207 | enumerable: true,
208 | get: function get() {
209 | return _getusermedia.shimGetUserMedia;
210 | }
211 | });
212 |
213 | var _getdisplaymedia = require('./getdisplaymedia');
214 |
215 | Object.defineProperty(exports, 'shimGetDisplayMedia', {
216 | enumerable: true,
217 | get: function get() {
218 | return _getdisplaymedia.shimGetDisplayMedia;
219 | }
220 | });
221 | exports.shimMediaStream = shimMediaStream;
222 | exports.shimOnTrack = shimOnTrack;
223 | exports.shimGetSendersWithDtmf = shimGetSendersWithDtmf;
224 | exports.shimGetStats = shimGetStats;
225 | exports.shimSenderReceiverGetStats = shimSenderReceiverGetStats;
226 | exports.shimAddTrackRemoveTrackWithNative = shimAddTrackRemoveTrackWithNative;
227 | exports.shimAddTrackRemoveTrack = shimAddTrackRemoveTrack;
228 | exports.shimPeerConnection = shimPeerConnection;
229 | exports.fixNegotiationNeeded = fixNegotiationNeeded;
230 |
231 | var _utils = require('../utils.js');
232 |
233 | var utils = _interopRequireWildcard(_utils);
234 |
235 | 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; } }
236 |
237 | 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; }
238 |
239 | function shimMediaStream(window) {
240 | window.MediaStream = window.MediaStream || window.webkitMediaStream;
241 | }
242 |
243 | function shimOnTrack(window) {
244 | if ((typeof window === 'undefined' ? 'undefined' : _typeof(window)) === 'object' && window.RTCPeerConnection && !('ontrack' in window.RTCPeerConnection.prototype)) {
245 | Object.defineProperty(window.RTCPeerConnection.prototype, 'ontrack', {
246 | get: function get() {
247 | return this._ontrack;
248 | },
249 | set: function set(f) {
250 | if (this._ontrack) {
251 | this.removeEventListener('track', this._ontrack);
252 | }
253 | this.addEventListener('track', this._ontrack = f);
254 | },
255 |
256 | enumerable: true,
257 | configurable: true
258 | });
259 | var origSetRemoteDescription = window.RTCPeerConnection.prototype.setRemoteDescription;
260 | window.RTCPeerConnection.prototype.setRemoteDescription = function setRemoteDescription() {
261 | var _this = this;
262 |
263 | if (!this._ontrackpoly) {
264 | this._ontrackpoly = function (e) {
265 | // onaddstream does not fire when a track is added to an existing
266 | // stream. But stream.onaddtrack is implemented so we use that.
267 | e.stream.addEventListener('addtrack', function (te) {
268 | var receiver = void 0;
269 | if (window.RTCPeerConnection.prototype.getReceivers) {
270 | receiver = _this.getReceivers().find(function (r) {
271 | return r.track && r.track.id === te.track.id;
272 | });
273 | } else {
274 | receiver = { track: te.track };
275 | }
276 |
277 | var event = new Event('track');
278 | event.track = te.track;
279 | event.receiver = receiver;
280 | event.transceiver = { receiver: receiver };
281 | event.streams = [e.stream];
282 | _this.dispatchEvent(event);
283 | });
284 | e.stream.getTracks().forEach(function (track) {
285 | var receiver = void 0;
286 | if (window.RTCPeerConnection.prototype.getReceivers) {
287 | receiver = _this.getReceivers().find(function (r) {
288 | return r.track && r.track.id === track.id;
289 | });
290 | } else {
291 | receiver = { track: track };
292 | }
293 | var event = new Event('track');
294 | event.track = track;
295 | event.receiver = receiver;
296 | event.transceiver = { receiver: receiver };
297 | event.streams = [e.stream];
298 | _this.dispatchEvent(event);
299 | });
300 | };
301 | this.addEventListener('addstream', this._ontrackpoly);
302 | }
303 | return origSetRemoteDescription.apply(this, arguments);
304 | };
305 | } else {
306 | // even if RTCRtpTransceiver is in window, it is only used and
307 | // emitted in unified-plan. Unfortunately this means we need
308 | // to unconditionally wrap the event.
309 | utils.wrapPeerConnectionEvent(window, 'track', function (e) {
310 | if (!e.transceiver) {
311 | Object.defineProperty(e, 'transceiver', { value: { receiver: e.receiver } });
312 | }
313 | return e;
314 | });
315 | }
316 | }
317 |
318 | function shimGetSendersWithDtmf(window) {
319 | // Overrides addTrack/removeTrack, depends on shimAddTrackRemoveTrack.
320 | if ((typeof window === 'undefined' ? 'undefined' : _typeof(window)) === 'object' && window.RTCPeerConnection && !('getSenders' in window.RTCPeerConnection.prototype) && 'createDTMFSender' in window.RTCPeerConnection.prototype) {
321 | var shimSenderWithDtmf = function shimSenderWithDtmf(pc, track) {
322 | return {
323 | track: track,
324 | get dtmf() {
325 | if (this._dtmf === undefined) {
326 | if (track.kind === 'audio') {
327 | this._dtmf = pc.createDTMFSender(track);
328 | } else {
329 | this._dtmf = null;
330 | }
331 | }
332 | return this._dtmf;
333 | },
334 | _pc: pc
335 | };
336 | };
337 |
338 | // augment addTrack when getSenders is not available.
339 | if (!window.RTCPeerConnection.prototype.getSenders) {
340 | window.RTCPeerConnection.prototype.getSenders = function getSenders() {
341 | this._senders = this._senders || [];
342 | return this._senders.slice(); // return a copy of the internal state.
343 | };
344 | var origAddTrack = window.RTCPeerConnection.prototype.addTrack;
345 | window.RTCPeerConnection.prototype.addTrack = function addTrack(track, stream) {
346 | var sender = origAddTrack.apply(this, arguments);
347 | if (!sender) {
348 | sender = shimSenderWithDtmf(this, track);
349 | this._senders.push(sender);
350 | }
351 | return sender;
352 | };
353 |
354 | var origRemoveTrack = window.RTCPeerConnection.prototype.removeTrack;
355 | window.RTCPeerConnection.prototype.removeTrack = function removeTrack(sender) {
356 | origRemoveTrack.apply(this, arguments);
357 | var idx = this._senders.indexOf(sender);
358 | if (idx !== -1) {
359 | this._senders.splice(idx, 1);
360 | }
361 | };
362 | }
363 | var origAddStream = window.RTCPeerConnection.prototype.addStream;
364 | window.RTCPeerConnection.prototype.addStream = function addStream(stream) {
365 | var _this2 = this;
366 |
367 | this._senders = this._senders || [];
368 | origAddStream.apply(this, [stream]);
369 | stream.getTracks().forEach(function (track) {
370 | _this2._senders.push(shimSenderWithDtmf(_this2, track));
371 | });
372 | };
373 |
374 | var origRemoveStream = window.RTCPeerConnection.prototype.removeStream;
375 | window.RTCPeerConnection.prototype.removeStream = function removeStream(stream) {
376 | var _this3 = this;
377 |
378 | this._senders = this._senders || [];
379 | origRemoveStream.apply(this, [stream]);
380 |
381 | stream.getTracks().forEach(function (track) {
382 | var sender = _this3._senders.find(function (s) {
383 | return s.track === track;
384 | });
385 | if (sender) {
386 | // remove sender
387 | _this3._senders.splice(_this3._senders.indexOf(sender), 1);
388 | }
389 | });
390 | };
391 | } 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)) {
392 | var origGetSenders = window.RTCPeerConnection.prototype.getSenders;
393 | window.RTCPeerConnection.prototype.getSenders = function getSenders() {
394 | var _this4 = this;
395 |
396 | var senders = origGetSenders.apply(this, []);
397 | senders.forEach(function (sender) {
398 | return sender._pc = _this4;
399 | });
400 | return senders;
401 | };
402 |
403 | Object.defineProperty(window.RTCRtpSender.prototype, 'dtmf', {
404 | get: function get() {
405 | if (this._dtmf === undefined) {
406 | if (this.track.kind === 'audio') {
407 | this._dtmf = this._pc.createDTMFSender(this.track);
408 | } else {
409 | this._dtmf = null;
410 | }
411 | }
412 | return this._dtmf;
413 | }
414 | });
415 | }
416 | }
417 |
418 | function shimGetStats(window) {
419 | if (!window.RTCPeerConnection) {
420 | return;
421 | }
422 |
423 | var origGetStats = window.RTCPeerConnection.prototype.getStats;
424 | window.RTCPeerConnection.prototype.getStats = function getStats() {
425 | var _this5 = this;
426 |
427 | var _arguments = Array.prototype.slice.call(arguments),
428 | selector = _arguments[0],
429 | onSucc = _arguments[1],
430 | onErr = _arguments[2];
431 |
432 | // If selector is a function then we are in the old style stats so just
433 | // pass back the original getStats format to avoid breaking old users.
434 |
435 |
436 | if (arguments.length > 0 && typeof selector === 'function') {
437 | return origGetStats.apply(this, arguments);
438 | }
439 |
440 | // When spec-style getStats is supported, return those when called with
441 | // either no arguments or the selector argument is null.
442 | if (origGetStats.length === 0 && (arguments.length === 0 || typeof selector !== 'function')) {
443 | return origGetStats.apply(this, []);
444 | }
445 |
446 | var fixChromeStats_ = function fixChromeStats_(response) {
447 | var standardReport = {};
448 | var reports = response.result();
449 | reports.forEach(function (report) {
450 | var standardStats = {
451 | id: report.id,
452 | timestamp: report.timestamp,
453 | type: {
454 | localcandidate: 'local-candidate',
455 | remotecandidate: 'remote-candidate'
456 | }[report.type] || report.type
457 | };
458 | report.names().forEach(function (name) {
459 | standardStats[name] = report.stat(name);
460 | });
461 | standardReport[standardStats.id] = standardStats;
462 | });
463 |
464 | return standardReport;
465 | };
466 |
467 | // shim getStats with maplike support
468 | var makeMapStats = function makeMapStats(stats) {
469 | return new Map(Object.keys(stats).map(function (key) {
470 | return [key, stats[key]];
471 | }));
472 | };
473 |
474 | if (arguments.length >= 2) {
475 | var successCallbackWrapper_ = function successCallbackWrapper_(response) {
476 | onSucc(makeMapStats(fixChromeStats_(response)));
477 | };
478 |
479 | return origGetStats.apply(this, [successCallbackWrapper_, selector]);
480 | }
481 |
482 | // promise-support
483 | return new Promise(function (resolve, reject) {
484 | origGetStats.apply(_this5, [function (response) {
485 | resolve(makeMapStats(fixChromeStats_(response)));
486 | }, reject]);
487 | }).then(onSucc, onErr);
488 | };
489 | }
490 |
491 | function shimSenderReceiverGetStats(window) {
492 | if (!((typeof window === 'undefined' ? 'undefined' : _typeof(window)) === 'object' && window.RTCPeerConnection && window.RTCRtpSender && window.RTCRtpReceiver)) {
493 | return;
494 | }
495 |
496 | // shim sender stats.
497 | if (!('getStats' in window.RTCRtpSender.prototype)) {
498 | var origGetSenders = window.RTCPeerConnection.prototype.getSenders;
499 | if (origGetSenders) {
500 | window.RTCPeerConnection.prototype.getSenders = function getSenders() {
501 | var _this6 = this;
502 |
503 | var senders = origGetSenders.apply(this, []);
504 | senders.forEach(function (sender) {
505 | return sender._pc = _this6;
506 | });
507 | return senders;
508 | };
509 | }
510 |
511 | var origAddTrack = window.RTCPeerConnection.prototype.addTrack;
512 | if (origAddTrack) {
513 | window.RTCPeerConnection.prototype.addTrack = function addTrack() {
514 | var sender = origAddTrack.apply(this, arguments);
515 | sender._pc = this;
516 | return sender;
517 | };
518 | }
519 | window.RTCRtpSender.prototype.getStats = function getStats() {
520 | var sender = this;
521 | return this._pc.getStats().then(function (result) {
522 | return (
523 | /* Note: this will include stats of all senders that
524 | * send a track with the same id as sender.track as
525 | * it is not possible to identify the RTCRtpSender.
526 | */
527 | utils.filterStats(result, sender.track, true)
528 | );
529 | });
530 | };
531 | }
532 |
533 | // shim receiver stats.
534 | if (!('getStats' in window.RTCRtpReceiver.prototype)) {
535 | var origGetReceivers = window.RTCPeerConnection.prototype.getReceivers;
536 | if (origGetReceivers) {
537 | window.RTCPeerConnection.prototype.getReceivers = function getReceivers() {
538 | var _this7 = this;
539 |
540 | var receivers = origGetReceivers.apply(this, []);
541 | receivers.forEach(function (receiver) {
542 | return receiver._pc = _this7;
543 | });
544 | return receivers;
545 | };
546 | }
547 | utils.wrapPeerConnectionEvent(window, 'track', function (e) {
548 | e.receiver._pc = e.srcElement;
549 | return e;
550 | });
551 | window.RTCRtpReceiver.prototype.getStats = function getStats() {
552 | var receiver = this;
553 | return this._pc.getStats().then(function (result) {
554 | return utils.filterStats(result, receiver.track, false);
555 | });
556 | };
557 | }
558 |
559 | if (!('getStats' in window.RTCRtpSender.prototype && 'getStats' in window.RTCRtpReceiver.prototype)) {
560 | return;
561 | }
562 |
563 | // shim RTCPeerConnection.getStats(track).
564 | var origGetStats = window.RTCPeerConnection.prototype.getStats;
565 | window.RTCPeerConnection.prototype.getStats = function getStats() {
566 | if (arguments.length > 0 && arguments[0] instanceof window.MediaStreamTrack) {
567 | var track = arguments[0];
568 | var sender = void 0;
569 | var receiver = void 0;
570 | var err = void 0;
571 | this.getSenders().forEach(function (s) {
572 | if (s.track === track) {
573 | if (sender) {
574 | err = true;
575 | } else {
576 | sender = s;
577 | }
578 | }
579 | });
580 | this.getReceivers().forEach(function (r) {
581 | if (r.track === track) {
582 | if (receiver) {
583 | err = true;
584 | } else {
585 | receiver = r;
586 | }
587 | }
588 | return r.track === track;
589 | });
590 | if (err || sender && receiver) {
591 | return Promise.reject(new DOMException('There are more than one sender or receiver for the track.', 'InvalidAccessError'));
592 | } else if (sender) {
593 | return sender.getStats();
594 | } else if (receiver) {
595 | return receiver.getStats();
596 | }
597 | return Promise.reject(new DOMException('There is no sender or receiver for the track.', 'InvalidAccessError'));
598 | }
599 | return origGetStats.apply(this, arguments);
600 | };
601 | }
602 |
603 | function shimAddTrackRemoveTrackWithNative(window) {
604 | // shim addTrack/removeTrack with native variants in order to make
605 | // the interactions with legacy getLocalStreams behave as in other browsers.
606 | // Keeps a mapping stream.id => [stream, rtpsenders...]
607 | window.RTCPeerConnection.prototype.getLocalStreams = function getLocalStreams() {
608 | var _this8 = this;
609 |
610 | this._shimmedLocalStreams = this._shimmedLocalStreams || {};
611 | return Object.keys(this._shimmedLocalStreams).map(function (streamId) {
612 | return _this8._shimmedLocalStreams[streamId][0];
613 | });
614 | };
615 |
616 | var origAddTrack = window.RTCPeerConnection.prototype.addTrack;
617 | window.RTCPeerConnection.prototype.addTrack = function addTrack(track, stream) {
618 | if (!stream) {
619 | return origAddTrack.apply(this, arguments);
620 | }
621 | this._shimmedLocalStreams = this._shimmedLocalStreams || {};
622 |
623 | var sender = origAddTrack.apply(this, arguments);
624 | if (!this._shimmedLocalStreams[stream.id]) {
625 | this._shimmedLocalStreams[stream.id] = [stream, sender];
626 | } else if (this._shimmedLocalStreams[stream.id].indexOf(sender) === -1) {
627 | this._shimmedLocalStreams[stream.id].push(sender);
628 | }
629 | return sender;
630 | };
631 |
632 | var origAddStream = window.RTCPeerConnection.prototype.addStream;
633 | window.RTCPeerConnection.prototype.addStream = function addStream(stream) {
634 | var _this9 = this;
635 |
636 | this._shimmedLocalStreams = this._shimmedLocalStreams || {};
637 |
638 | stream.getTracks().forEach(function (track) {
639 | var alreadyExists = _this9.getSenders().find(function (s) {
640 | return s.track === track;
641 | });
642 | if (alreadyExists) {
643 | throw new DOMException('Track already exists.', 'InvalidAccessError');
644 | }
645 | });
646 | var existingSenders = this.getSenders();
647 | origAddStream.apply(this, arguments);
648 | var newSenders = this.getSenders().filter(function (newSender) {
649 | return existingSenders.indexOf(newSender) === -1;
650 | });
651 | this._shimmedLocalStreams[stream.id] = [stream].concat(newSenders);
652 | };
653 |
654 | var origRemoveStream = window.RTCPeerConnection.prototype.removeStream;
655 | window.RTCPeerConnection.prototype.removeStream = function removeStream(stream) {
656 | this._shimmedLocalStreams = this._shimmedLocalStreams || {};
657 | delete this._shimmedLocalStreams[stream.id];
658 | return origRemoveStream.apply(this, arguments);
659 | };
660 |
661 | var origRemoveTrack = window.RTCPeerConnection.prototype.removeTrack;
662 | window.RTCPeerConnection.prototype.removeTrack = function removeTrack(sender) {
663 | var _this10 = this;
664 |
665 | this._shimmedLocalStreams = this._shimmedLocalStreams || {};
666 | if (sender) {
667 | Object.keys(this._shimmedLocalStreams).forEach(function (streamId) {
668 | var idx = _this10._shimmedLocalStreams[streamId].indexOf(sender);
669 | if (idx !== -1) {
670 | _this10._shimmedLocalStreams[streamId].splice(idx, 1);
671 | }
672 | if (_this10._shimmedLocalStreams[streamId].length === 1) {
673 | delete _this10._shimmedLocalStreams[streamId];
674 | }
675 | });
676 | }
677 | return origRemoveTrack.apply(this, arguments);
678 | };
679 | }
680 |
681 | function shimAddTrackRemoveTrack(window, browserDetails) {
682 | if (!window.RTCPeerConnection) {
683 | return;
684 | }
685 | // shim addTrack and removeTrack.
686 | if (window.RTCPeerConnection.prototype.addTrack && browserDetails.version >= 65) {
687 | return shimAddTrackRemoveTrackWithNative(window);
688 | }
689 |
690 | // also shim pc.getLocalStreams when addTrack is shimmed
691 | // to return the original streams.
692 | var origGetLocalStreams = window.RTCPeerConnection.prototype.getLocalStreams;
693 | window.RTCPeerConnection.prototype.getLocalStreams = function getLocalStreams() {
694 | var _this11 = this;
695 |
696 | var nativeStreams = origGetLocalStreams.apply(this);
697 | this._reverseStreams = this._reverseStreams || {};
698 | return nativeStreams.map(function (stream) {
699 | return _this11._reverseStreams[stream.id];
700 | });
701 | };
702 |
703 | var origAddStream = window.RTCPeerConnection.prototype.addStream;
704 | window.RTCPeerConnection.prototype.addStream = function addStream(stream) {
705 | var _this12 = this;
706 |
707 | this._streams = this._streams || {};
708 | this._reverseStreams = this._reverseStreams || {};
709 |
710 | stream.getTracks().forEach(function (track) {
711 | var alreadyExists = _this12.getSenders().find(function (s) {
712 | return s.track === track;
713 | });
714 | if (alreadyExists) {
715 | throw new DOMException('Track already exists.', 'InvalidAccessError');
716 | }
717 | });
718 | // Add identity mapping for consistency with addTrack.
719 | // Unless this is being used with a stream from addTrack.
720 | if (!this._reverseStreams[stream.id]) {
721 | var newStream = new window.MediaStream(stream.getTracks());
722 | this._streams[stream.id] = newStream;
723 | this._reverseStreams[newStream.id] = stream;
724 | stream = newStream;
725 | }
726 | origAddStream.apply(this, [stream]);
727 | };
728 |
729 | var origRemoveStream = window.RTCPeerConnection.prototype.removeStream;
730 | window.RTCPeerConnection.prototype.removeStream = function removeStream(stream) {
731 | this._streams = this._streams || {};
732 | this._reverseStreams = this._reverseStreams || {};
733 |
734 | origRemoveStream.apply(this, [this._streams[stream.id] || stream]);
735 | delete this._reverseStreams[this._streams[stream.id] ? this._streams[stream.id].id : stream.id];
736 | delete this._streams[stream.id];
737 | };
738 |
739 | window.RTCPeerConnection.prototype.addTrack = function addTrack(track, stream) {
740 | var _this13 = this;
741 |
742 | if (this.signalingState === 'closed') {
743 | throw new DOMException('The RTCPeerConnection\'s signalingState is \'closed\'.', 'InvalidStateError');
744 | }
745 | var streams = [].slice.call(arguments, 1);
746 | if (streams.length !== 1 || !streams[0].getTracks().find(function (t) {
747 | return t === track;
748 | })) {
749 | // this is not fully correct but all we can manage without
750 | // [[associated MediaStreams]] internal slot.
751 | throw new DOMException('The adapter.js addTrack polyfill only supports a single ' + ' stream which is associated with the specified track.', 'NotSupportedError');
752 | }
753 |
754 | var alreadyExists = this.getSenders().find(function (s) {
755 | return s.track === track;
756 | });
757 | if (alreadyExists) {
758 | throw new DOMException('Track already exists.', 'InvalidAccessError');
759 | }
760 |
761 | this._streams = this._streams || {};
762 | this._reverseStreams = this._reverseStreams || {};
763 | var oldStream = this._streams[stream.id];
764 | if (oldStream) {
765 | // this is using odd Chrome behaviour, use with caution:
766 | // https://bugs.chromium.org/p/webrtc/issues/detail?id=7815
767 | // Note: we rely on the high-level addTrack/dtmf shim to
768 | // create the sender with a dtmf sender.
769 | oldStream.addTrack(track);
770 |
771 | // Trigger ONN async.
772 | Promise.resolve().then(function () {
773 | _this13.dispatchEvent(new Event('negotiationneeded'));
774 | });
775 | } else {
776 | var newStream = new window.MediaStream([track]);
777 | this._streams[stream.id] = newStream;
778 | this._reverseStreams[newStream.id] = stream;
779 | this.addStream(newStream);
780 | }
781 | return this.getSenders().find(function (s) {
782 | return s.track === track;
783 | });
784 | };
785 |
786 | // replace the internal stream id with the external one and
787 | // vice versa.
788 | function replaceInternalStreamId(pc, description) {
789 | var sdp = description.sdp;
790 | Object.keys(pc._reverseStreams || []).forEach(function (internalId) {
791 | var externalStream = pc._reverseStreams[internalId];
792 | var internalStream = pc._streams[externalStream.id];
793 | sdp = sdp.replace(new RegExp(internalStream.id, 'g'), externalStream.id);
794 | });
795 | return new RTCSessionDescription({
796 | type: description.type,
797 | sdp: sdp
798 | });
799 | }
800 | function replaceExternalStreamId(pc, description) {
801 | var sdp = description.sdp;
802 | Object.keys(pc._reverseStreams || []).forEach(function (internalId) {
803 | var externalStream = pc._reverseStreams[internalId];
804 | var internalStream = pc._streams[externalStream.id];
805 | sdp = sdp.replace(new RegExp(externalStream.id, 'g'), internalStream.id);
806 | });
807 | return new RTCSessionDescription({
808 | type: description.type,
809 | sdp: sdp
810 | });
811 | }
812 | ['createOffer', 'createAnswer'].forEach(function (method) {
813 | var nativeMethod = window.RTCPeerConnection.prototype[method];
814 | var methodObj = _defineProperty({}, method, function () {
815 | var _this14 = this;
816 |
817 | var args = arguments;
818 | var isLegacyCall = arguments.length && typeof arguments[0] === 'function';
819 | if (isLegacyCall) {
820 | return nativeMethod.apply(this, [function (description) {
821 | var desc = replaceInternalStreamId(_this14, description);
822 | args[0].apply(null, [desc]);
823 | }, function (err) {
824 | if (args[1]) {
825 | args[1].apply(null, err);
826 | }
827 | }, arguments[2]]);
828 | }
829 | return nativeMethod.apply(this, arguments).then(function (description) {
830 | return replaceInternalStreamId(_this14, description);
831 | });
832 | });
833 | window.RTCPeerConnection.prototype[method] = methodObj[method];
834 | });
835 |
836 | var origSetLocalDescription = window.RTCPeerConnection.prototype.setLocalDescription;
837 | window.RTCPeerConnection.prototype.setLocalDescription = function setLocalDescription() {
838 | if (!arguments.length || !arguments[0].type) {
839 | return origSetLocalDescription.apply(this, arguments);
840 | }
841 | arguments[0] = replaceExternalStreamId(this, arguments[0]);
842 | return origSetLocalDescription.apply(this, arguments);
843 | };
844 |
845 | // TODO: mangle getStats: https://w3c.github.io/webrtc-stats/#dom-rtcmediastreamstats-streamidentifier
846 |
847 | var origLocalDescription = Object.getOwnPropertyDescriptor(window.RTCPeerConnection.prototype, 'localDescription');
848 | Object.defineProperty(window.RTCPeerConnection.prototype, 'localDescription', {
849 | get: function get() {
850 | var description = origLocalDescription.get.apply(this);
851 | if (description.type === '') {
852 | return description;
853 | }
854 | return replaceInternalStreamId(this, description);
855 | }
856 | });
857 |
858 | window.RTCPeerConnection.prototype.removeTrack = function removeTrack(sender) {
859 | var _this15 = this;
860 |
861 | if (this.signalingState === 'closed') {
862 | throw new DOMException('The RTCPeerConnection\'s signalingState is \'closed\'.', 'InvalidStateError');
863 | }
864 | // We can not yet check for sender instanceof RTCRtpSender
865 | // since we shim RTPSender. So we check if sender._pc is set.
866 | if (!sender._pc) {
867 | throw new DOMException('Argument 1 of RTCPeerConnection.removeTrack ' + 'does not implement interface RTCRtpSender.', 'TypeError');
868 | }
869 | var isLocal = sender._pc === this;
870 | if (!isLocal) {
871 | throw new DOMException('Sender was not created by this connection.', 'InvalidAccessError');
872 | }
873 |
874 | // Search for the native stream the senders track belongs to.
875 | this._streams = this._streams || {};
876 | var stream = void 0;
877 | Object.keys(this._streams).forEach(function (streamid) {
878 | var hasTrack = _this15._streams[streamid].getTracks().find(function (track) {
879 | return sender.track === track;
880 | });
881 | if (hasTrack) {
882 | stream = _this15._streams[streamid];
883 | }
884 | });
885 |
886 | if (stream) {
887 | if (stream.getTracks().length === 1) {
888 | // if this is the last track of the stream, remove the stream. This
889 | // takes care of any shimmed _senders.
890 | this.removeStream(this._reverseStreams[stream.id]);
891 | } else {
892 | // relying on the same odd chrome behaviour as above.
893 | stream.removeTrack(sender.track);
894 | }
895 | this.dispatchEvent(new Event('negotiationneeded'));
896 | }
897 | };
898 | }
899 |
900 | function shimPeerConnection(window, browserDetails) {
901 | if (!window.RTCPeerConnection && window.webkitRTCPeerConnection) {
902 | // very basic support for old versions.
903 | window.RTCPeerConnection = window.webkitRTCPeerConnection;
904 | }
905 | if (!window.RTCPeerConnection) {
906 | return;
907 | }
908 |
909 | // shim implicit creation of RTCSessionDescription/RTCIceCandidate
910 | if (browserDetails.version < 53) {
911 | ['setLocalDescription', 'setRemoteDescription', 'addIceCandidate'].forEach(function (method) {
912 | var nativeMethod = window.RTCPeerConnection.prototype[method];
913 | var methodObj = _defineProperty({}, method, function () {
914 | arguments[0] = new (method === 'addIceCandidate' ? window.RTCIceCandidate : window.RTCSessionDescription)(arguments[0]);
915 | return nativeMethod.apply(this, arguments);
916 | });
917 | window.RTCPeerConnection.prototype[method] = methodObj[method];
918 | });
919 | }
920 | }
921 |
922 | // Attempt to fix ONN in plan-b mode.
923 | function fixNegotiationNeeded(window, browserDetails) {
924 | utils.wrapPeerConnectionEvent(window, 'negotiationneeded', function (e) {
925 | var pc = e.target;
926 | if (browserDetails.version < 72 || pc.getConfiguration && pc.getConfiguration().sdpSemantics === 'plan-b') {
927 | if (pc.signalingState !== 'stable') {
928 | return;
929 | }
930 | }
931 | return e;
932 | });
933 | }
934 |
935 | },{"../utils.js":11,"./getdisplaymedia":4,"./getusermedia":5}],4:[function(require,module,exports){
936 | /*
937 | * Copyright (c) 2018 The adapter.js project authors. All Rights Reserved.
938 | *
939 | * Use of this source code is governed by a BSD-style license
940 | * that can be found in the LICENSE file in the root of the source
941 | * tree.
942 | */
943 | /* eslint-env node */
944 | 'use strict';
945 |
946 | Object.defineProperty(exports, "__esModule", {
947 | value: true
948 | });
949 | exports.shimGetDisplayMedia = shimGetDisplayMedia;
950 | function shimGetDisplayMedia(window, getSourceId) {
951 | if (window.navigator.mediaDevices && 'getDisplayMedia' in window.navigator.mediaDevices) {
952 | return;
953 | }
954 | if (!window.navigator.mediaDevices) {
955 | return;
956 | }
957 | // getSourceId is a function that returns a promise resolving with
958 | // the sourceId of the screen/window/tab to be shared.
959 | if (typeof getSourceId !== 'function') {
960 | console.error('shimGetDisplayMedia: getSourceId argument is not ' + 'a function');
961 | return;
962 | }
963 | window.navigator.mediaDevices.getDisplayMedia = function getDisplayMedia(constraints) {
964 | return getSourceId(constraints).then(function (sourceId) {
965 | var widthSpecified = constraints.video && constraints.video.width;
966 | var heightSpecified = constraints.video && constraints.video.height;
967 | var frameRateSpecified = constraints.video && constraints.video.frameRate;
968 | constraints.video = {
969 | mandatory: {
970 | chromeMediaSource: 'desktop',
971 | chromeMediaSourceId: sourceId,
972 | maxFrameRate: frameRateSpecified || 3
973 | }
974 | };
975 | if (widthSpecified) {
976 | constraints.video.mandatory.maxWidth = widthSpecified;
977 | }
978 | if (heightSpecified) {
979 | constraints.video.mandatory.maxHeight = heightSpecified;
980 | }
981 | return window.navigator.mediaDevices.getUserMedia(constraints);
982 | });
983 | };
984 | }
985 |
986 | },{}],5:[function(require,module,exports){
987 | /*
988 | * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
989 | *
990 | * Use of this source code is governed by a BSD-style license
991 | * that can be found in the LICENSE file in the root of the source
992 | * tree.
993 | */
994 | /* eslint-env node */
995 | 'use strict';
996 |
997 | Object.defineProperty(exports, "__esModule", {
998 | value: true
999 | });
1000 |
1001 | 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; };
1002 |
1003 | exports.shimGetUserMedia = shimGetUserMedia;
1004 |
1005 | var _utils = require('../utils.js');
1006 |
1007 | var utils = _interopRequireWildcard(_utils);
1008 |
1009 | 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; } }
1010 |
1011 | var logging = utils.log;
1012 |
1013 | function shimGetUserMedia(window, browserDetails) {
1014 | var navigator = window && window.navigator;
1015 |
1016 | if (!navigator.mediaDevices) {
1017 | return;
1018 | }
1019 |
1020 | var constraintsToChrome_ = function constraintsToChrome_(c) {
1021 | if ((typeof c === 'undefined' ? 'undefined' : _typeof(c)) !== 'object' || c.mandatory || c.optional) {
1022 | return c;
1023 | }
1024 | var cc = {};
1025 | Object.keys(c).forEach(function (key) {
1026 | if (key === 'require' || key === 'advanced' || key === 'mediaSource') {
1027 | return;
1028 | }
1029 | var r = _typeof(c[key]) === 'object' ? c[key] : { ideal: c[key] };
1030 | if (r.exact !== undefined && typeof r.exact === 'number') {
1031 | r.min = r.max = r.exact;
1032 | }
1033 | var oldname_ = function oldname_(prefix, name) {
1034 | if (prefix) {
1035 | return prefix + name.charAt(0).toUpperCase() + name.slice(1);
1036 | }
1037 | return name === 'deviceId' ? 'sourceId' : name;
1038 | };
1039 | if (r.ideal !== undefined) {
1040 | cc.optional = cc.optional || [];
1041 | var oc = {};
1042 | if (typeof r.ideal === 'number') {
1043 | oc[oldname_('min', key)] = r.ideal;
1044 | cc.optional.push(oc);
1045 | oc = {};
1046 | oc[oldname_('max', key)] = r.ideal;
1047 | cc.optional.push(oc);
1048 | } else {
1049 | oc[oldname_('', key)] = r.ideal;
1050 | cc.optional.push(oc);
1051 | }
1052 | }
1053 | if (r.exact !== undefined && typeof r.exact !== 'number') {
1054 | cc.mandatory = cc.mandatory || {};
1055 | cc.mandatory[oldname_('', key)] = r.exact;
1056 | } else {
1057 | ['min', 'max'].forEach(function (mix) {
1058 | if (r[mix] !== undefined) {
1059 | cc.mandatory = cc.mandatory || {};
1060 | cc.mandatory[oldname_(mix, key)] = r[mix];
1061 | }
1062 | });
1063 | }
1064 | });
1065 | if (c.advanced) {
1066 | cc.optional = (cc.optional || []).concat(c.advanced);
1067 | }
1068 | return cc;
1069 | };
1070 |
1071 | var shimConstraints_ = function shimConstraints_(constraints, func) {
1072 | if (browserDetails.version >= 61) {
1073 | return func(constraints);
1074 | }
1075 | constraints = JSON.parse(JSON.stringify(constraints));
1076 | if (constraints && _typeof(constraints.audio) === 'object') {
1077 | var remap = function remap(obj, a, b) {
1078 | if (a in obj && !(b in obj)) {
1079 | obj[b] = obj[a];
1080 | delete obj[a];
1081 | }
1082 | };
1083 | constraints = JSON.parse(JSON.stringify(constraints));
1084 | remap(constraints.audio, 'autoGainControl', 'googAutoGainControl');
1085 | remap(constraints.audio, 'noiseSuppression', 'googNoiseSuppression');
1086 | constraints.audio = constraintsToChrome_(constraints.audio);
1087 | }
1088 | if (constraints && _typeof(constraints.video) === 'object') {
1089 | // Shim facingMode for mobile & surface pro.
1090 | var face = constraints.video.facingMode;
1091 | face = face && ((typeof face === 'undefined' ? 'undefined' : _typeof(face)) === 'object' ? face : { ideal: face });
1092 | var getSupportedFacingModeLies = browserDetails.version < 66;
1093 |
1094 | if (face && (face.exact === 'user' || face.exact === 'environment' || face.ideal === 'user' || face.ideal === 'environment') && !(navigator.mediaDevices.getSupportedConstraints && navigator.mediaDevices.getSupportedConstraints().facingMode && !getSupportedFacingModeLies)) {
1095 | delete constraints.video.facingMode;
1096 | var matches = void 0;
1097 | if (face.exact === 'environment' || face.ideal === 'environment') {
1098 | matches = ['back', 'rear'];
1099 | } else if (face.exact === 'user' || face.ideal === 'user') {
1100 | matches = ['front'];
1101 | }
1102 | if (matches) {
1103 | // Look for matches in label, or use last cam for back (typical).
1104 | return navigator.mediaDevices.enumerateDevices().then(function (devices) {
1105 | devices = devices.filter(function (d) {
1106 | return d.kind === 'videoinput';
1107 | });
1108 | var dev = devices.find(function (d) {
1109 | return matches.some(function (match) {
1110 | return d.label.toLowerCase().includes(match);
1111 | });
1112 | });
1113 | if (!dev && devices.length && matches.includes('back')) {
1114 | dev = devices[devices.length - 1]; // more likely the back cam
1115 | }
1116 | if (dev) {
1117 | constraints.video.deviceId = face.exact ? { exact: dev.deviceId } : { ideal: dev.deviceId };
1118 | }
1119 | constraints.video = constraintsToChrome_(constraints.video);
1120 | logging('chrome: ' + JSON.stringify(constraints));
1121 | return func(constraints);
1122 | });
1123 | }
1124 | }
1125 | constraints.video = constraintsToChrome_(constraints.video);
1126 | }
1127 | logging('chrome: ' + JSON.stringify(constraints));
1128 | return func(constraints);
1129 | };
1130 |
1131 | var shimError_ = function shimError_(e) {
1132 | if (browserDetails.version >= 64) {
1133 | return e;
1134 | }
1135 | return {
1136 | name: {
1137 | PermissionDeniedError: 'NotAllowedError',
1138 | PermissionDismissedError: 'NotAllowedError',
1139 | InvalidStateError: 'NotAllowedError',
1140 | DevicesNotFoundError: 'NotFoundError',
1141 | ConstraintNotSatisfiedError: 'OverconstrainedError',
1142 | TrackStartError: 'NotReadableError',
1143 | MediaDeviceFailedDueToShutdown: 'NotAllowedError',
1144 | MediaDeviceKillSwitchOn: 'NotAllowedError',
1145 | TabCaptureError: 'AbortError',
1146 | ScreenCaptureError: 'AbortError',
1147 | DeviceCaptureError: 'AbortError'
1148 | }[e.name] || e.name,
1149 | message: e.message,
1150 | constraint: e.constraint || e.constraintName,
1151 | toString: function toString() {
1152 | return this.name + (this.message && ': ') + this.message;
1153 | }
1154 | };
1155 | };
1156 |
1157 | var getUserMedia_ = function getUserMedia_(constraints, onSuccess, onError) {
1158 | shimConstraints_(constraints, function (c) {
1159 | navigator.webkitGetUserMedia(c, onSuccess, function (e) {
1160 | if (onError) {
1161 | onError(shimError_(e));
1162 | }
1163 | });
1164 | });
1165 | };
1166 | navigator.getUserMedia = getUserMedia_.bind(navigator);
1167 |
1168 | // Even though Chrome 45 has navigator.mediaDevices and a getUserMedia
1169 | // function which returns a Promise, it does not accept spec-style
1170 | // constraints.
1171 | if (navigator.mediaDevices.getUserMedia) {
1172 | var origGetUserMedia = navigator.mediaDevices.getUserMedia.bind(navigator.mediaDevices);
1173 | navigator.mediaDevices.getUserMedia = function (cs) {
1174 | return shimConstraints_(cs, function (c) {
1175 | return origGetUserMedia(c).then(function (stream) {
1176 | if (c.audio && !stream.getAudioTracks().length || c.video && !stream.getVideoTracks().length) {
1177 | stream.getTracks().forEach(function (track) {
1178 | track.stop();
1179 | });
1180 | throw new DOMException('', 'NotFoundError');
1181 | }
1182 | return stream;
1183 | }, function (e) {
1184 | return Promise.reject(shimError_(e));
1185 | });
1186 | });
1187 | };
1188 | }
1189 | }
1190 |
1191 | },{"../utils.js":11}],6:[function(require,module,exports){
1192 | /*
1193 | * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
1194 | *
1195 | * Use of this source code is governed by a BSD-style license
1196 | * that can be found in the LICENSE file in the root of the source
1197 | * tree.
1198 | */
1199 | /* eslint-env node */
1200 | 'use strict';
1201 |
1202 | Object.defineProperty(exports, "__esModule", {
1203 | value: true
1204 | });
1205 |
1206 | 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; };
1207 |
1208 | exports.shimRTCIceCandidate = shimRTCIceCandidate;
1209 | exports.shimMaxMessageSize = shimMaxMessageSize;
1210 | exports.shimSendThrowTypeError = shimSendThrowTypeError;
1211 | exports.shimConnectionState = shimConnectionState;
1212 | exports.removeExtmapAllowMixed = removeExtmapAllowMixed;
1213 | exports.shimAddIceCandidateNullOrEmpty = shimAddIceCandidateNullOrEmpty;
1214 | exports.shimParameterlessSetLocalDescription = shimParameterlessSetLocalDescription;
1215 |
1216 | var _sdp = require('sdp');
1217 |
1218 | var _sdp2 = _interopRequireDefault(_sdp);
1219 |
1220 | var _utils = require('./utils');
1221 |
1222 | var utils = _interopRequireWildcard(_utils);
1223 |
1224 | 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; } }
1225 |
1226 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
1227 |
1228 | function shimRTCIceCandidate(window) {
1229 | // foundation is arbitrarily chosen as an indicator for full support for
1230 | // https://w3c.github.io/webrtc-pc/#rtcicecandidate-interface
1231 | if (!window.RTCIceCandidate || window.RTCIceCandidate && 'foundation' in window.RTCIceCandidate.prototype) {
1232 | return;
1233 | }
1234 |
1235 | var NativeRTCIceCandidate = window.RTCIceCandidate;
1236 | window.RTCIceCandidate = function RTCIceCandidate(args) {
1237 | // Remove the a= which shouldn't be part of the candidate string.
1238 | if ((typeof args === 'undefined' ? 'undefined' : _typeof(args)) === 'object' && args.candidate && args.candidate.indexOf('a=') === 0) {
1239 | args = JSON.parse(JSON.stringify(args));
1240 | args.candidate = args.candidate.substr(2);
1241 | }
1242 |
1243 | if (args.candidate && args.candidate.length) {
1244 | // Augment the native candidate with the parsed fields.
1245 | var nativeCandidate = new NativeRTCIceCandidate(args);
1246 | var parsedCandidate = _sdp2.default.parseCandidate(args.candidate);
1247 | var augmentedCandidate = Object.assign(nativeCandidate, parsedCandidate);
1248 |
1249 | // Add a serializer that does not serialize the extra attributes.
1250 | augmentedCandidate.toJSON = function toJSON() {
1251 | return {
1252 | candidate: augmentedCandidate.candidate,
1253 | sdpMid: augmentedCandidate.sdpMid,
1254 | sdpMLineIndex: augmentedCandidate.sdpMLineIndex,
1255 | usernameFragment: augmentedCandidate.usernameFragment
1256 | };
1257 | };
1258 | return augmentedCandidate;
1259 | }
1260 | return new NativeRTCIceCandidate(args);
1261 | };
1262 | window.RTCIceCandidate.prototype = NativeRTCIceCandidate.prototype;
1263 |
1264 | // Hook up the augmented candidate in onicecandidate and
1265 | // addEventListener('icecandidate', ...)
1266 | utils.wrapPeerConnectionEvent(window, 'icecandidate', function (e) {
1267 | if (e.candidate) {
1268 | Object.defineProperty(e, 'candidate', {
1269 | value: new window.RTCIceCandidate(e.candidate),
1270 | writable: 'false'
1271 | });
1272 | }
1273 | return e;
1274 | });
1275 | }
1276 |
1277 | function shimMaxMessageSize(window, browserDetails) {
1278 | if (!window.RTCPeerConnection) {
1279 | return;
1280 | }
1281 |
1282 | if (!('sctp' in window.RTCPeerConnection.prototype)) {
1283 | Object.defineProperty(window.RTCPeerConnection.prototype, 'sctp', {
1284 | get: function get() {
1285 | return typeof this._sctp === 'undefined' ? null : this._sctp;
1286 | }
1287 | });
1288 | }
1289 |
1290 | var sctpInDescription = function sctpInDescription(description) {
1291 | if (!description || !description.sdp) {
1292 | return false;
1293 | }
1294 | var sections = _sdp2.default.splitSections(description.sdp);
1295 | sections.shift();
1296 | return sections.some(function (mediaSection) {
1297 | var mLine = _sdp2.default.parseMLine(mediaSection);
1298 | return mLine && mLine.kind === 'application' && mLine.protocol.indexOf('SCTP') !== -1;
1299 | });
1300 | };
1301 |
1302 | var getRemoteFirefoxVersion = function getRemoteFirefoxVersion(description) {
1303 | // TODO: Is there a better solution for detecting Firefox?
1304 | var match = description.sdp.match(/mozilla...THIS_IS_SDPARTA-(\d+)/);
1305 | if (match === null || match.length < 2) {
1306 | return -1;
1307 | }
1308 | var version = parseInt(match[1], 10);
1309 | // Test for NaN (yes, this is ugly)
1310 | return version !== version ? -1 : version;
1311 | };
1312 |
1313 | var getCanSendMaxMessageSize = function getCanSendMaxMessageSize(remoteIsFirefox) {
1314 | // Every implementation we know can send at least 64 KiB.
1315 | // Note: Although Chrome is technically able to send up to 256 KiB, the
1316 | // data does not reach the other peer reliably.
1317 | // See: https://bugs.chromium.org/p/webrtc/issues/detail?id=8419
1318 | var canSendMaxMessageSize = 65536;
1319 | if (browserDetails.browser === 'firefox') {
1320 | if (browserDetails.version < 57) {
1321 | if (remoteIsFirefox === -1) {
1322 | // FF < 57 will send in 16 KiB chunks using the deprecated PPID
1323 | // fragmentation.
1324 | canSendMaxMessageSize = 16384;
1325 | } else {
1326 | // However, other FF (and RAWRTC) can reassemble PPID-fragmented
1327 | // messages. Thus, supporting ~2 GiB when sending.
1328 | canSendMaxMessageSize = 2147483637;
1329 | }
1330 | } else if (browserDetails.version < 60) {
1331 | // Currently, all FF >= 57 will reset the remote maximum message size
1332 | // to the default value when a data channel is created at a later
1333 | // stage. :(
1334 | // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1426831
1335 | canSendMaxMessageSize = browserDetails.version === 57 ? 65535 : 65536;
1336 | } else {
1337 | // FF >= 60 supports sending ~2 GiB
1338 | canSendMaxMessageSize = 2147483637;
1339 | }
1340 | }
1341 | return canSendMaxMessageSize;
1342 | };
1343 |
1344 | var getMaxMessageSize = function getMaxMessageSize(description, remoteIsFirefox) {
1345 | // Note: 65536 bytes is the default value from the SDP spec. Also,
1346 | // every implementation we know supports receiving 65536 bytes.
1347 | var maxMessageSize = 65536;
1348 |
1349 | // FF 57 has a slightly incorrect default remote max message size, so
1350 | // we need to adjust it here to avoid a failure when sending.
1351 | // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1425697
1352 | if (browserDetails.browser === 'firefox' && browserDetails.version === 57) {
1353 | maxMessageSize = 65535;
1354 | }
1355 |
1356 | var match = _sdp2.default.matchPrefix(description.sdp, 'a=max-message-size:');
1357 | if (match.length > 0) {
1358 | maxMessageSize = parseInt(match[0].substr(19), 10);
1359 | } else if (browserDetails.browser === 'firefox' && remoteIsFirefox !== -1) {
1360 | // If the maximum message size is not present in the remote SDP and
1361 | // both local and remote are Firefox, the remote peer can receive
1362 | // ~2 GiB.
1363 | maxMessageSize = 2147483637;
1364 | }
1365 | return maxMessageSize;
1366 | };
1367 |
1368 | var origSetRemoteDescription = window.RTCPeerConnection.prototype.setRemoteDescription;
1369 | window.RTCPeerConnection.prototype.setRemoteDescription = function setRemoteDescription() {
1370 | this._sctp = null;
1371 | // Chrome decided to not expose .sctp in plan-b mode.
1372 | // As usual, adapter.js has to do an 'ugly worakaround'
1373 | // to cover up the mess.
1374 | if (browserDetails.browser === 'chrome' && browserDetails.version >= 76) {
1375 | var _getConfiguration = this.getConfiguration(),
1376 | sdpSemantics = _getConfiguration.sdpSemantics;
1377 |
1378 | if (sdpSemantics === 'plan-b') {
1379 | Object.defineProperty(this, 'sctp', {
1380 | get: function get() {
1381 | return typeof this._sctp === 'undefined' ? null : this._sctp;
1382 | },
1383 |
1384 | enumerable: true,
1385 | configurable: true
1386 | });
1387 | }
1388 | }
1389 |
1390 | if (sctpInDescription(arguments[0])) {
1391 | // Check if the remote is FF.
1392 | var isFirefox = getRemoteFirefoxVersion(arguments[0]);
1393 |
1394 | // Get the maximum message size the local peer is capable of sending
1395 | var canSendMMS = getCanSendMaxMessageSize(isFirefox);
1396 |
1397 | // Get the maximum message size of the remote peer.
1398 | var remoteMMS = getMaxMessageSize(arguments[0], isFirefox);
1399 |
1400 | // Determine final maximum message size
1401 | var maxMessageSize = void 0;
1402 | if (canSendMMS === 0 && remoteMMS === 0) {
1403 | maxMessageSize = Number.POSITIVE_INFINITY;
1404 | } else if (canSendMMS === 0 || remoteMMS === 0) {
1405 | maxMessageSize = Math.max(canSendMMS, remoteMMS);
1406 | } else {
1407 | maxMessageSize = Math.min(canSendMMS, remoteMMS);
1408 | }
1409 |
1410 | // Create a dummy RTCSctpTransport object and the 'maxMessageSize'
1411 | // attribute.
1412 | var sctp = {};
1413 | Object.defineProperty(sctp, 'maxMessageSize', {
1414 | get: function get() {
1415 | return maxMessageSize;
1416 | }
1417 | });
1418 | this._sctp = sctp;
1419 | }
1420 |
1421 | return origSetRemoteDescription.apply(this, arguments);
1422 | };
1423 | }
1424 |
1425 | function shimSendThrowTypeError(window) {
1426 | if (!(window.RTCPeerConnection && 'createDataChannel' in window.RTCPeerConnection.prototype)) {
1427 | return;
1428 | }
1429 |
1430 | // Note: Although Firefox >= 57 has a native implementation, the maximum
1431 | // message size can be reset for all data channels at a later stage.
1432 | // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1426831
1433 |
1434 | function wrapDcSend(dc, pc) {
1435 | var origDataChannelSend = dc.send;
1436 | dc.send = function send() {
1437 | var data = arguments[0];
1438 | var length = data.length || data.size || data.byteLength;
1439 | if (dc.readyState === 'open' && pc.sctp && length > pc.sctp.maxMessageSize) {
1440 | throw new TypeError('Message too large (can send a maximum of ' + pc.sctp.maxMessageSize + ' bytes)');
1441 | }
1442 | return origDataChannelSend.apply(dc, arguments);
1443 | };
1444 | }
1445 | var origCreateDataChannel = window.RTCPeerConnection.prototype.createDataChannel;
1446 | window.RTCPeerConnection.prototype.createDataChannel = function createDataChannel() {
1447 | var dataChannel = origCreateDataChannel.apply(this, arguments);
1448 | wrapDcSend(dataChannel, this);
1449 | return dataChannel;
1450 | };
1451 | utils.wrapPeerConnectionEvent(window, 'datachannel', function (e) {
1452 | wrapDcSend(e.channel, e.target);
1453 | return e;
1454 | });
1455 | }
1456 |
1457 | /* shims RTCConnectionState by pretending it is the same as iceConnectionState.
1458 | * See https://bugs.chromium.org/p/webrtc/issues/detail?id=6145#c12
1459 | * for why this is a valid hack in Chrome. In Firefox it is slightly incorrect
1460 | * since DTLS failures would be hidden. See
1461 | * https://bugzilla.mozilla.org/show_bug.cgi?id=1265827
1462 | * for the Firefox tracking bug.
1463 | */
1464 | function shimConnectionState(window) {
1465 | if (!window.RTCPeerConnection || 'connectionState' in window.RTCPeerConnection.prototype) {
1466 | return;
1467 | }
1468 | var proto = window.RTCPeerConnection.prototype;
1469 | Object.defineProperty(proto, 'connectionState', {
1470 | get: function get() {
1471 | return {
1472 | completed: 'connected',
1473 | checking: 'connecting'
1474 | }[this.iceConnectionState] || this.iceConnectionState;
1475 | },
1476 |
1477 | enumerable: true,
1478 | configurable: true
1479 | });
1480 | Object.defineProperty(proto, 'onconnectionstatechange', {
1481 | get: function get() {
1482 | return this._onconnectionstatechange || null;
1483 | },
1484 | set: function set(cb) {
1485 | if (this._onconnectionstatechange) {
1486 | this.removeEventListener('connectionstatechange', this._onconnectionstatechange);
1487 | delete this._onconnectionstatechange;
1488 | }
1489 | if (cb) {
1490 | this.addEventListener('connectionstatechange', this._onconnectionstatechange = cb);
1491 | }
1492 | },
1493 |
1494 | enumerable: true,
1495 | configurable: true
1496 | });
1497 |
1498 | ['setLocalDescription', 'setRemoteDescription'].forEach(function (method) {
1499 | var origMethod = proto[method];
1500 | proto[method] = function () {
1501 | if (!this._connectionstatechangepoly) {
1502 | this._connectionstatechangepoly = function (e) {
1503 | var pc = e.target;
1504 | if (pc._lastConnectionState !== pc.connectionState) {
1505 | pc._lastConnectionState = pc.connectionState;
1506 | var newEvent = new Event('connectionstatechange', e);
1507 | pc.dispatchEvent(newEvent);
1508 | }
1509 | return e;
1510 | };
1511 | this.addEventListener('iceconnectionstatechange', this._connectionstatechangepoly);
1512 | }
1513 | return origMethod.apply(this, arguments);
1514 | };
1515 | });
1516 | }
1517 |
1518 | function removeExtmapAllowMixed(window, browserDetails) {
1519 | /* remove a=extmap-allow-mixed for webrtc.org < M71 */
1520 | if (!window.RTCPeerConnection) {
1521 | return;
1522 | }
1523 | if (browserDetails.browser === 'chrome' && browserDetails.version >= 71) {
1524 | return;
1525 | }
1526 | if (browserDetails.browser === 'safari' && browserDetails.version >= 605) {
1527 | return;
1528 | }
1529 | var nativeSRD = window.RTCPeerConnection.prototype.setRemoteDescription;
1530 | window.RTCPeerConnection.prototype.setRemoteDescription = function setRemoteDescription(desc) {
1531 | if (desc && desc.sdp && desc.sdp.indexOf('\na=extmap-allow-mixed') !== -1) {
1532 | var sdp = desc.sdp.split('\n').filter(function (line) {
1533 | return line.trim() !== 'a=extmap-allow-mixed';
1534 | }).join('\n');
1535 | // Safari enforces read-only-ness of RTCSessionDescription fields.
1536 | if (window.RTCSessionDescription && desc instanceof window.RTCSessionDescription) {
1537 | arguments[0] = new window.RTCSessionDescription({
1538 | type: desc.type,
1539 | sdp: sdp
1540 | });
1541 | } else {
1542 | desc.sdp = sdp;
1543 | }
1544 | }
1545 | return nativeSRD.apply(this, arguments);
1546 | };
1547 | }
1548 |
1549 | function shimAddIceCandidateNullOrEmpty(window, browserDetails) {
1550 | // Support for addIceCandidate(null or undefined)
1551 | // as well as addIceCandidate({candidate: "", ...})
1552 | // https://bugs.chromium.org/p/chromium/issues/detail?id=978582
1553 | // Note: must be called before other polyfills which change the signature.
1554 | if (!(window.RTCPeerConnection && window.RTCPeerConnection.prototype)) {
1555 | return;
1556 | }
1557 | var nativeAddIceCandidate = window.RTCPeerConnection.prototype.addIceCandidate;
1558 | if (!nativeAddIceCandidate || nativeAddIceCandidate.length === 0) {
1559 | return;
1560 | }
1561 | window.RTCPeerConnection.prototype.addIceCandidate = function addIceCandidate() {
1562 | if (!arguments[0]) {
1563 | if (arguments[1]) {
1564 | arguments[1].apply(null);
1565 | }
1566 | return Promise.resolve();
1567 | }
1568 | // Firefox 68+ emits and processes {candidate: "", ...}, ignore
1569 | // in older versions.
1570 | // Native support for ignoring exists for Chrome M77+.
1571 | // Safari ignores as well, exact version unknown but works in the same
1572 | // version that also ignores addIceCandidate(null).
1573 | if ((browserDetails.browser === 'chrome' && browserDetails.version < 78 || browserDetails.browser === 'firefox' && browserDetails.version < 68 || browserDetails.browser === 'safari') && arguments[0] && arguments[0].candidate === '') {
1574 | return Promise.resolve();
1575 | }
1576 | return nativeAddIceCandidate.apply(this, arguments);
1577 | };
1578 | }
1579 |
1580 | // Note: Make sure to call this ahead of APIs that modify
1581 | // setLocalDescription.length
1582 | function shimParameterlessSetLocalDescription(window, browserDetails) {
1583 | if (!(window.RTCPeerConnection && window.RTCPeerConnection.prototype)) {
1584 | return;
1585 | }
1586 | var nativeSetLocalDescription = window.RTCPeerConnection.prototype.setLocalDescription;
1587 | if (!nativeSetLocalDescription || nativeSetLocalDescription.length === 0) {
1588 | return;
1589 | }
1590 | window.RTCPeerConnection.prototype.setLocalDescription = function setLocalDescription() {
1591 | var _this = this;
1592 |
1593 | var desc = arguments[0] || {};
1594 | if ((typeof desc === 'undefined' ? 'undefined' : _typeof(desc)) !== 'object' || desc.type && desc.sdp) {
1595 | return nativeSetLocalDescription.apply(this, arguments);
1596 | }
1597 | // The remaining steps should technically happen when SLD comes off the
1598 | // RTCPeerConnection's operations chain (not ahead of going on it), but
1599 | // this is too difficult to shim. Instead, this shim only covers the
1600 | // common case where the operations chain is empty. This is imperfect, but
1601 | // should cover many cases. Rationale: Even if we can't reduce the glare
1602 | // window to zero on imperfect implementations, there's value in tapping
1603 | // into the perfect negotiation pattern that several browsers support.
1604 | desc = { type: desc.type, sdp: desc.sdp };
1605 | if (!desc.type) {
1606 | switch (this.signalingState) {
1607 | case 'stable':
1608 | case 'have-local-offer':
1609 | case 'have-remote-pranswer':
1610 | desc.type = 'offer';
1611 | break;
1612 | default:
1613 | desc.type = 'answer';
1614 | break;
1615 | }
1616 | }
1617 | if (desc.sdp || desc.type !== 'offer' && desc.type !== 'answer') {
1618 | return nativeSetLocalDescription.apply(this, [desc]);
1619 | }
1620 | var func = desc.type === 'offer' ? this.createOffer : this.createAnswer;
1621 | return func.apply(this).then(function (d) {
1622 | return nativeSetLocalDescription.apply(_this, [d]);
1623 | });
1624 | };
1625 | }
1626 |
1627 | },{"./utils":11,"sdp":12}],7:[function(require,module,exports){
1628 | /*
1629 | * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
1630 | *
1631 | * Use of this source code is governed by a BSD-style license
1632 | * that can be found in the LICENSE file in the root of the source
1633 | * tree.
1634 | */
1635 | /* eslint-env node */
1636 | 'use strict';
1637 |
1638 | Object.defineProperty(exports, "__esModule", {
1639 | value: true
1640 | });
1641 | exports.shimGetDisplayMedia = exports.shimGetUserMedia = undefined;
1642 |
1643 | 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; };
1644 |
1645 | var _getusermedia = require('./getusermedia');
1646 |
1647 | Object.defineProperty(exports, 'shimGetUserMedia', {
1648 | enumerable: true,
1649 | get: function get() {
1650 | return _getusermedia.shimGetUserMedia;
1651 | }
1652 | });
1653 |
1654 | var _getdisplaymedia = require('./getdisplaymedia');
1655 |
1656 | Object.defineProperty(exports, 'shimGetDisplayMedia', {
1657 | enumerable: true,
1658 | get: function get() {
1659 | return _getdisplaymedia.shimGetDisplayMedia;
1660 | }
1661 | });
1662 | exports.shimOnTrack = shimOnTrack;
1663 | exports.shimPeerConnection = shimPeerConnection;
1664 | exports.shimSenderGetStats = shimSenderGetStats;
1665 | exports.shimReceiverGetStats = shimReceiverGetStats;
1666 | exports.shimRemoveStream = shimRemoveStream;
1667 | exports.shimRTCDataChannel = shimRTCDataChannel;
1668 | exports.shimAddTransceiver = shimAddTransceiver;
1669 | exports.shimGetParameters = shimGetParameters;
1670 | exports.shimCreateOffer = shimCreateOffer;
1671 | exports.shimCreateAnswer = shimCreateAnswer;
1672 |
1673 | var _utils = require('../utils');
1674 |
1675 | var utils = _interopRequireWildcard(_utils);
1676 |
1677 | 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; } }
1678 |
1679 | 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; }
1680 |
1681 | function shimOnTrack(window) {
1682 | if ((typeof window === 'undefined' ? 'undefined' : _typeof(window)) === 'object' && window.RTCTrackEvent && 'receiver' in window.RTCTrackEvent.prototype && !('transceiver' in window.RTCTrackEvent.prototype)) {
1683 | Object.defineProperty(window.RTCTrackEvent.prototype, 'transceiver', {
1684 | get: function get() {
1685 | return { receiver: this.receiver };
1686 | }
1687 | });
1688 | }
1689 | }
1690 |
1691 | function shimPeerConnection(window, browserDetails) {
1692 | if ((typeof window === 'undefined' ? 'undefined' : _typeof(window)) !== 'object' || !(window.RTCPeerConnection || window.mozRTCPeerConnection)) {
1693 | return; // probably media.peerconnection.enabled=false in about:config
1694 | }
1695 | if (!window.RTCPeerConnection && window.mozRTCPeerConnection) {
1696 | // very basic support for old versions.
1697 | window.RTCPeerConnection = window.mozRTCPeerConnection;
1698 | }
1699 |
1700 | if (browserDetails.version < 53) {
1701 | // shim away need for obsolete RTCIceCandidate/RTCSessionDescription.
1702 | ['setLocalDescription', 'setRemoteDescription', 'addIceCandidate'].forEach(function (method) {
1703 | var nativeMethod = window.RTCPeerConnection.prototype[method];
1704 | var methodObj = _defineProperty({}, method, function () {
1705 | arguments[0] = new (method === 'addIceCandidate' ? window.RTCIceCandidate : window.RTCSessionDescription)(arguments[0]);
1706 | return nativeMethod.apply(this, arguments);
1707 | });
1708 | window.RTCPeerConnection.prototype[method] = methodObj[method];
1709 | });
1710 | }
1711 |
1712 | var modernStatsTypes = {
1713 | inboundrtp: 'inbound-rtp',
1714 | outboundrtp: 'outbound-rtp',
1715 | candidatepair: 'candidate-pair',
1716 | localcandidate: 'local-candidate',
1717 | remotecandidate: 'remote-candidate'
1718 | };
1719 |
1720 | var nativeGetStats = window.RTCPeerConnection.prototype.getStats;
1721 | window.RTCPeerConnection.prototype.getStats = function getStats() {
1722 | var _arguments = Array.prototype.slice.call(arguments),
1723 | selector = _arguments[0],
1724 | onSucc = _arguments[1],
1725 | onErr = _arguments[2];
1726 |
1727 | return nativeGetStats.apply(this, [selector || null]).then(function (stats) {
1728 | if (browserDetails.version < 53 && !onSucc) {
1729 | // Shim only promise getStats with spec-hyphens in type names
1730 | // Leave callback version alone; misc old uses of forEach before Map
1731 | try {
1732 | stats.forEach(function (stat) {
1733 | stat.type = modernStatsTypes[stat.type] || stat.type;
1734 | });
1735 | } catch (e) {
1736 | if (e.name !== 'TypeError') {
1737 | throw e;
1738 | }
1739 | // Avoid TypeError: "type" is read-only, in old versions. 34-43ish
1740 | stats.forEach(function (stat, i) {
1741 | stats.set(i, Object.assign({}, stat, {
1742 | type: modernStatsTypes[stat.type] || stat.type
1743 | }));
1744 | });
1745 | }
1746 | }
1747 | return stats;
1748 | }).then(onSucc, onErr);
1749 | };
1750 | }
1751 |
1752 | function shimSenderGetStats(window) {
1753 | if (!((typeof window === 'undefined' ? 'undefined' : _typeof(window)) === 'object' && window.RTCPeerConnection && window.RTCRtpSender)) {
1754 | return;
1755 | }
1756 | if (window.RTCRtpSender && 'getStats' in window.RTCRtpSender.prototype) {
1757 | return;
1758 | }
1759 | var origGetSenders = window.RTCPeerConnection.prototype.getSenders;
1760 | if (origGetSenders) {
1761 | window.RTCPeerConnection.prototype.getSenders = function getSenders() {
1762 | var _this = this;
1763 |
1764 | var senders = origGetSenders.apply(this, []);
1765 | senders.forEach(function (sender) {
1766 | return sender._pc = _this;
1767 | });
1768 | return senders;
1769 | };
1770 | }
1771 |
1772 | var origAddTrack = window.RTCPeerConnection.prototype.addTrack;
1773 | if (origAddTrack) {
1774 | window.RTCPeerConnection.prototype.addTrack = function addTrack() {
1775 | var sender = origAddTrack.apply(this, arguments);
1776 | sender._pc = this;
1777 | return sender;
1778 | };
1779 | }
1780 | window.RTCRtpSender.prototype.getStats = function getStats() {
1781 | return this.track ? this._pc.getStats(this.track) : Promise.resolve(new Map());
1782 | };
1783 | }
1784 |
1785 | function shimReceiverGetStats(window) {
1786 | if (!((typeof window === 'undefined' ? 'undefined' : _typeof(window)) === 'object' && window.RTCPeerConnection && window.RTCRtpSender)) {
1787 | return;
1788 | }
1789 | if (window.RTCRtpSender && 'getStats' in window.RTCRtpReceiver.prototype) {
1790 | return;
1791 | }
1792 | var origGetReceivers = window.RTCPeerConnection.prototype.getReceivers;
1793 | if (origGetReceivers) {
1794 | window.RTCPeerConnection.prototype.getReceivers = function getReceivers() {
1795 | var _this2 = this;
1796 |
1797 | var receivers = origGetReceivers.apply(this, []);
1798 | receivers.forEach(function (receiver) {
1799 | return receiver._pc = _this2;
1800 | });
1801 | return receivers;
1802 | };
1803 | }
1804 | utils.wrapPeerConnectionEvent(window, 'track', function (e) {
1805 | e.receiver._pc = e.srcElement;
1806 | return e;
1807 | });
1808 | window.RTCRtpReceiver.prototype.getStats = function getStats() {
1809 | return this._pc.getStats(this.track);
1810 | };
1811 | }
1812 |
1813 | function shimRemoveStream(window) {
1814 | if (!window.RTCPeerConnection || 'removeStream' in window.RTCPeerConnection.prototype) {
1815 | return;
1816 | }
1817 | window.RTCPeerConnection.prototype.removeStream = function removeStream(stream) {
1818 | var _this3 = this;
1819 |
1820 | utils.deprecated('removeStream', 'removeTrack');
1821 | this.getSenders().forEach(function (sender) {
1822 | if (sender.track && stream.getTracks().includes(sender.track)) {
1823 | _this3.removeTrack(sender);
1824 | }
1825 | });
1826 | };
1827 | }
1828 |
1829 | function shimRTCDataChannel(window) {
1830 | // rename DataChannel to RTCDataChannel (native fix in FF60):
1831 | // https://bugzilla.mozilla.org/show_bug.cgi?id=1173851
1832 | if (window.DataChannel && !window.RTCDataChannel) {
1833 | window.RTCDataChannel = window.DataChannel;
1834 | }
1835 | }
1836 |
1837 | function shimAddTransceiver(window) {
1838 | // https://github.com/webrtcHacks/adapter/issues/998#issuecomment-516921647
1839 | // Firefox ignores the init sendEncodings options passed to addTransceiver
1840 | // https://bugzilla.mozilla.org/show_bug.cgi?id=1396918
1841 | if (!((typeof window === 'undefined' ? 'undefined' : _typeof(window)) === 'object' && window.RTCPeerConnection)) {
1842 | return;
1843 | }
1844 | var origAddTransceiver = window.RTCPeerConnection.prototype.addTransceiver;
1845 | if (origAddTransceiver) {
1846 | window.RTCPeerConnection.prototype.addTransceiver = function addTransceiver() {
1847 | this.setParametersPromises = [];
1848 | var initParameters = arguments[1];
1849 | var shouldPerformCheck = initParameters && 'sendEncodings' in initParameters;
1850 | if (shouldPerformCheck) {
1851 | // If sendEncodings params are provided, validate grammar
1852 | initParameters.sendEncodings.forEach(function (encodingParam) {
1853 | if ('rid' in encodingParam) {
1854 | var ridRegex = /^[a-z0-9]{0,16}$/i;
1855 | if (!ridRegex.test(encodingParam.rid)) {
1856 | throw new TypeError('Invalid RID value provided.');
1857 | }
1858 | }
1859 | if ('scaleResolutionDownBy' in encodingParam) {
1860 | if (!(parseFloat(encodingParam.scaleResolutionDownBy) >= 1.0)) {
1861 | throw new RangeError('scale_resolution_down_by must be >= 1.0');
1862 | }
1863 | }
1864 | if ('maxFramerate' in encodingParam) {
1865 | if (!(parseFloat(encodingParam.maxFramerate) >= 0)) {
1866 | throw new RangeError('max_framerate must be >= 0.0');
1867 | }
1868 | }
1869 | });
1870 | }
1871 | var transceiver = origAddTransceiver.apply(this, arguments);
1872 | if (shouldPerformCheck) {
1873 | // Check if the init options were applied. If not we do this in an
1874 | // asynchronous way and save the promise reference in a global object.
1875 | // This is an ugly hack, but at the same time is way more robust than
1876 | // checking the sender parameters before and after the createOffer
1877 | // Also note that after the createoffer we are not 100% sure that
1878 | // the params were asynchronously applied so we might miss the
1879 | // opportunity to recreate offer.
1880 | var sender = transceiver.sender;
1881 |
1882 | var params = sender.getParameters();
1883 | if (!('encodings' in params) ||
1884 | // Avoid being fooled by patched getParameters() below.
1885 | params.encodings.length === 1 && Object.keys(params.encodings[0]).length === 0) {
1886 | params.encodings = initParameters.sendEncodings;
1887 | sender.sendEncodings = initParameters.sendEncodings;
1888 | this.setParametersPromises.push(sender.setParameters(params).then(function () {
1889 | delete sender.sendEncodings;
1890 | }).catch(function () {
1891 | delete sender.sendEncodings;
1892 | }));
1893 | }
1894 | }
1895 | return transceiver;
1896 | };
1897 | }
1898 | }
1899 |
1900 | function shimGetParameters(window) {
1901 | if (!((typeof window === 'undefined' ? 'undefined' : _typeof(window)) === 'object' && window.RTCRtpSender)) {
1902 | return;
1903 | }
1904 | var origGetParameters = window.RTCRtpSender.prototype.getParameters;
1905 | if (origGetParameters) {
1906 | window.RTCRtpSender.prototype.getParameters = function getParameters() {
1907 | var params = origGetParameters.apply(this, arguments);
1908 | if (!('encodings' in params)) {
1909 | params.encodings = [].concat(this.sendEncodings || [{}]);
1910 | }
1911 | return params;
1912 | };
1913 | }
1914 | }
1915 |
1916 | function shimCreateOffer(window) {
1917 | // https://github.com/webrtcHacks/adapter/issues/998#issuecomment-516921647
1918 | // Firefox ignores the init sendEncodings options passed to addTransceiver
1919 | // https://bugzilla.mozilla.org/show_bug.cgi?id=1396918
1920 | if (!((typeof window === 'undefined' ? 'undefined' : _typeof(window)) === 'object' && window.RTCPeerConnection)) {
1921 | return;
1922 | }
1923 | var origCreateOffer = window.RTCPeerConnection.prototype.createOffer;
1924 | window.RTCPeerConnection.prototype.createOffer = function createOffer() {
1925 | var _this4 = this,
1926 | _arguments2 = arguments;
1927 |
1928 | if (this.setParametersPromises && this.setParametersPromises.length) {
1929 | return Promise.all(this.setParametersPromises).then(function () {
1930 | return origCreateOffer.apply(_this4, _arguments2);
1931 | }).finally(function () {
1932 | _this4.setParametersPromises = [];
1933 | });
1934 | }
1935 | return origCreateOffer.apply(this, arguments);
1936 | };
1937 | }
1938 |
1939 | function shimCreateAnswer(window) {
1940 | // https://github.com/webrtcHacks/adapter/issues/998#issuecomment-516921647
1941 | // Firefox ignores the init sendEncodings options passed to addTransceiver
1942 | // https://bugzilla.mozilla.org/show_bug.cgi?id=1396918
1943 | if (!((typeof window === 'undefined' ? 'undefined' : _typeof(window)) === 'object' && window.RTCPeerConnection)) {
1944 | return;
1945 | }
1946 | var origCreateAnswer = window.RTCPeerConnection.prototype.createAnswer;
1947 | window.RTCPeerConnection.prototype.createAnswer = function createAnswer() {
1948 | var _this5 = this,
1949 | _arguments3 = arguments;
1950 |
1951 | if (this.setParametersPromises && this.setParametersPromises.length) {
1952 | return Promise.all(this.setParametersPromises).then(function () {
1953 | return origCreateAnswer.apply(_this5, _arguments3);
1954 | }).finally(function () {
1955 | _this5.setParametersPromises = [];
1956 | });
1957 | }
1958 | return origCreateAnswer.apply(this, arguments);
1959 | };
1960 | }
1961 |
1962 | },{"../utils":11,"./getdisplaymedia":8,"./getusermedia":9}],8:[function(require,module,exports){
1963 | /*
1964 | * Copyright (c) 2018 The adapter.js project authors. All Rights Reserved.
1965 | *
1966 | * Use of this source code is governed by a BSD-style license
1967 | * that can be found in the LICENSE file in the root of the source
1968 | * tree.
1969 | */
1970 | /* eslint-env node */
1971 | 'use strict';
1972 |
1973 | Object.defineProperty(exports, "__esModule", {
1974 | value: true
1975 | });
1976 | exports.shimGetDisplayMedia = shimGetDisplayMedia;
1977 | function shimGetDisplayMedia(window, preferredMediaSource) {
1978 | if (window.navigator.mediaDevices && 'getDisplayMedia' in window.navigator.mediaDevices) {
1979 | return;
1980 | }
1981 | if (!window.navigator.mediaDevices) {
1982 | return;
1983 | }
1984 | window.navigator.mediaDevices.getDisplayMedia = function getDisplayMedia(constraints) {
1985 | if (!(constraints && constraints.video)) {
1986 | var err = new DOMException('getDisplayMedia without video ' + 'constraints is undefined');
1987 | err.name = 'NotFoundError';
1988 | // from https://heycam.github.io/webidl/#idl-DOMException-error-names
1989 | err.code = 8;
1990 | return Promise.reject(err);
1991 | }
1992 | if (constraints.video === true) {
1993 | constraints.video = { mediaSource: preferredMediaSource };
1994 | } else {
1995 | constraints.video.mediaSource = preferredMediaSource;
1996 | }
1997 | return window.navigator.mediaDevices.getUserMedia(constraints);
1998 | };
1999 | }
2000 |
2001 | },{}],9:[function(require,module,exports){
2002 | /*
2003 | * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
2004 | *
2005 | * Use of this source code is governed by a BSD-style license
2006 | * that can be found in the LICENSE file in the root of the source
2007 | * tree.
2008 | */
2009 | /* eslint-env node */
2010 | 'use strict';
2011 |
2012 | Object.defineProperty(exports, "__esModule", {
2013 | value: true
2014 | });
2015 |
2016 | 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; };
2017 |
2018 | exports.shimGetUserMedia = shimGetUserMedia;
2019 |
2020 | var _utils = require('../utils');
2021 |
2022 | var utils = _interopRequireWildcard(_utils);
2023 |
2024 | 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; } }
2025 |
2026 | function shimGetUserMedia(window, browserDetails) {
2027 | var navigator = window && window.navigator;
2028 | var MediaStreamTrack = window && window.MediaStreamTrack;
2029 |
2030 | navigator.getUserMedia = function (constraints, onSuccess, onError) {
2031 | // Replace Firefox 44+'s deprecation warning with unprefixed version.
2032 | utils.deprecated('navigator.getUserMedia', 'navigator.mediaDevices.getUserMedia');
2033 | navigator.mediaDevices.getUserMedia(constraints).then(onSuccess, onError);
2034 | };
2035 |
2036 | if (!(browserDetails.version > 55 && 'autoGainControl' in navigator.mediaDevices.getSupportedConstraints())) {
2037 | var remap = function remap(obj, a, b) {
2038 | if (a in obj && !(b in obj)) {
2039 | obj[b] = obj[a];
2040 | delete obj[a];
2041 | }
2042 | };
2043 |
2044 | var nativeGetUserMedia = navigator.mediaDevices.getUserMedia.bind(navigator.mediaDevices);
2045 | navigator.mediaDevices.getUserMedia = function (c) {
2046 | if ((typeof c === 'undefined' ? 'undefined' : _typeof(c)) === 'object' && _typeof(c.audio) === 'object') {
2047 | c = JSON.parse(JSON.stringify(c));
2048 | remap(c.audio, 'autoGainControl', 'mozAutoGainControl');
2049 | remap(c.audio, 'noiseSuppression', 'mozNoiseSuppression');
2050 | }
2051 | return nativeGetUserMedia(c);
2052 | };
2053 |
2054 | if (MediaStreamTrack && MediaStreamTrack.prototype.getSettings) {
2055 | var nativeGetSettings = MediaStreamTrack.prototype.getSettings;
2056 | MediaStreamTrack.prototype.getSettings = function () {
2057 | var obj = nativeGetSettings.apply(this, arguments);
2058 | remap(obj, 'mozAutoGainControl', 'autoGainControl');
2059 | remap(obj, 'mozNoiseSuppression', 'noiseSuppression');
2060 | return obj;
2061 | };
2062 | }
2063 |
2064 | if (MediaStreamTrack && MediaStreamTrack.prototype.applyConstraints) {
2065 | var nativeApplyConstraints = MediaStreamTrack.prototype.applyConstraints;
2066 | MediaStreamTrack.prototype.applyConstraints = function (c) {
2067 | if (this.kind === 'audio' && (typeof c === 'undefined' ? 'undefined' : _typeof(c)) === 'object') {
2068 | c = JSON.parse(JSON.stringify(c));
2069 | remap(c, 'autoGainControl', 'mozAutoGainControl');
2070 | remap(c, 'noiseSuppression', 'mozNoiseSuppression');
2071 | }
2072 | return nativeApplyConstraints.apply(this, [c]);
2073 | };
2074 | }
2075 | }
2076 | }
2077 |
2078 | },{"../utils":11}],10:[function(require,module,exports){
2079 | /*
2080 | * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
2081 | *
2082 | * Use of this source code is governed by a BSD-style license
2083 | * that can be found in the LICENSE file in the root of the source
2084 | * tree.
2085 | */
2086 | 'use strict';
2087 |
2088 | Object.defineProperty(exports, "__esModule", {
2089 | value: true
2090 | });
2091 |
2092 | 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; };
2093 |
2094 | exports.shimLocalStreamsAPI = shimLocalStreamsAPI;
2095 | exports.shimRemoteStreamsAPI = shimRemoteStreamsAPI;
2096 | exports.shimCallbacksAPI = shimCallbacksAPI;
2097 | exports.shimGetUserMedia = shimGetUserMedia;
2098 | exports.shimConstraints = shimConstraints;
2099 | exports.shimRTCIceServerUrls = shimRTCIceServerUrls;
2100 | exports.shimTrackEventTransceiver = shimTrackEventTransceiver;
2101 | exports.shimCreateOfferLegacy = shimCreateOfferLegacy;
2102 | exports.shimAudioContext = shimAudioContext;
2103 |
2104 | var _utils = require('../utils');
2105 |
2106 | var utils = _interopRequireWildcard(_utils);
2107 |
2108 | 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; } }
2109 |
2110 | function shimLocalStreamsAPI(window) {
2111 | if ((typeof window === 'undefined' ? 'undefined' : _typeof(window)) !== 'object' || !window.RTCPeerConnection) {
2112 | return;
2113 | }
2114 | if (!('getLocalStreams' in window.RTCPeerConnection.prototype)) {
2115 | window.RTCPeerConnection.prototype.getLocalStreams = function getLocalStreams() {
2116 | if (!this._localStreams) {
2117 | this._localStreams = [];
2118 | }
2119 | return this._localStreams;
2120 | };
2121 | }
2122 | if (!('addStream' in window.RTCPeerConnection.prototype)) {
2123 | var _addTrack = window.RTCPeerConnection.prototype.addTrack;
2124 | window.RTCPeerConnection.prototype.addStream = function addStream(stream) {
2125 | var _this = this;
2126 |
2127 | if (!this._localStreams) {
2128 | this._localStreams = [];
2129 | }
2130 | if (!this._localStreams.includes(stream)) {
2131 | this._localStreams.push(stream);
2132 | }
2133 | // Try to emulate Chrome's behaviour of adding in audio-video order.
2134 | // Safari orders by track id.
2135 | stream.getAudioTracks().forEach(function (track) {
2136 | return _addTrack.call(_this, track, stream);
2137 | });
2138 | stream.getVideoTracks().forEach(function (track) {
2139 | return _addTrack.call(_this, track, stream);
2140 | });
2141 | };
2142 |
2143 | window.RTCPeerConnection.prototype.addTrack = function addTrack(track) {
2144 | var _this2 = this;
2145 |
2146 | for (var _len = arguments.length, streams = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
2147 | streams[_key - 1] = arguments[_key];
2148 | }
2149 |
2150 | if (streams) {
2151 | streams.forEach(function (stream) {
2152 | if (!_this2._localStreams) {
2153 | _this2._localStreams = [stream];
2154 | } else if (!_this2._localStreams.includes(stream)) {
2155 | _this2._localStreams.push(stream);
2156 | }
2157 | });
2158 | }
2159 | return _addTrack.apply(this, arguments);
2160 | };
2161 | }
2162 | if (!('removeStream' in window.RTCPeerConnection.prototype)) {
2163 | window.RTCPeerConnection.prototype.removeStream = function removeStream(stream) {
2164 | var _this3 = this;
2165 |
2166 | if (!this._localStreams) {
2167 | this._localStreams = [];
2168 | }
2169 | var index = this._localStreams.indexOf(stream);
2170 | if (index === -1) {
2171 | return;
2172 | }
2173 | this._localStreams.splice(index, 1);
2174 | var tracks = stream.getTracks();
2175 | this.getSenders().forEach(function (sender) {
2176 | if (tracks.includes(sender.track)) {
2177 | _this3.removeTrack(sender);
2178 | }
2179 | });
2180 | };
2181 | }
2182 | }
2183 |
2184 | function shimRemoteStreamsAPI(window) {
2185 | if ((typeof window === 'undefined' ? 'undefined' : _typeof(window)) !== 'object' || !window.RTCPeerConnection) {
2186 | return;
2187 | }
2188 | if (!('getRemoteStreams' in window.RTCPeerConnection.prototype)) {
2189 | window.RTCPeerConnection.prototype.getRemoteStreams = function getRemoteStreams() {
2190 | return this._remoteStreams ? this._remoteStreams : [];
2191 | };
2192 | }
2193 | if (!('onaddstream' in window.RTCPeerConnection.prototype)) {
2194 | Object.defineProperty(window.RTCPeerConnection.prototype, 'onaddstream', {
2195 | get: function get() {
2196 | return this._onaddstream;
2197 | },
2198 | set: function set(f) {
2199 | var _this4 = this;
2200 |
2201 | if (this._onaddstream) {
2202 | this.removeEventListener('addstream', this._onaddstream);
2203 | this.removeEventListener('track', this._onaddstreampoly);
2204 | }
2205 | this.addEventListener('addstream', this._onaddstream = f);
2206 | this.addEventListener('track', this._onaddstreampoly = function (e) {
2207 | e.streams.forEach(function (stream) {
2208 | if (!_this4._remoteStreams) {
2209 | _this4._remoteStreams = [];
2210 | }
2211 | if (_this4._remoteStreams.includes(stream)) {
2212 | return;
2213 | }
2214 | _this4._remoteStreams.push(stream);
2215 | var event = new Event('addstream');
2216 | event.stream = stream;
2217 | _this4.dispatchEvent(event);
2218 | });
2219 | });
2220 | }
2221 | });
2222 | var origSetRemoteDescription = window.RTCPeerConnection.prototype.setRemoteDescription;
2223 | window.RTCPeerConnection.prototype.setRemoteDescription = function setRemoteDescription() {
2224 | var pc = this;
2225 | if (!this._onaddstreampoly) {
2226 | this.addEventListener('track', this._onaddstreampoly = function (e) {
2227 | e.streams.forEach(function (stream) {
2228 | if (!pc._remoteStreams) {
2229 | pc._remoteStreams = [];
2230 | }
2231 | if (pc._remoteStreams.indexOf(stream) >= 0) {
2232 | return;
2233 | }
2234 | pc._remoteStreams.push(stream);
2235 | var event = new Event('addstream');
2236 | event.stream = stream;
2237 | pc.dispatchEvent(event);
2238 | });
2239 | });
2240 | }
2241 | return origSetRemoteDescription.apply(pc, arguments);
2242 | };
2243 | }
2244 | }
2245 |
2246 | function shimCallbacksAPI(window) {
2247 | if ((typeof window === 'undefined' ? 'undefined' : _typeof(window)) !== 'object' || !window.RTCPeerConnection) {
2248 | return;
2249 | }
2250 | var prototype = window.RTCPeerConnection.prototype;
2251 | var origCreateOffer = prototype.createOffer;
2252 | var origCreateAnswer = prototype.createAnswer;
2253 | var setLocalDescription = prototype.setLocalDescription;
2254 | var setRemoteDescription = prototype.setRemoteDescription;
2255 | var addIceCandidate = prototype.addIceCandidate;
2256 |
2257 | prototype.createOffer = function createOffer(successCallback, failureCallback) {
2258 | var options = arguments.length >= 2 ? arguments[2] : arguments[0];
2259 | var promise = origCreateOffer.apply(this, [options]);
2260 | if (!failureCallback) {
2261 | return promise;
2262 | }
2263 | promise.then(successCallback, failureCallback);
2264 | return Promise.resolve();
2265 | };
2266 |
2267 | prototype.createAnswer = function createAnswer(successCallback, failureCallback) {
2268 | var options = arguments.length >= 2 ? arguments[2] : arguments[0];
2269 | var promise = origCreateAnswer.apply(this, [options]);
2270 | if (!failureCallback) {
2271 | return promise;
2272 | }
2273 | promise.then(successCallback, failureCallback);
2274 | return Promise.resolve();
2275 | };
2276 |
2277 | var withCallback = function withCallback(description, successCallback, failureCallback) {
2278 | var promise = setLocalDescription.apply(this, [description]);
2279 | if (!failureCallback) {
2280 | return promise;
2281 | }
2282 | promise.then(successCallback, failureCallback);
2283 | return Promise.resolve();
2284 | };
2285 | prototype.setLocalDescription = withCallback;
2286 |
2287 | withCallback = function withCallback(description, successCallback, failureCallback) {
2288 | var promise = setRemoteDescription.apply(this, [description]);
2289 | if (!failureCallback) {
2290 | return promise;
2291 | }
2292 | promise.then(successCallback, failureCallback);
2293 | return Promise.resolve();
2294 | };
2295 | prototype.setRemoteDescription = withCallback;
2296 |
2297 | withCallback = function withCallback(candidate, successCallback, failureCallback) {
2298 | var promise = addIceCandidate.apply(this, [candidate]);
2299 | if (!failureCallback) {
2300 | return promise;
2301 | }
2302 | promise.then(successCallback, failureCallback);
2303 | return Promise.resolve();
2304 | };
2305 | prototype.addIceCandidate = withCallback;
2306 | }
2307 |
2308 | function shimGetUserMedia(window) {
2309 | var navigator = window && window.navigator;
2310 |
2311 | if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
2312 | // shim not needed in Safari 12.1
2313 | var mediaDevices = navigator.mediaDevices;
2314 | var _getUserMedia = mediaDevices.getUserMedia.bind(mediaDevices);
2315 | navigator.mediaDevices.getUserMedia = function (constraints) {
2316 | return _getUserMedia(shimConstraints(constraints));
2317 | };
2318 | }
2319 |
2320 | if (!navigator.getUserMedia && navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
2321 | navigator.getUserMedia = function getUserMedia(constraints, cb, errcb) {
2322 | navigator.mediaDevices.getUserMedia(constraints).then(cb, errcb);
2323 | }.bind(navigator);
2324 | }
2325 | }
2326 |
2327 | function shimConstraints(constraints) {
2328 | if (constraints && constraints.video !== undefined) {
2329 | return Object.assign({}, constraints, { video: utils.compactObject(constraints.video) });
2330 | }
2331 |
2332 | return constraints;
2333 | }
2334 |
2335 | function shimRTCIceServerUrls(window) {
2336 | if (!window.RTCPeerConnection) {
2337 | return;
2338 | }
2339 | // migrate from non-spec RTCIceServer.url to RTCIceServer.urls
2340 | var OrigPeerConnection = window.RTCPeerConnection;
2341 | window.RTCPeerConnection = function RTCPeerConnection(pcConfig, pcConstraints) {
2342 | if (pcConfig && pcConfig.iceServers) {
2343 | var newIceServers = [];
2344 | for (var i = 0; i < pcConfig.iceServers.length; i++) {
2345 | var server = pcConfig.iceServers[i];
2346 | if (!server.hasOwnProperty('urls') && server.hasOwnProperty('url')) {
2347 | utils.deprecated('RTCIceServer.url', 'RTCIceServer.urls');
2348 | server = JSON.parse(JSON.stringify(server));
2349 | server.urls = server.url;
2350 | delete server.url;
2351 | newIceServers.push(server);
2352 | } else {
2353 | newIceServers.push(pcConfig.iceServers[i]);
2354 | }
2355 | }
2356 | pcConfig.iceServers = newIceServers;
2357 | }
2358 | return new OrigPeerConnection(pcConfig, pcConstraints);
2359 | };
2360 | window.RTCPeerConnection.prototype = OrigPeerConnection.prototype;
2361 | // wrap static methods. Currently just generateCertificate.
2362 | if ('generateCertificate' in OrigPeerConnection) {
2363 | Object.defineProperty(window.RTCPeerConnection, 'generateCertificate', {
2364 | get: function get() {
2365 | return OrigPeerConnection.generateCertificate;
2366 | }
2367 | });
2368 | }
2369 | }
2370 |
2371 | function shimTrackEventTransceiver(window) {
2372 | // Add event.transceiver member over deprecated event.receiver
2373 | if ((typeof window === 'undefined' ? 'undefined' : _typeof(window)) === 'object' && window.RTCTrackEvent && 'receiver' in window.RTCTrackEvent.prototype && !('transceiver' in window.RTCTrackEvent.prototype)) {
2374 | Object.defineProperty(window.RTCTrackEvent.prototype, 'transceiver', {
2375 | get: function get() {
2376 | return { receiver: this.receiver };
2377 | }
2378 | });
2379 | }
2380 | }
2381 |
2382 | function shimCreateOfferLegacy(window) {
2383 | var origCreateOffer = window.RTCPeerConnection.prototype.createOffer;
2384 | window.RTCPeerConnection.prototype.createOffer = function createOffer(offerOptions) {
2385 | if (offerOptions) {
2386 | if (typeof offerOptions.offerToReceiveAudio !== 'undefined') {
2387 | // support bit values
2388 | offerOptions.offerToReceiveAudio = !!offerOptions.offerToReceiveAudio;
2389 | }
2390 | var audioTransceiver = this.getTransceivers().find(function (transceiver) {
2391 | return transceiver.receiver.track.kind === 'audio';
2392 | });
2393 | if (offerOptions.offerToReceiveAudio === false && audioTransceiver) {
2394 | if (audioTransceiver.direction === 'sendrecv') {
2395 | if (audioTransceiver.setDirection) {
2396 | audioTransceiver.setDirection('sendonly');
2397 | } else {
2398 | audioTransceiver.direction = 'sendonly';
2399 | }
2400 | } else if (audioTransceiver.direction === 'recvonly') {
2401 | if (audioTransceiver.setDirection) {
2402 | audioTransceiver.setDirection('inactive');
2403 | } else {
2404 | audioTransceiver.direction = 'inactive';
2405 | }
2406 | }
2407 | } else if (offerOptions.offerToReceiveAudio === true && !audioTransceiver) {
2408 | this.addTransceiver('audio', { direction: 'recvonly' });
2409 | }
2410 |
2411 | if (typeof offerOptions.offerToReceiveVideo !== 'undefined') {
2412 | // support bit values
2413 | offerOptions.offerToReceiveVideo = !!offerOptions.offerToReceiveVideo;
2414 | }
2415 | var videoTransceiver = this.getTransceivers().find(function (transceiver) {
2416 | return transceiver.receiver.track.kind === 'video';
2417 | });
2418 | if (offerOptions.offerToReceiveVideo === false && videoTransceiver) {
2419 | if (videoTransceiver.direction === 'sendrecv') {
2420 | if (videoTransceiver.setDirection) {
2421 | videoTransceiver.setDirection('sendonly');
2422 | } else {
2423 | videoTransceiver.direction = 'sendonly';
2424 | }
2425 | } else if (videoTransceiver.direction === 'recvonly') {
2426 | if (videoTransceiver.setDirection) {
2427 | videoTransceiver.setDirection('inactive');
2428 | } else {
2429 | videoTransceiver.direction = 'inactive';
2430 | }
2431 | }
2432 | } else if (offerOptions.offerToReceiveVideo === true && !videoTransceiver) {
2433 | this.addTransceiver('video', { direction: 'recvonly' });
2434 | }
2435 | }
2436 | return origCreateOffer.apply(this, arguments);
2437 | };
2438 | }
2439 |
2440 | function shimAudioContext(window) {
2441 | if ((typeof window === 'undefined' ? 'undefined' : _typeof(window)) !== 'object' || window.AudioContext) {
2442 | return;
2443 | }
2444 | window.AudioContext = window.webkitAudioContext;
2445 | }
2446 |
2447 | },{"../utils":11}],11:[function(require,module,exports){
2448 | /*
2449 | * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
2450 | *
2451 | * Use of this source code is governed by a BSD-style license
2452 | * that can be found in the LICENSE file in the root of the source
2453 | * tree.
2454 | */
2455 | /* eslint-env node */
2456 | 'use strict';
2457 |
2458 | Object.defineProperty(exports, "__esModule", {
2459 | value: true
2460 | });
2461 |
2462 | 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; };
2463 |
2464 | exports.extractVersion = extractVersion;
2465 | exports.wrapPeerConnectionEvent = wrapPeerConnectionEvent;
2466 | exports.disableLog = disableLog;
2467 | exports.disableWarnings = disableWarnings;
2468 | exports.log = log;
2469 | exports.deprecated = deprecated;
2470 | exports.detectBrowser = detectBrowser;
2471 | exports.compactObject = compactObject;
2472 | exports.walkStats = walkStats;
2473 | exports.filterStats = filterStats;
2474 |
2475 | 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; }
2476 |
2477 | var logDisabled_ = true;
2478 | var deprecationWarnings_ = true;
2479 |
2480 | /**
2481 | * Extract browser version out of the provided user agent string.
2482 | *
2483 | * @param {!string} uastring userAgent string.
2484 | * @param {!string} expr Regular expression used as match criteria.
2485 | * @param {!number} pos position in the version string to be returned.
2486 | * @return {!number} browser version.
2487 | */
2488 | function extractVersion(uastring, expr, pos) {
2489 | var match = uastring.match(expr);
2490 | return match && match.length >= pos && parseInt(match[pos], 10);
2491 | }
2492 |
2493 | // Wraps the peerconnection event eventNameToWrap in a function
2494 | // which returns the modified event object (or false to prevent
2495 | // the event).
2496 | function wrapPeerConnectionEvent(window, eventNameToWrap, wrapper) {
2497 | if (!window.RTCPeerConnection) {
2498 | return;
2499 | }
2500 | var proto = window.RTCPeerConnection.prototype;
2501 | var nativeAddEventListener = proto.addEventListener;
2502 | proto.addEventListener = function (nativeEventName, cb) {
2503 | if (nativeEventName !== eventNameToWrap) {
2504 | return nativeAddEventListener.apply(this, arguments);
2505 | }
2506 | var wrappedCallback = function wrappedCallback(e) {
2507 | var modifiedEvent = wrapper(e);
2508 | if (modifiedEvent) {
2509 | if (cb.handleEvent) {
2510 | cb.handleEvent(modifiedEvent);
2511 | } else {
2512 | cb(modifiedEvent);
2513 | }
2514 | }
2515 | };
2516 | this._eventMap = this._eventMap || {};
2517 | if (!this._eventMap[eventNameToWrap]) {
2518 | this._eventMap[eventNameToWrap] = new Map();
2519 | }
2520 | this._eventMap[eventNameToWrap].set(cb, wrappedCallback);
2521 | return nativeAddEventListener.apply(this, [nativeEventName, wrappedCallback]);
2522 | };
2523 |
2524 | var nativeRemoveEventListener = proto.removeEventListener;
2525 | proto.removeEventListener = function (nativeEventName, cb) {
2526 | if (nativeEventName !== eventNameToWrap || !this._eventMap || !this._eventMap[eventNameToWrap]) {
2527 | return nativeRemoveEventListener.apply(this, arguments);
2528 | }
2529 | if (!this._eventMap[eventNameToWrap].has(cb)) {
2530 | return nativeRemoveEventListener.apply(this, arguments);
2531 | }
2532 | var unwrappedCb = this._eventMap[eventNameToWrap].get(cb);
2533 | this._eventMap[eventNameToWrap].delete(cb);
2534 | if (this._eventMap[eventNameToWrap].size === 0) {
2535 | delete this._eventMap[eventNameToWrap];
2536 | }
2537 | if (Object.keys(this._eventMap).length === 0) {
2538 | delete this._eventMap;
2539 | }
2540 | return nativeRemoveEventListener.apply(this, [nativeEventName, unwrappedCb]);
2541 | };
2542 |
2543 | Object.defineProperty(proto, 'on' + eventNameToWrap, {
2544 | get: function get() {
2545 | return this['_on' + eventNameToWrap];
2546 | },
2547 | set: function set(cb) {
2548 | if (this['_on' + eventNameToWrap]) {
2549 | this.removeEventListener(eventNameToWrap, this['_on' + eventNameToWrap]);
2550 | delete this['_on' + eventNameToWrap];
2551 | }
2552 | if (cb) {
2553 | this.addEventListener(eventNameToWrap, this['_on' + eventNameToWrap] = cb);
2554 | }
2555 | },
2556 |
2557 | enumerable: true,
2558 | configurable: true
2559 | });
2560 | }
2561 |
2562 | function disableLog(bool) {
2563 | if (typeof bool !== 'boolean') {
2564 | return new Error('Argument type: ' + (typeof bool === 'undefined' ? 'undefined' : _typeof(bool)) + '. Please use a boolean.');
2565 | }
2566 | logDisabled_ = bool;
2567 | return bool ? 'adapter.js logging disabled' : 'adapter.js logging enabled';
2568 | }
2569 |
2570 | /**
2571 | * Disable or enable deprecation warnings
2572 | * @param {!boolean} bool set to true to disable warnings.
2573 | */
2574 | function disableWarnings(bool) {
2575 | if (typeof bool !== 'boolean') {
2576 | return new Error('Argument type: ' + (typeof bool === 'undefined' ? 'undefined' : _typeof(bool)) + '. Please use a boolean.');
2577 | }
2578 | deprecationWarnings_ = !bool;
2579 | return 'adapter.js deprecation warnings ' + (bool ? 'disabled' : 'enabled');
2580 | }
2581 |
2582 | function log() {
2583 | if ((typeof window === 'undefined' ? 'undefined' : _typeof(window)) === 'object') {
2584 | if (logDisabled_) {
2585 | return;
2586 | }
2587 | if (typeof console !== 'undefined' && typeof console.log === 'function') {
2588 | console.log.apply(console, arguments);
2589 | }
2590 | }
2591 | }
2592 |
2593 | /**
2594 | * Shows a deprecation warning suggesting the modern and spec-compatible API.
2595 | */
2596 | function deprecated(oldMethod, newMethod) {
2597 | if (!deprecationWarnings_) {
2598 | return;
2599 | }
2600 | console.warn(oldMethod + ' is deprecated, please use ' + newMethod + ' instead.');
2601 | }
2602 |
2603 | /**
2604 | * Browser detector.
2605 | *
2606 | * @return {object} result containing browser and version
2607 | * properties.
2608 | */
2609 | function detectBrowser(window) {
2610 | // Returned result object.
2611 | var result = { browser: null, version: null };
2612 |
2613 | // Fail early if it's not a browser
2614 | if (typeof window === 'undefined' || !window.navigator) {
2615 | result.browser = 'Not a browser.';
2616 | return result;
2617 | }
2618 |
2619 | var navigator = window.navigator;
2620 |
2621 |
2622 | if (navigator.mozGetUserMedia) {
2623 | // Firefox.
2624 | result.browser = 'firefox';
2625 | result.version = extractVersion(navigator.userAgent, /Firefox\/(\d+)\./, 1);
2626 | } else if (navigator.webkitGetUserMedia || window.isSecureContext === false && window.webkitRTCPeerConnection && !window.RTCIceGatherer) {
2627 | // Chrome, Chromium, Webview, Opera.
2628 | // Version matches Chrome/WebRTC version.
2629 | // Chrome 74 removed webkitGetUserMedia on http as well so we need the
2630 | // more complicated fallback to webkitRTCPeerConnection.
2631 | result.browser = 'chrome';
2632 | result.version = extractVersion(navigator.userAgent, /Chrom(e|ium)\/(\d+)\./, 2);
2633 | } else if (window.RTCPeerConnection && navigator.userAgent.match(/AppleWebKit\/(\d+)\./)) {
2634 | // Safari.
2635 | result.browser = 'safari';
2636 | result.version = extractVersion(navigator.userAgent, /AppleWebKit\/(\d+)\./, 1);
2637 | result.supportsUnifiedPlan = window.RTCRtpTransceiver && 'currentDirection' in window.RTCRtpTransceiver.prototype;
2638 | } else {
2639 | // Default fallthrough: not supported.
2640 | result.browser = 'Not a supported browser.';
2641 | return result;
2642 | }
2643 |
2644 | return result;
2645 | }
2646 |
2647 | /**
2648 | * Checks if something is an object.
2649 | *
2650 | * @param {*} val The something you want to check.
2651 | * @return true if val is an object, false otherwise.
2652 | */
2653 | function isObject(val) {
2654 | return Object.prototype.toString.call(val) === '[object Object]';
2655 | }
2656 |
2657 | /**
2658 | * Remove all empty objects and undefined values
2659 | * from a nested object -- an enhanced and vanilla version
2660 | * of Lodash's `compact`.
2661 | */
2662 | function compactObject(data) {
2663 | if (!isObject(data)) {
2664 | return data;
2665 | }
2666 |
2667 | return Object.keys(data).reduce(function (accumulator, key) {
2668 | var isObj = isObject(data[key]);
2669 | var value = isObj ? compactObject(data[key]) : data[key];
2670 | var isEmptyObject = isObj && !Object.keys(value).length;
2671 | if (value === undefined || isEmptyObject) {
2672 | return accumulator;
2673 | }
2674 | return Object.assign(accumulator, _defineProperty({}, key, value));
2675 | }, {});
2676 | }
2677 |
2678 | /* iterates the stats graph recursively. */
2679 | function walkStats(stats, base, resultSet) {
2680 | if (!base || resultSet.has(base.id)) {
2681 | return;
2682 | }
2683 | resultSet.set(base.id, base);
2684 | Object.keys(base).forEach(function (name) {
2685 | if (name.endsWith('Id')) {
2686 | walkStats(stats, stats.get(base[name]), resultSet);
2687 | } else if (name.endsWith('Ids')) {
2688 | base[name].forEach(function (id) {
2689 | walkStats(stats, stats.get(id), resultSet);
2690 | });
2691 | }
2692 | });
2693 | }
2694 |
2695 | /* filter getStats for a sender/receiver track. */
2696 | function filterStats(result, track, outbound) {
2697 | var streamStatsType = outbound ? 'outbound-rtp' : 'inbound-rtp';
2698 | var filteredResult = new Map();
2699 | if (track === null) {
2700 | return filteredResult;
2701 | }
2702 | var trackStats = [];
2703 | result.forEach(function (value) {
2704 | if (value.type === 'track' && value.trackIdentifier === track.id) {
2705 | trackStats.push(value);
2706 | }
2707 | });
2708 | trackStats.forEach(function (trackStat) {
2709 | result.forEach(function (stats) {
2710 | if (stats.type === streamStatsType && stats.trackId === trackStat.id) {
2711 | walkStats(result, stats, filteredResult);
2712 | }
2713 | });
2714 | });
2715 | return filteredResult;
2716 | }
2717 |
2718 | },{}],12:[function(require,module,exports){
2719 | /* eslint-env node */
2720 | 'use strict';
2721 |
2722 | // SDP helpers.
2723 |
2724 | 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; };
2725 |
2726 | var SDPUtils = {};
2727 |
2728 | // Generate an alphanumeric identifier for cname or mids.
2729 | // TODO: use UUIDs instead? https://gist.github.com/jed/982883
2730 | SDPUtils.generateIdentifier = function () {
2731 | return Math.random().toString(36).substr(2, 10);
2732 | };
2733 |
2734 | // The RTCP CNAME used by all peerconnections from the same JS.
2735 | SDPUtils.localCName = SDPUtils.generateIdentifier();
2736 |
2737 | // Splits SDP into lines, dealing with both CRLF and LF.
2738 | SDPUtils.splitLines = function (blob) {
2739 | return blob.trim().split('\n').map(function (line) {
2740 | return line.trim();
2741 | });
2742 | };
2743 | // Splits SDP into sessionpart and mediasections. Ensures CRLF.
2744 | SDPUtils.splitSections = function (blob) {
2745 | var parts = blob.split('\nm=');
2746 | return parts.map(function (part, index) {
2747 | return (index > 0 ? 'm=' + part : part).trim() + '\r\n';
2748 | });
2749 | };
2750 |
2751 | // Returns the session description.
2752 | SDPUtils.getDescription = function (blob) {
2753 | var sections = SDPUtils.splitSections(blob);
2754 | return sections && sections[0];
2755 | };
2756 |
2757 | // Returns the individual media sections.
2758 | SDPUtils.getMediaSections = function (blob) {
2759 | var sections = SDPUtils.splitSections(blob);
2760 | sections.shift();
2761 | return sections;
2762 | };
2763 |
2764 | // Returns lines that start with a certain prefix.
2765 | SDPUtils.matchPrefix = function (blob, prefix) {
2766 | return SDPUtils.splitLines(blob).filter(function (line) {
2767 | return line.indexOf(prefix) === 0;
2768 | });
2769 | };
2770 |
2771 | // Parses an ICE candidate line. Sample input:
2772 | // candidate:702786350 2 udp 41819902 8.8.8.8 60769 typ relay raddr 8.8.8.8
2773 | // rport 55996"
2774 | // Input can be prefixed with a=.
2775 | SDPUtils.parseCandidate = function (line) {
2776 | var parts = void 0;
2777 | // Parse both variants.
2778 | if (line.indexOf('a=candidate:') === 0) {
2779 | parts = line.substring(12).split(' ');
2780 | } else {
2781 | parts = line.substring(10).split(' ');
2782 | }
2783 |
2784 | var candidate = {
2785 | foundation: parts[0],
2786 | component: { 1: 'rtp', 2: 'rtcp' }[parts[1]] || parts[1],
2787 | protocol: parts[2].toLowerCase(),
2788 | priority: parseInt(parts[3], 10),
2789 | ip: parts[4],
2790 | address: parts[4], // address is an alias for ip.
2791 | port: parseInt(parts[5], 10),
2792 | // skip parts[6] == 'typ'
2793 | type: parts[7]
2794 | };
2795 |
2796 | for (var i = 8; i < parts.length; i += 2) {
2797 | switch (parts[i]) {
2798 | case 'raddr':
2799 | candidate.relatedAddress = parts[i + 1];
2800 | break;
2801 | case 'rport':
2802 | candidate.relatedPort = parseInt(parts[i + 1], 10);
2803 | break;
2804 | case 'tcptype':
2805 | candidate.tcpType = parts[i + 1];
2806 | break;
2807 | case 'ufrag':
2808 | candidate.ufrag = parts[i + 1]; // for backward compatibility.
2809 | candidate.usernameFragment = parts[i + 1];
2810 | break;
2811 | default:
2812 | // extension handling, in particular ufrag. Don't overwrite.
2813 | if (candidate[parts[i]] === undefined) {
2814 | candidate[parts[i]] = parts[i + 1];
2815 | }
2816 | break;
2817 | }
2818 | }
2819 | return candidate;
2820 | };
2821 |
2822 | // Translates a candidate object into SDP candidate attribute.
2823 | // This does not include the a= prefix!
2824 | SDPUtils.writeCandidate = function (candidate) {
2825 | var sdp = [];
2826 | sdp.push(candidate.foundation);
2827 |
2828 | var component = candidate.component;
2829 | if (component === 'rtp') {
2830 | sdp.push(1);
2831 | } else if (component === 'rtcp') {
2832 | sdp.push(2);
2833 | } else {
2834 | sdp.push(component);
2835 | }
2836 | sdp.push(candidate.protocol.toUpperCase());
2837 | sdp.push(candidate.priority);
2838 | sdp.push(candidate.address || candidate.ip);
2839 | sdp.push(candidate.port);
2840 |
2841 | var type = candidate.type;
2842 | sdp.push('typ');
2843 | sdp.push(type);
2844 | if (type !== 'host' && candidate.relatedAddress && candidate.relatedPort) {
2845 | sdp.push('raddr');
2846 | sdp.push(candidate.relatedAddress);
2847 | sdp.push('rport');
2848 | sdp.push(candidate.relatedPort);
2849 | }
2850 | if (candidate.tcpType && candidate.protocol.toLowerCase() === 'tcp') {
2851 | sdp.push('tcptype');
2852 | sdp.push(candidate.tcpType);
2853 | }
2854 | if (candidate.usernameFragment || candidate.ufrag) {
2855 | sdp.push('ufrag');
2856 | sdp.push(candidate.usernameFragment || candidate.ufrag);
2857 | }
2858 | return 'candidate:' + sdp.join(' ');
2859 | };
2860 |
2861 | // Parses an ice-options line, returns an array of option tags.
2862 | // Sample input:
2863 | // a=ice-options:foo bar
2864 | SDPUtils.parseIceOptions = function (line) {
2865 | return line.substr(14).split(' ');
2866 | };
2867 |
2868 | // Parses a rtpmap line, returns RTCRtpCoddecParameters. Sample input:
2869 | // a=rtpmap:111 opus/48000/2
2870 | SDPUtils.parseRtpMap = function (line) {
2871 | var parts = line.substr(9).split(' ');
2872 | var parsed = {
2873 | payloadType: parseInt(parts.shift(), 10) // was: id
2874 | };
2875 |
2876 | parts = parts[0].split('/');
2877 |
2878 | parsed.name = parts[0];
2879 | parsed.clockRate = parseInt(parts[1], 10); // was: clockrate
2880 | parsed.channels = parts.length === 3 ? parseInt(parts[2], 10) : 1;
2881 | // legacy alias, got renamed back to channels in ORTC.
2882 | parsed.numChannels = parsed.channels;
2883 | return parsed;
2884 | };
2885 |
2886 | // Generates a rtpmap line from RTCRtpCodecCapability or
2887 | // RTCRtpCodecParameters.
2888 | SDPUtils.writeRtpMap = function (codec) {
2889 | var pt = codec.payloadType;
2890 | if (codec.preferredPayloadType !== undefined) {
2891 | pt = codec.preferredPayloadType;
2892 | }
2893 | var channels = codec.channels || codec.numChannels || 1;
2894 | return 'a=rtpmap:' + pt + ' ' + codec.name + '/' + codec.clockRate + (channels !== 1 ? '/' + channels : '') + '\r\n';
2895 | };
2896 |
2897 | // Parses a extmap line (headerextension from RFC 5285). Sample input:
2898 | // a=extmap:2 urn:ietf:params:rtp-hdrext:toffset
2899 | // a=extmap:2/sendonly urn:ietf:params:rtp-hdrext:toffset
2900 | SDPUtils.parseExtmap = function (line) {
2901 | var parts = line.substr(9).split(' ');
2902 | return {
2903 | id: parseInt(parts[0], 10),
2904 | direction: parts[0].indexOf('/') > 0 ? parts[0].split('/')[1] : 'sendrecv',
2905 | uri: parts[1]
2906 | };
2907 | };
2908 |
2909 | // Generates an extmap line from RTCRtpHeaderExtensionParameters or
2910 | // RTCRtpHeaderExtension.
2911 | SDPUtils.writeExtmap = function (headerExtension) {
2912 | return 'a=extmap:' + (headerExtension.id || headerExtension.preferredId) + (headerExtension.direction && headerExtension.direction !== 'sendrecv' ? '/' + headerExtension.direction : '') + ' ' + headerExtension.uri + '\r\n';
2913 | };
2914 |
2915 | // Parses a fmtp line, returns dictionary. Sample input:
2916 | // a=fmtp:96 vbr=on;cng=on
2917 | // Also deals with vbr=on; cng=on
2918 | SDPUtils.parseFmtp = function (line) {
2919 | var parsed = {};
2920 | var kv = void 0;
2921 | var parts = line.substr(line.indexOf(' ') + 1).split(';');
2922 | for (var j = 0; j < parts.length; j++) {
2923 | kv = parts[j].trim().split('=');
2924 | parsed[kv[0].trim()] = kv[1];
2925 | }
2926 | return parsed;
2927 | };
2928 |
2929 | // Generates a fmtp line from RTCRtpCodecCapability or RTCRtpCodecParameters.
2930 | SDPUtils.writeFmtp = function (codec) {
2931 | var line = '';
2932 | var pt = codec.payloadType;
2933 | if (codec.preferredPayloadType !== undefined) {
2934 | pt = codec.preferredPayloadType;
2935 | }
2936 | if (codec.parameters && Object.keys(codec.parameters).length) {
2937 | var params = [];
2938 | Object.keys(codec.parameters).forEach(function (param) {
2939 | if (codec.parameters[param] !== undefined) {
2940 | params.push(param + '=' + codec.parameters[param]);
2941 | } else {
2942 | params.push(param);
2943 | }
2944 | });
2945 | line += 'a=fmtp:' + pt + ' ' + params.join(';') + '\r\n';
2946 | }
2947 | return line;
2948 | };
2949 |
2950 | // Parses a rtcp-fb line, returns RTCPRtcpFeedback object. Sample input:
2951 | // a=rtcp-fb:98 nack rpsi
2952 | SDPUtils.parseRtcpFb = function (line) {
2953 | var parts = line.substr(line.indexOf(' ') + 1).split(' ');
2954 | return {
2955 | type: parts.shift(),
2956 | parameter: parts.join(' ')
2957 | };
2958 | };
2959 |
2960 | // Generate a=rtcp-fb lines from RTCRtpCodecCapability or RTCRtpCodecParameters.
2961 | SDPUtils.writeRtcpFb = function (codec) {
2962 | var lines = '';
2963 | var pt = codec.payloadType;
2964 | if (codec.preferredPayloadType !== undefined) {
2965 | pt = codec.preferredPayloadType;
2966 | }
2967 | if (codec.rtcpFeedback && codec.rtcpFeedback.length) {
2968 | // FIXME: special handling for trr-int?
2969 | codec.rtcpFeedback.forEach(function (fb) {
2970 | lines += 'a=rtcp-fb:' + pt + ' ' + fb.type + (fb.parameter && fb.parameter.length ? ' ' + fb.parameter : '') + '\r\n';
2971 | });
2972 | }
2973 | return lines;
2974 | };
2975 |
2976 | // Parses a RFC 5576 ssrc media attribute. Sample input:
2977 | // a=ssrc:3735928559 cname:something
2978 | SDPUtils.parseSsrcMedia = function (line) {
2979 | var sp = line.indexOf(' ');
2980 | var parts = {
2981 | ssrc: parseInt(line.substr(7, sp - 7), 10)
2982 | };
2983 | var colon = line.indexOf(':', sp);
2984 | if (colon > -1) {
2985 | parts.attribute = line.substr(sp + 1, colon - sp - 1);
2986 | parts.value = line.substr(colon + 1);
2987 | } else {
2988 | parts.attribute = line.substr(sp + 1);
2989 | }
2990 | return parts;
2991 | };
2992 |
2993 | // Parse a ssrc-group line (see RFC 5576). Sample input:
2994 | // a=ssrc-group:semantics 12 34
2995 | SDPUtils.parseSsrcGroup = function (line) {
2996 | var parts = line.substr(13).split(' ');
2997 | return {
2998 | semantics: parts.shift(),
2999 | ssrcs: parts.map(function (ssrc) {
3000 | return parseInt(ssrc, 10);
3001 | })
3002 | };
3003 | };
3004 |
3005 | // Extracts the MID (RFC 5888) from a media section.
3006 | // Returns the MID or undefined if no mid line was found.
3007 | SDPUtils.getMid = function (mediaSection) {
3008 | var mid = SDPUtils.matchPrefix(mediaSection, 'a=mid:')[0];
3009 | if (mid) {
3010 | return mid.substr(6);
3011 | }
3012 | };
3013 |
3014 | // Parses a fingerprint line for DTLS-SRTP.
3015 | SDPUtils.parseFingerprint = function (line) {
3016 | var parts = line.substr(14).split(' ');
3017 | return {
3018 | algorithm: parts[0].toLowerCase(), // algorithm is case-sensitive in Edge.
3019 | value: parts[1].toUpperCase() // the definition is upper-case in RFC 4572.
3020 | };
3021 | };
3022 |
3023 | // Extracts DTLS parameters from SDP media section or sessionpart.
3024 | // FIXME: for consistency with other functions this should only
3025 | // get the fingerprint line as input. See also getIceParameters.
3026 | SDPUtils.getDtlsParameters = function (mediaSection, sessionpart) {
3027 | var lines = SDPUtils.matchPrefix(mediaSection + sessionpart, 'a=fingerprint:');
3028 | // Note: a=setup line is ignored since we use the 'auto' role in Edge.
3029 | return {
3030 | role: 'auto',
3031 | fingerprints: lines.map(SDPUtils.parseFingerprint)
3032 | };
3033 | };
3034 |
3035 | // Serializes DTLS parameters to SDP.
3036 | SDPUtils.writeDtlsParameters = function (params, setupType) {
3037 | var sdp = 'a=setup:' + setupType + '\r\n';
3038 | params.fingerprints.forEach(function (fp) {
3039 | sdp += 'a=fingerprint:' + fp.algorithm + ' ' + fp.value + '\r\n';
3040 | });
3041 | return sdp;
3042 | };
3043 |
3044 | // Parses a=crypto lines into
3045 | // https://rawgit.com/aboba/edgertc/master/msortc-rs4.html#dictionary-rtcsrtpsdesparameters-members
3046 | SDPUtils.parseCryptoLine = function (line) {
3047 | var parts = line.substr(9).split(' ');
3048 | return {
3049 | tag: parseInt(parts[0], 10),
3050 | cryptoSuite: parts[1],
3051 | keyParams: parts[2],
3052 | sessionParams: parts.slice(3)
3053 | };
3054 | };
3055 |
3056 | SDPUtils.writeCryptoLine = function (parameters) {
3057 | return 'a=crypto:' + parameters.tag + ' ' + parameters.cryptoSuite + ' ' + (_typeof(parameters.keyParams) === 'object' ? SDPUtils.writeCryptoKeyParams(parameters.keyParams) : parameters.keyParams) + (parameters.sessionParams ? ' ' + parameters.sessionParams.join(' ') : '') + '\r\n';
3058 | };
3059 |
3060 | // Parses the crypto key parameters into
3061 | // https://rawgit.com/aboba/edgertc/master/msortc-rs4.html#rtcsrtpkeyparam*
3062 | SDPUtils.parseCryptoKeyParams = function (keyParams) {
3063 | if (keyParams.indexOf('inline:') !== 0) {
3064 | return null;
3065 | }
3066 | var parts = keyParams.substr(7).split('|');
3067 | return {
3068 | keyMethod: 'inline',
3069 | keySalt: parts[0],
3070 | lifeTime: parts[1],
3071 | mkiValue: parts[2] ? parts[2].split(':')[0] : undefined,
3072 | mkiLength: parts[2] ? parts[2].split(':')[1] : undefined
3073 | };
3074 | };
3075 |
3076 | SDPUtils.writeCryptoKeyParams = function (keyParams) {
3077 | return keyParams.keyMethod + ':' + keyParams.keySalt + (keyParams.lifeTime ? '|' + keyParams.lifeTime : '') + (keyParams.mkiValue && keyParams.mkiLength ? '|' + keyParams.mkiValue + ':' + keyParams.mkiLength : '');
3078 | };
3079 |
3080 | // Extracts all SDES parameters.
3081 | SDPUtils.getCryptoParameters = function (mediaSection, sessionpart) {
3082 | var lines = SDPUtils.matchPrefix(mediaSection + sessionpart, 'a=crypto:');
3083 | return lines.map(SDPUtils.parseCryptoLine);
3084 | };
3085 |
3086 | // Parses ICE information from SDP media section or sessionpart.
3087 | // FIXME: for consistency with other functions this should only
3088 | // get the ice-ufrag and ice-pwd lines as input.
3089 | SDPUtils.getIceParameters = function (mediaSection, sessionpart) {
3090 | var ufrag = SDPUtils.matchPrefix(mediaSection + sessionpart, 'a=ice-ufrag:')[0];
3091 | var pwd = SDPUtils.matchPrefix(mediaSection + sessionpart, 'a=ice-pwd:')[0];
3092 | if (!(ufrag && pwd)) {
3093 | return null;
3094 | }
3095 | return {
3096 | usernameFragment: ufrag.substr(12),
3097 | password: pwd.substr(10)
3098 | };
3099 | };
3100 |
3101 | // Serializes ICE parameters to SDP.
3102 | SDPUtils.writeIceParameters = function (params) {
3103 | var sdp = 'a=ice-ufrag:' + params.usernameFragment + '\r\n' + 'a=ice-pwd:' + params.password + '\r\n';
3104 | if (params.iceLite) {
3105 | sdp += 'a=ice-lite\r\n';
3106 | }
3107 | return sdp;
3108 | };
3109 |
3110 | // Parses the SDP media section and returns RTCRtpParameters.
3111 | SDPUtils.parseRtpParameters = function (mediaSection) {
3112 | var description = {
3113 | codecs: [],
3114 | headerExtensions: [],
3115 | fecMechanisms: [],
3116 | rtcp: []
3117 | };
3118 | var lines = SDPUtils.splitLines(mediaSection);
3119 | var mline = lines[0].split(' ');
3120 | for (var i = 3; i < mline.length; i++) {
3121 | // find all codecs from mline[3..]
3122 | var pt = mline[i];
3123 | var rtpmapline = SDPUtils.matchPrefix(mediaSection, 'a=rtpmap:' + pt + ' ')[0];
3124 | if (rtpmapline) {
3125 | var codec = SDPUtils.parseRtpMap(rtpmapline);
3126 | var fmtps = SDPUtils.matchPrefix(mediaSection, 'a=fmtp:' + pt + ' ');
3127 | // Only the first a=fmtp: is considered.
3128 | codec.parameters = fmtps.length ? SDPUtils.parseFmtp(fmtps[0]) : {};
3129 | codec.rtcpFeedback = SDPUtils.matchPrefix(mediaSection, 'a=rtcp-fb:' + pt + ' ').map(SDPUtils.parseRtcpFb);
3130 | description.codecs.push(codec);
3131 | // parse FEC mechanisms from rtpmap lines.
3132 | switch (codec.name.toUpperCase()) {
3133 | case 'RED':
3134 | case 'ULPFEC':
3135 | description.fecMechanisms.push(codec.name.toUpperCase());
3136 | break;
3137 | default:
3138 | // only RED and ULPFEC are recognized as FEC mechanisms.
3139 | break;
3140 | }
3141 | }
3142 | }
3143 | SDPUtils.matchPrefix(mediaSection, 'a=extmap:').forEach(function (line) {
3144 | description.headerExtensions.push(SDPUtils.parseExtmap(line));
3145 | });
3146 | // FIXME: parse rtcp.
3147 | return description;
3148 | };
3149 |
3150 | // Generates parts of the SDP media section describing the capabilities /
3151 | // parameters.
3152 | SDPUtils.writeRtpDescription = function (kind, caps) {
3153 | var sdp = '';
3154 |
3155 | // Build the mline.
3156 | sdp += 'm=' + kind + ' ';
3157 | sdp += caps.codecs.length > 0 ? '9' : '0'; // reject if no codecs.
3158 | sdp += ' UDP/TLS/RTP/SAVPF ';
3159 | sdp += caps.codecs.map(function (codec) {
3160 | if (codec.preferredPayloadType !== undefined) {
3161 | return codec.preferredPayloadType;
3162 | }
3163 | return codec.payloadType;
3164 | }).join(' ') + '\r\n';
3165 |
3166 | sdp += 'c=IN IP4 0.0.0.0\r\n';
3167 | sdp += 'a=rtcp:9 IN IP4 0.0.0.0\r\n';
3168 |
3169 | // Add a=rtpmap lines for each codec. Also fmtp and rtcp-fb.
3170 | caps.codecs.forEach(function (codec) {
3171 | sdp += SDPUtils.writeRtpMap(codec);
3172 | sdp += SDPUtils.writeFmtp(codec);
3173 | sdp += SDPUtils.writeRtcpFb(codec);
3174 | });
3175 | var maxptime = 0;
3176 | caps.codecs.forEach(function (codec) {
3177 | if (codec.maxptime > maxptime) {
3178 | maxptime = codec.maxptime;
3179 | }
3180 | });
3181 | if (maxptime > 0) {
3182 | sdp += 'a=maxptime:' + maxptime + '\r\n';
3183 | }
3184 |
3185 | if (caps.headerExtensions) {
3186 | caps.headerExtensions.forEach(function (extension) {
3187 | sdp += SDPUtils.writeExtmap(extension);
3188 | });
3189 | }
3190 | // FIXME: write fecMechanisms.
3191 | return sdp;
3192 | };
3193 |
3194 | // Parses the SDP media section and returns an array of
3195 | // RTCRtpEncodingParameters.
3196 | SDPUtils.parseRtpEncodingParameters = function (mediaSection) {
3197 | var encodingParameters = [];
3198 | var description = SDPUtils.parseRtpParameters(mediaSection);
3199 | var hasRed = description.fecMechanisms.indexOf('RED') !== -1;
3200 | var hasUlpfec = description.fecMechanisms.indexOf('ULPFEC') !== -1;
3201 |
3202 | // filter a=ssrc:... cname:, ignore PlanB-msid
3203 | var ssrcs = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:').map(function (line) {
3204 | return SDPUtils.parseSsrcMedia(line);
3205 | }).filter(function (parts) {
3206 | return parts.attribute === 'cname';
3207 | });
3208 | var primarySsrc = ssrcs.length > 0 && ssrcs[0].ssrc;
3209 | var secondarySsrc = void 0;
3210 |
3211 | var flows = SDPUtils.matchPrefix(mediaSection, 'a=ssrc-group:FID').map(function (line) {
3212 | var parts = line.substr(17).split(' ');
3213 | return parts.map(function (part) {
3214 | return parseInt(part, 10);
3215 | });
3216 | });
3217 | if (flows.length > 0 && flows[0].length > 1 && flows[0][0] === primarySsrc) {
3218 | secondarySsrc = flows[0][1];
3219 | }
3220 |
3221 | description.codecs.forEach(function (codec) {
3222 | if (codec.name.toUpperCase() === 'RTX' && codec.parameters.apt) {
3223 | var encParam = {
3224 | ssrc: primarySsrc,
3225 | codecPayloadType: parseInt(codec.parameters.apt, 10)
3226 | };
3227 | if (primarySsrc && secondarySsrc) {
3228 | encParam.rtx = { ssrc: secondarySsrc };
3229 | }
3230 | encodingParameters.push(encParam);
3231 | if (hasRed) {
3232 | encParam = JSON.parse(JSON.stringify(encParam));
3233 | encParam.fec = {
3234 | ssrc: primarySsrc,
3235 | mechanism: hasUlpfec ? 'red+ulpfec' : 'red'
3236 | };
3237 | encodingParameters.push(encParam);
3238 | }
3239 | }
3240 | });
3241 | if (encodingParameters.length === 0 && primarySsrc) {
3242 | encodingParameters.push({
3243 | ssrc: primarySsrc
3244 | });
3245 | }
3246 |
3247 | // we support both b=AS and b=TIAS but interpret AS as TIAS.
3248 | var bandwidth = SDPUtils.matchPrefix(mediaSection, 'b=');
3249 | if (bandwidth.length) {
3250 | if (bandwidth[0].indexOf('b=TIAS:') === 0) {
3251 | bandwidth = parseInt(bandwidth[0].substr(7), 10);
3252 | } else if (bandwidth[0].indexOf('b=AS:') === 0) {
3253 | // use formula from JSEP to convert b=AS to TIAS value.
3254 | bandwidth = parseInt(bandwidth[0].substr(5), 10) * 1000 * 0.95 - 50 * 40 * 8;
3255 | } else {
3256 | bandwidth = undefined;
3257 | }
3258 | encodingParameters.forEach(function (params) {
3259 | params.maxBitrate = bandwidth;
3260 | });
3261 | }
3262 | return encodingParameters;
3263 | };
3264 |
3265 | // parses http://draft.ortc.org/#rtcrtcpparameters*
3266 | SDPUtils.parseRtcpParameters = function (mediaSection) {
3267 | var rtcpParameters = {};
3268 |
3269 | // Gets the first SSRC. Note that with RTX there might be multiple
3270 | // SSRCs.
3271 | var remoteSsrc = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:').map(function (line) {
3272 | return SDPUtils.parseSsrcMedia(line);
3273 | }).filter(function (obj) {
3274 | return obj.attribute === 'cname';
3275 | })[0];
3276 | if (remoteSsrc) {
3277 | rtcpParameters.cname = remoteSsrc.value;
3278 | rtcpParameters.ssrc = remoteSsrc.ssrc;
3279 | }
3280 |
3281 | // Edge uses the compound attribute instead of reducedSize
3282 | // compound is !reducedSize
3283 | var rsize = SDPUtils.matchPrefix(mediaSection, 'a=rtcp-rsize');
3284 | rtcpParameters.reducedSize = rsize.length > 0;
3285 | rtcpParameters.compound = rsize.length === 0;
3286 |
3287 | // parses the rtcp-mux attrіbute.
3288 | // Note that Edge does not support unmuxed RTCP.
3289 | var mux = SDPUtils.matchPrefix(mediaSection, 'a=rtcp-mux');
3290 | rtcpParameters.mux = mux.length > 0;
3291 |
3292 | return rtcpParameters;
3293 | };
3294 |
3295 | SDPUtils.writeRtcpParameters = function (rtcpParameters) {
3296 | var sdp = '';
3297 | if (rtcpParameters.reducedSize) {
3298 | sdp += 'a=rtcp-rsize\r\n';
3299 | }
3300 | if (rtcpParameters.mux) {
3301 | sdp += 'a=rtcp-mux\r\n';
3302 | }
3303 | if (rtcpParameters.ssrc !== undefined && rtcpParameters.cname) {
3304 | sdp += 'a=ssrc:' + rtcpParameters.ssrc + ' cname:' + rtcpParameters.cname + '\r\n';
3305 | }
3306 | return sdp;
3307 | };
3308 |
3309 | // parses either a=msid: or a=ssrc:... msid lines and returns
3310 | // the id of the MediaStream and MediaStreamTrack.
3311 | SDPUtils.parseMsid = function (mediaSection) {
3312 | var parts = void 0;
3313 | var spec = SDPUtils.matchPrefix(mediaSection, 'a=msid:');
3314 | if (spec.length === 1) {
3315 | parts = spec[0].substr(7).split(' ');
3316 | return { stream: parts[0], track: parts[1] };
3317 | }
3318 | var planB = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:').map(function (line) {
3319 | return SDPUtils.parseSsrcMedia(line);
3320 | }).filter(function (msidParts) {
3321 | return msidParts.attribute === 'msid';
3322 | });
3323 | if (planB.length > 0) {
3324 | parts = planB[0].value.split(' ');
3325 | return { stream: parts[0], track: parts[1] };
3326 | }
3327 | };
3328 |
3329 | // SCTP
3330 | // parses draft-ietf-mmusic-sctp-sdp-26 first and falls back
3331 | // to draft-ietf-mmusic-sctp-sdp-05
3332 | SDPUtils.parseSctpDescription = function (mediaSection) {
3333 | var mline = SDPUtils.parseMLine(mediaSection);
3334 | var maxSizeLine = SDPUtils.matchPrefix(mediaSection, 'a=max-message-size:');
3335 | var maxMessageSize = void 0;
3336 | if (maxSizeLine.length > 0) {
3337 | maxMessageSize = parseInt(maxSizeLine[0].substr(19), 10);
3338 | }
3339 | if (isNaN(maxMessageSize)) {
3340 | maxMessageSize = 65536;
3341 | }
3342 | var sctpPort = SDPUtils.matchPrefix(mediaSection, 'a=sctp-port:');
3343 | if (sctpPort.length > 0) {
3344 | return {
3345 | port: parseInt(sctpPort[0].substr(12), 10),
3346 | protocol: mline.fmt,
3347 | maxMessageSize: maxMessageSize
3348 | };
3349 | }
3350 | var sctpMapLines = SDPUtils.matchPrefix(mediaSection, 'a=sctpmap:');
3351 | if (sctpMapLines.length > 0) {
3352 | var parts = sctpMapLines[0].substr(10).split(' ');
3353 | return {
3354 | port: parseInt(parts[0], 10),
3355 | protocol: parts[1],
3356 | maxMessageSize: maxMessageSize
3357 | };
3358 | }
3359 | };
3360 |
3361 | // SCTP
3362 | // outputs the draft-ietf-mmusic-sctp-sdp-26 version that all browsers
3363 | // support by now receiving in this format, unless we originally parsed
3364 | // as the draft-ietf-mmusic-sctp-sdp-05 format (indicated by the m-line
3365 | // protocol of DTLS/SCTP -- without UDP/ or TCP/)
3366 | SDPUtils.writeSctpDescription = function (media, sctp) {
3367 | var output = [];
3368 | if (media.protocol !== 'DTLS/SCTP') {
3369 | output = ['m=' + media.kind + ' 9 ' + media.protocol + ' ' + sctp.protocol + '\r\n', 'c=IN IP4 0.0.0.0\r\n', 'a=sctp-port:' + sctp.port + '\r\n'];
3370 | } else {
3371 | output = ['m=' + media.kind + ' 9 ' + media.protocol + ' ' + sctp.port + '\r\n', 'c=IN IP4 0.0.0.0\r\n', 'a=sctpmap:' + sctp.port + ' ' + sctp.protocol + ' 65535\r\n'];
3372 | }
3373 | if (sctp.maxMessageSize !== undefined) {
3374 | output.push('a=max-message-size:' + sctp.maxMessageSize + '\r\n');
3375 | }
3376 | return output.join('');
3377 | };
3378 |
3379 | // Generate a session ID for SDP.
3380 | // https://tools.ietf.org/html/draft-ietf-rtcweb-jsep-20#section-5.2.1
3381 | // recommends using a cryptographically random +ve 64-bit value
3382 | // but right now this should be acceptable and within the right range
3383 | SDPUtils.generateSessionId = function () {
3384 | return Math.random().toString().substr(2, 21);
3385 | };
3386 |
3387 | // Write boiler plate for start of SDP
3388 | // sessId argument is optional - if not supplied it will
3389 | // be generated randomly
3390 | // sessVersion is optional and defaults to 2
3391 | // sessUser is optional and defaults to 'thisisadapterortc'
3392 | SDPUtils.writeSessionBoilerplate = function (sessId, sessVer, sessUser) {
3393 | var sessionId = void 0;
3394 | var version = sessVer !== undefined ? sessVer : 2;
3395 | if (sessId) {
3396 | sessionId = sessId;
3397 | } else {
3398 | sessionId = SDPUtils.generateSessionId();
3399 | }
3400 | var user = sessUser || 'thisisadapterortc';
3401 | // FIXME: sess-id should be an NTP timestamp.
3402 | return 'v=0\r\n' + 'o=' + user + ' ' + sessionId + ' ' + version + ' IN IP4 127.0.0.1\r\n' + 's=-\r\n' + 't=0 0\r\n';
3403 | };
3404 |
3405 | // Gets the direction from the mediaSection or the sessionpart.
3406 | SDPUtils.getDirection = function (mediaSection, sessionpart) {
3407 | // Look for sendrecv, sendonly, recvonly, inactive, default to sendrecv.
3408 | var lines = SDPUtils.splitLines(mediaSection);
3409 | for (var i = 0; i < lines.length; i++) {
3410 | switch (lines[i]) {
3411 | case 'a=sendrecv':
3412 | case 'a=sendonly':
3413 | case 'a=recvonly':
3414 | case 'a=inactive':
3415 | return lines[i].substr(2);
3416 | default:
3417 | // FIXME: What should happen here?
3418 | }
3419 | }
3420 | if (sessionpart) {
3421 | return SDPUtils.getDirection(sessionpart);
3422 | }
3423 | return 'sendrecv';
3424 | };
3425 |
3426 | SDPUtils.getKind = function (mediaSection) {
3427 | var lines = SDPUtils.splitLines(mediaSection);
3428 | var mline = lines[0].split(' ');
3429 | return mline[0].substr(2);
3430 | };
3431 |
3432 | SDPUtils.isRejected = function (mediaSection) {
3433 | return mediaSection.split(' ', 2)[1] === '0';
3434 | };
3435 |
3436 | SDPUtils.parseMLine = function (mediaSection) {
3437 | var lines = SDPUtils.splitLines(mediaSection);
3438 | var parts = lines[0].substr(2).split(' ');
3439 | return {
3440 | kind: parts[0],
3441 | port: parseInt(parts[1], 10),
3442 | protocol: parts[2],
3443 | fmt: parts.slice(3).join(' ')
3444 | };
3445 | };
3446 |
3447 | SDPUtils.parseOLine = function (mediaSection) {
3448 | var line = SDPUtils.matchPrefix(mediaSection, 'o=')[0];
3449 | var parts = line.substr(2).split(' ');
3450 | return {
3451 | username: parts[0],
3452 | sessionId: parts[1],
3453 | sessionVersion: parseInt(parts[2], 10),
3454 | netType: parts[3],
3455 | addressType: parts[4],
3456 | address: parts[5]
3457 | };
3458 | };
3459 |
3460 | // a very naive interpretation of a valid SDP.
3461 | SDPUtils.isValidSDP = function (blob) {
3462 | if (typeof blob !== 'string' || blob.length === 0) {
3463 | return false;
3464 | }
3465 | var lines = SDPUtils.splitLines(blob);
3466 | for (var i = 0; i < lines.length; i++) {
3467 | if (lines[i].length < 2 || lines[i].charAt(1) !== '=') {
3468 | return false;
3469 | }
3470 | // TODO: check the modifier a bit more.
3471 | }
3472 | return true;
3473 | };
3474 |
3475 | // Expose public methods.
3476 | if ((typeof module === 'undefined' ? 'undefined' : _typeof(module)) === 'object') {
3477 | module.exports = SDPUtils;
3478 | }
3479 | },{}]},{},[1])(1)
3480 | });
--------------------------------------------------------------------------------
/js/main.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /* globals MediaRecorder */
4 | // Spec is at http://dvcs.w3.org/hg/dap/raw-file/tip/media-stream-capture/RecordingProposal.html
5 |
6 | var constraints = {audio:true,video:{width:{min:640,ideal:1280,max:1280 },height:{ min:480,ideal:720,max:720},framerate:60}};
7 |
8 | var recBtn = document.querySelector('button#rec');
9 | var pauseResBtn = document.querySelector('button#pauseRes');
10 | var stopBtn = document.querySelector('button#stop');
11 |
12 | var liveVideoElement = document.querySelector('#live');
13 | var playbackVideoElement = document.querySelector('#playback');
14 | var dataElement = document.querySelector('#data');
15 | var downloadLink = document.querySelector('a#downloadLink');
16 |
17 | liveVideoElement.controls = false;
18 | playbackVideoElement.controls=false;
19 |
20 | var mediaRecorder;
21 | var chunks = [];
22 | var count = 0;
23 | var localStream = null;
24 | var soundMeter = null;
25 | var containerType = "video/webm"; //defaults to webm but we switch to mp4 on Safari 14.0.2+
26 |
27 | if (!navigator.mediaDevices.getUserMedia){
28 | alert('navigator.mediaDevices.getUserMedia not supported on your browser, use the latest version of Safari, Edge, Firefox or Chrome');
29 | }else{
30 | if (window.MediaRecorder == undefined) {
31 | alert('MediaRecorder not supported on your browser, use the latest version of Safari, Edge, Firefox or Chrome');
32 | }else{
33 | navigator.mediaDevices.getUserMedia(constraints)
34 | .then(function(stream) {
35 | localStream = stream;
36 |
37 | localStream.getTracks().forEach(function(track) {
38 | if(track.kind == "audio"){
39 | track.onended = () => log("audio track.onended track.readyState="+track.readyState+", track.muted=" + track.muted);
40 | track.onmute = () => log("audio track.onmute track.readyState="+track.readyState+", track.muted=" + track.muted);
41 | track.onunmute = () => log("audio track.onunmute track.readyState="+track.readyState+", track.muted=" + track.muted);
42 | }
43 | if(track.kind == "video"){
44 | track.onended = () => log("video track.onended track.readyState="+track.readyState+", track.muted=" + track.muted);
45 | track.onmute = () => log("video track.onmute track.readyState="+track.readyState+", track.muted=" + track.muted);
46 | track.onunmute = () => log("video track.onunmute track.readyState="+track.readyState+", track.muted=" + track.muted);
47 | }
48 | });
49 |
50 | liveVideoElement.srcObject = localStream;
51 | liveVideoElement.play();
52 |
53 | try {
54 | window.AudioContext = window.AudioContext || window.webkitAudioContext;
55 | window.audioContext = new AudioContext();
56 | } catch (e) {
57 | log('Web Audio API not supported.');
58 | }
59 |
60 | soundMeter = window.soundMeter = new SoundMeter(window.audioContext);
61 | soundMeter.connectToSource(localStream, function(e) {
62 | if (e) {
63 | log(e);
64 | return;
65 | }else{
66 | /*setInterval(function() {
67 | log(Math.round(soundMeter.instant.toFixed(2) * 100));
68 | }, 100);*/
69 | }
70 | });
71 |
72 | }).catch(function(err) {
73 | /* handle the error */
74 | log('navigator.getUserMedia error: '+err);
75 | });
76 | }
77 | }
78 |
79 | function onBtnRecordClicked (){
80 | if (localStream == null) {
81 | alert('Could not get local stream from mic/camera');
82 | }else {
83 | recBtn.disabled = true;
84 | pauseResBtn.disabled = false;
85 | stopBtn.disabled = false;
86 |
87 | chunks = [];
88 |
89 | /* use the stream */
90 | log('Start recording...');
91 | if (typeof MediaRecorder.isTypeSupported == 'function'){
92 | /*
93 | MediaRecorder.isTypeSupported is a function announced in https://developers.google.com/web/updates/2016/01/mediarecorder and later introduced in the MediaRecorder API spec http://www.w3.org/TR/mediastream-recording/
94 | */
95 | if (MediaRecorder.isTypeSupported('video/webm;codecs=vp9')) {
96 | var options = {mimeType: 'video/webm;codecs=vp9'};
97 | } else if (MediaRecorder.isTypeSupported('video/webm;codecs=h264')) {
98 | var options = {mimeType: 'video/webm;codecs=h264'};
99 | } else if (MediaRecorder.isTypeSupported('video/webm')) {
100 | var options = {mimeType: 'video/webm'};
101 | } else if (MediaRecorder.isTypeSupported('video/mp4')) {
102 | //Safari 14.0.2 has an EXPERIMENTAL version of MediaRecorder enabled by default
103 | containerType = "video/mp4";
104 | var options = {mimeType: 'video/mp4'};
105 | }
106 | log('Using '+options.mimeType);
107 | mediaRecorder = new MediaRecorder(localStream, options);
108 | }else{
109 | log('isTypeSupported is not supported, using default codecs for browser');
110 | mediaRecorder = new MediaRecorder(localStream);
111 | }
112 |
113 | mediaRecorder.ondataavailable = function(e) {
114 | log('mediaRecorder.ondataavailable, e.data.size='+e.data.size+', e.timecode='+e.timecode);
115 | if (e.data && e.data.size > 0) {
116 | chunks.push(e.data);
117 | }
118 | };
119 |
120 | mediaRecorder.onerror = function(e){
121 | log('mediaRecorder.onerror: ' + e);
122 | };
123 |
124 | mediaRecorder.onstart = function(){
125 | log('mediaRecorder.onstart, mediaRecorder.state = ' + mediaRecorder.state);
126 |
127 | localStream.getTracks().forEach(function(track) {
128 | if(track.kind == "audio"){
129 | log("onstart - Audio track.readyState="+track.readyState+", track.muted=" + track.muted);
130 | }
131 | if(track.kind == "video"){
132 | log("onstart - Video track.readyState="+track.readyState+", track.muted=" + track.muted);
133 | }
134 | });
135 |
136 | };
137 |
138 | mediaRecorder.onstop = function(){
139 | log('mediaRecorder.onstop, mediaRecorder.state = ' + mediaRecorder.state);
140 |
141 | //var recording = new Blob(chunks, {type: containerType});
142 | var recording = new Blob(chunks, {type: mediaRecorder.mimeType});
143 |
144 |
145 | downloadLink.href = URL.createObjectURL(recording);
146 |
147 | /*
148 | srcObject code from https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/srcObject
149 | */
150 |
151 | /*if ('srcObject' in playbackVideoElement) {
152 | try {
153 | playbackVideoElement.srcObject = recording;
154 | } catch (err) {
155 | if (err.name != "TypeError") {
156 | throw err;
157 | }*/
158 | // Even if they do, they may only support MediaStream
159 | playbackVideoElement.src = URL.createObjectURL(recording);
160 | /* }
161 | } else {
162 | playbackVideoElement.src = URL.createObjectURL(recording);
163 | } */
164 |
165 | playbackVideoElement.controls = true;
166 | playbackVideoElement.play();
167 |
168 | var rand = Math.floor((Math.random() * 10000000));
169 | switch(containerType){
170 | case "video/mp4":
171 | var name = "video_"+rand+".mp4" ;
172 | break;
173 | default:
174 | var name = "video_"+rand+".webm" ;
175 | }
176 |
177 | downloadLink.innerHTML = 'Download '+name;
178 |
179 | downloadLink.setAttribute( "download", name);
180 | downloadLink.setAttribute( "name", name);
181 | };
182 |
183 | mediaRecorder.onpause = function(){
184 | log('mediaRecorder.onpause, mediaRecorder.state = ' + mediaRecorder.state);
185 | }
186 |
187 | mediaRecorder.onresume = function(){
188 | log('mediaRecorder.onresume, mediaRecorder.state = ' + mediaRecorder.state);
189 | }
190 |
191 | mediaRecorder.onwarning = function(e){
192 | log('mediaRecorder.onwarning: ' + e);
193 | };
194 |
195 | pauseResBtn.textContent = "Pause";
196 |
197 | mediaRecorder.start(1000);
198 |
199 | localStream.getTracks().forEach(function(track) {
200 | log(track.kind+":"+JSON.stringify(track.getSettings()));
201 | console.log(track.getSettings());
202 | })
203 | }
204 | }
205 |
206 | navigator.mediaDevices.ondevicechange = function(event) {
207 | log("mediaDevices.ondevicechange");
208 | /*
209 | if (localStream != null){
210 | localStream.getTracks().forEach(function(track) {
211 | if(track.kind == "audio"){
212 | track.onended = function(event){
213 | log("audio track.onended");
214 | }
215 | }
216 | });
217 | }
218 | */
219 | }
220 |
221 | function onBtnStopClicked(){
222 | mediaRecorder.stop();
223 | recBtn.disabled = false;
224 | pauseResBtn.disabled = true;
225 | stopBtn.disabled = true;
226 | }
227 |
228 | function onPauseResumeClicked(){
229 | if(pauseResBtn.textContent === "Pause"){
230 | pauseResBtn.textContent = "Resume";
231 | mediaRecorder.pause();
232 | stopBtn.disabled = true;
233 | }else{
234 | pauseResBtn.textContent = "Pause";
235 | mediaRecorder.resume();
236 | stopBtn.disabled = false;
237 | }
238 | recBtn.disabled = true;
239 | pauseResBtn.disabled = false;
240 | }
241 |
242 | function onStateClicked(){
243 |
244 | if(mediaRecorder != null && localStream != null && soundMeter != null){
245 | log("mediaRecorder.state="+mediaRecorder.state);
246 | log("mediaRecorder.mimeType="+mediaRecorder.mimeType);
247 | log("mediaRecorder.videoBitsPerSecond="+mediaRecorder.videoBitsPerSecond);
248 | log("mediaRecorder.audioBitsPerSecond="+mediaRecorder.audioBitsPerSecond);
249 |
250 | localStream.getTracks().forEach(function(track) {
251 | if(track.kind == "audio"){
252 | log("Audio: track.readyState="+track.readyState+", track.muted=" + track.muted);
253 | }
254 | if(track.kind == "video"){
255 | log("Video: track.readyState="+track.readyState+", track.muted=" + track.muted);
256 | }
257 | });
258 |
259 | log("Audio activity: " + Math.round(soundMeter.instant.toFixed(2) * 100));
260 | }
261 |
262 | }
263 |
264 | function log(message){
265 | dataElement.innerHTML = dataElement.innerHTML+ '
' + new Date().toISOString() + " " + message;
266 | console.log(message)
267 | }
268 |
269 | // Define the AudioWorkletProcessor as a string to be dynamically loaded
270 | const volumeProcessorCode = `
271 | class VolumeProcessor extends AudioWorkletProcessor {
272 | constructor() {
273 | super();
274 | this.instant = 0.0;
275 | this.slow = 0.0;
276 | this.clip = 0.0;
277 | }
278 |
279 | process(inputs, outputs, parameters) {
280 | const input = inputs[0];
281 | if (input.length > 0) {
282 | const channelData = input[0];
283 | let sum = 0.0;
284 | let clipcount = 0;
285 |
286 | for (let i = 0; i < channelData.length; ++i) {
287 | sum += channelData[i] * channelData[i];
288 | if (Math.abs(channelData[i]) > 0.99) {
289 | clipcount += 1;
290 | }
291 | }
292 |
293 | this.instant = Math.sqrt(sum / channelData.length);
294 | this.slow = 0.95 * this.slow + 0.05 * this.instant;
295 | this.clip = clipcount / channelData.length;
296 |
297 | this.port.postMessage({
298 | instant: this.instant,
299 | slow: this.slow,
300 | clip: this.clip
301 | });
302 | }
303 |
304 | return true;
305 | }
306 | }
307 |
308 | registerProcessor('volume-processor', VolumeProcessor);
309 | `;
310 |
311 | // Meter class that generates a number correlated to audio volume.
312 | // The meter class itself displays nothing, but it makes the
313 | // instantaneous and time-decaying volumes available for inspection.
314 | // It also reports on the fraction of samples that were at or near
315 | // the top of the measurement range.
316 | class SoundMeter {
317 | constructor(context) {
318 | this.context = context;
319 | this.instant = 0.0;
320 | this.slow = 0.0;
321 | this.clip = 0.0;
322 |
323 | // Create a Blob URL for the AudioWorkletProcessor script
324 | // This approach is taken to avoid the need for a separate file for the processor
325 | const blob = new Blob([volumeProcessorCode], { type: 'application/javascript' });
326 | const url = URL.createObjectURL(blob);
327 |
328 | // Load the processor module into the AudioWorklet
329 | this.ready = context.audioWorklet.addModule(url).then(() => {
330 | // Create an AudioWorkletNode using the registered processor
331 | this.node = new AudioWorkletNode(context, 'volume-processor');
332 | // Set up a message event listener to receive volume metrics from the processor
333 | this.node.port.onmessage = (event) => {
334 | this.instant = event.data.instant;
335 | this.slow = event.data.slow;
336 | this.clip = event.data.clip;
337 | };
338 | });
339 | }
340 |
341 | async connectToSource(stream, callback) {
342 | console.log("pipe-log at " + new Date().toISOString() + " SoundMeter connecting");
343 | try {
344 | // Wait for the processor module to be ready
345 | await this.ready;
346 | // Create a MediaStreamSource from the provided audio stream
347 | this.mic = this.context.createMediaStreamSource(stream);
348 | // Connect the media source to the AudioWorkletNode
349 | this.mic.connect(this.node);
350 | // Connect the node to the destination to ensure it works (even though we don't need the output)
351 | this.node.connect(this.context.destination);
352 | // If a callback is provided, call it with no error
353 | if (typeof callback !== 'undefined') {
354 | callback(null);
355 | }
356 | } catch (e) {
357 | // Log any errors that occur during the connection process
358 | console.log("pipe-log at " + timeStamp() + " error occurred in SoundMeter:", e);
359 | // If a callback is provided, call it with the error
360 | if (typeof callback !== 'undefined') {
361 | callback(e);
362 | }
363 | }
364 | }
365 |
366 | stop() {
367 | // Disconnect the media source and the AudioWorkletNode to stop processing
368 | this.mic.disconnect();
369 | this.node.disconnect();
370 | }
371 | }
372 |
373 |
374 | //browser ID
375 | function getBrowser(){
376 | var nVer = navigator.appVersion;
377 | var nAgt = navigator.userAgent;
378 | var browserName = navigator.appName;
379 | var fullVersion = ''+parseFloat(navigator.appVersion);
380 | var majorVersion = parseInt(navigator.appVersion,10);
381 | var nameOffset,verOffset,ix;
382 |
383 | // In Opera, the true version is after "Opera" or after "Version"
384 | if ((verOffset=nAgt.indexOf("Opera"))!=-1) {
385 | browserName = "Opera";
386 | fullVersion = nAgt.substring(verOffset+6);
387 | if ((verOffset=nAgt.indexOf("Version"))!=-1)
388 | fullVersion = nAgt.substring(verOffset+8);
389 | }
390 | // In MSIE, the true version is after "MSIE" in userAgent
391 | else if ((verOffset=nAgt.indexOf("MSIE"))!=-1) {
392 | browserName = "Microsoft Internet Explorer";
393 | fullVersion = nAgt.substring(verOffset+5);
394 | }
395 | // In Chrome, the true version is after "Chrome"
396 | else if ((verOffset=nAgt.indexOf("Chrome"))!=-1) {
397 | browserName = "Chrome";
398 | fullVersion = nAgt.substring(verOffset+7);
399 | }
400 | // In Safari, the true version is after "Safari" or after "Version"
401 | else if ((verOffset=nAgt.indexOf("Safari"))!=-1) {
402 | browserName = "Safari";
403 | fullVersion = nAgt.substring(verOffset+7);
404 | if ((verOffset=nAgt.indexOf("Version"))!=-1)
405 | fullVersion = nAgt.substring(verOffset+8);
406 | }
407 | // In Firefox, the true version is after "Firefox"
408 | else if ((verOffset=nAgt.indexOf("Firefox"))!=-1) {
409 | browserName = "Firefox";
410 | fullVersion = nAgt.substring(verOffset+8);
411 | }
412 | // In most other browsers, "name/version" is at the end of userAgent
413 | else if ( (nameOffset=nAgt.lastIndexOf(' ')+1) <
414 | (verOffset=nAgt.lastIndexOf('/')) )
415 | {
416 | browserName = nAgt.substring(nameOffset,verOffset);
417 | fullVersion = nAgt.substring(verOffset+1);
418 | if (browserName.toLowerCase()==browserName.toUpperCase()) {
419 | browserName = navigator.appName;
420 | }
421 | }
422 | // trim the fullVersion string at semicolon/space if present
423 | if ((ix=fullVersion.indexOf(";"))!=-1)
424 | fullVersion=fullVersion.substring(0,ix);
425 | if ((ix=fullVersion.indexOf(" "))!=-1)
426 | fullVersion=fullVersion.substring(0,ix);
427 |
428 | majorVersion = parseInt(''+fullVersion,10);
429 | if (isNaN(majorVersion)) {
430 | fullVersion = ''+parseFloat(navigator.appVersion);
431 | majorVersion = parseInt(navigator.appVersion,10);
432 | }
433 |
434 |
435 | return browserName;
436 | }
437 |
--------------------------------------------------------------------------------