@lls/lls-audio 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,2592 @@
1
+ "use strict";
2
+
3
+ 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; };
4
+
5
+ (function (f) {
6
+ if ((typeof exports === "undefined" ? "undefined" : _typeof(exports)) === "object" && typeof module !== "undefined") {
7
+ module.exports = f();
8
+ } else if (typeof define === "function" && define.amd) {
9
+ define([], f);
10
+ } else {
11
+ var g;if (typeof window !== "undefined") {
12
+ g = window;
13
+ } else if (typeof global !== "undefined") {
14
+ g = global;
15
+ } else if (typeof self !== "undefined") {
16
+ g = self;
17
+ } else {
18
+ g = this;
19
+ }g.adapter = f();
20
+ }
21
+ })(function () {
22
+ var define, module, exports;return function e(t, n, r) {
23
+ function s(o, u) {
24
+ if (!n[o]) {
25
+ if (!t[o]) {
26
+ var a = typeof require == "function" && require;if (!u && a) return a(o, !0);if (i) return i(o, !0);var f = new Error("Cannot find module '" + o + "'");throw f.code = "MODULE_NOT_FOUND", f;
27
+ }var l = n[o] = { exports: {} };t[o][0].call(l.exports, function (e) {
28
+ var n = t[o][1][e];return s(n ? n : e);
29
+ }, l, l.exports, e, t, n, r);
30
+ }return n[o].exports;
31
+ }var i = typeof require == "function" && require;for (var o = 0; o < r.length; o++) {
32
+ s(r[o]);
33
+ }return s;
34
+ }({ 1: [function (require, module, exports) {
35
+ /* eslint-env node */
36
+ 'use strict';
37
+
38
+ // SDP helpers.
39
+
40
+ var SDPUtils = {};
41
+
42
+ // Generate an alphanumeric identifier for cname or mids.
43
+ // TODO: use UUIDs instead? https://gist.github.com/jed/982883
44
+ SDPUtils.generateIdentifier = function () {
45
+ return Math.random().toString(36).substr(2, 10);
46
+ };
47
+
48
+ // The RTCP CNAME used by all peerconnections from the same JS.
49
+ SDPUtils.localCName = SDPUtils.generateIdentifier();
50
+
51
+ // Splits SDP into lines, dealing with both CRLF and LF.
52
+ SDPUtils.splitLines = function (blob) {
53
+ return blob.trim().split('\n').map(function (line) {
54
+ return line.trim();
55
+ });
56
+ };
57
+ // Splits SDP into sessionpart and mediasections. Ensures CRLF.
58
+ SDPUtils.splitSections = function (blob) {
59
+ var parts = blob.split('\nm=');
60
+ return parts.map(function (part, index) {
61
+ return (index > 0 ? 'm=' + part : part).trim() + '\r\n';
62
+ });
63
+ };
64
+
65
+ // Returns lines that start with a certain prefix.
66
+ SDPUtils.matchPrefix = function (blob, prefix) {
67
+ return SDPUtils.splitLines(blob).filter(function (line) {
68
+ return line.indexOf(prefix) === 0;
69
+ });
70
+ };
71
+
72
+ // Parses an ICE candidate line. Sample input:
73
+ // candidate:702786350 2 udp 41819902 8.8.8.8 60769 typ relay raddr 8.8.8.8
74
+ // rport 55996"
75
+ SDPUtils.parseCandidate = function (line) {
76
+ var parts;
77
+ // Parse both variants.
78
+ if (line.indexOf('a=candidate:') === 0) {
79
+ parts = line.substring(12).split(' ');
80
+ } else {
81
+ parts = line.substring(10).split(' ');
82
+ }
83
+
84
+ var candidate = {
85
+ foundation: parts[0],
86
+ component: parts[1],
87
+ protocol: parts[2].toLowerCase(),
88
+ priority: parseInt(parts[3], 10),
89
+ ip: parts[4],
90
+ port: parseInt(parts[5], 10),
91
+ // skip parts[6] == 'typ'
92
+ type: parts[7]
93
+ };
94
+
95
+ for (var i = 8; i < parts.length; i += 2) {
96
+ switch (parts[i]) {
97
+ case 'raddr':
98
+ candidate.relatedAddress = parts[i + 1];
99
+ break;
100
+ case 'rport':
101
+ candidate.relatedPort = parseInt(parts[i + 1], 10);
102
+ break;
103
+ case 'tcptype':
104
+ candidate.tcpType = parts[i + 1];
105
+ break;
106
+ default:
107
+ // Unknown extensions are silently ignored.
108
+ break;
109
+ }
110
+ }
111
+ return candidate;
112
+ };
113
+
114
+ // Translates a candidate object into SDP candidate attribute.
115
+ SDPUtils.writeCandidate = function (candidate) {
116
+ var sdp = [];
117
+ sdp.push(candidate.foundation);
118
+ sdp.push(candidate.component);
119
+ sdp.push(candidate.protocol.toUpperCase());
120
+ sdp.push(candidate.priority);
121
+ sdp.push(candidate.ip);
122
+ sdp.push(candidate.port);
123
+
124
+ var type = candidate.type;
125
+ sdp.push('typ');
126
+ sdp.push(type);
127
+ if (type !== 'host' && candidate.relatedAddress && candidate.relatedPort) {
128
+ sdp.push('raddr');
129
+ sdp.push(candidate.relatedAddress); // was: relAddr
130
+ sdp.push('rport');
131
+ sdp.push(candidate.relatedPort); // was: relPort
132
+ }
133
+ if (candidate.tcpType && candidate.protocol.toLowerCase() === 'tcp') {
134
+ sdp.push('tcptype');
135
+ sdp.push(candidate.tcpType);
136
+ }
137
+ return 'candidate:' + sdp.join(' ');
138
+ };
139
+
140
+ // Parses an rtpmap line, returns RTCRtpCoddecParameters. Sample input:
141
+ // a=rtpmap:111 opus/48000/2
142
+ SDPUtils.parseRtpMap = function (line) {
143
+ var parts = line.substr(9).split(' ');
144
+ var parsed = {
145
+ payloadType: parseInt(parts.shift(), 10) // was: id
146
+ };
147
+
148
+ parts = parts[0].split('/');
149
+
150
+ parsed.name = parts[0];
151
+ parsed.clockRate = parseInt(parts[1], 10); // was: clockrate
152
+ // was: channels
153
+ parsed.numChannels = parts.length === 3 ? parseInt(parts[2], 10) : 1;
154
+ return parsed;
155
+ };
156
+
157
+ // Generate an a=rtpmap line from RTCRtpCodecCapability or
158
+ // RTCRtpCodecParameters.
159
+ SDPUtils.writeRtpMap = function (codec) {
160
+ var pt = codec.payloadType;
161
+ if (codec.preferredPayloadType !== undefined) {
162
+ pt = codec.preferredPayloadType;
163
+ }
164
+ return 'a=rtpmap:' + pt + ' ' + codec.name + '/' + codec.clockRate + (codec.numChannels !== 1 ? '/' + codec.numChannels : '') + '\r\n';
165
+ };
166
+
167
+ // Parses an a=extmap line (headerextension from RFC 5285). Sample input:
168
+ // a=extmap:2 urn:ietf:params:rtp-hdrext:toffset
169
+ SDPUtils.parseExtmap = function (line) {
170
+ var parts = line.substr(9).split(' ');
171
+ return {
172
+ id: parseInt(parts[0], 10),
173
+ uri: parts[1]
174
+ };
175
+ };
176
+
177
+ // Generates a=extmap line from RTCRtpHeaderExtensionParameters or
178
+ // RTCRtpHeaderExtension.
179
+ SDPUtils.writeExtmap = function (headerExtension) {
180
+ return 'a=extmap:' + (headerExtension.id || headerExtension.preferredId) + ' ' + headerExtension.uri + '\r\n';
181
+ };
182
+
183
+ // Parses an ftmp line, returns dictionary. Sample input:
184
+ // a=fmtp:96 vbr=on;cng=on
185
+ // Also deals with vbr=on; cng=on
186
+ SDPUtils.parseFmtp = function (line) {
187
+ var parsed = {};
188
+ var kv;
189
+ var parts = line.substr(line.indexOf(' ') + 1).split(';');
190
+ for (var j = 0; j < parts.length; j++) {
191
+ kv = parts[j].trim().split('=');
192
+ parsed[kv[0].trim()] = kv[1];
193
+ }
194
+ return parsed;
195
+ };
196
+
197
+ // Generates an a=ftmp line from RTCRtpCodecCapability or RTCRtpCodecParameters.
198
+ SDPUtils.writeFmtp = function (codec) {
199
+ var line = '';
200
+ var pt = codec.payloadType;
201
+ if (codec.preferredPayloadType !== undefined) {
202
+ pt = codec.preferredPayloadType;
203
+ }
204
+ if (codec.parameters && Object.keys(codec.parameters).length) {
205
+ var params = [];
206
+ Object.keys(codec.parameters).forEach(function (param) {
207
+ params.push(param + '=' + codec.parameters[param]);
208
+ });
209
+ line += 'a=fmtp:' + pt + ' ' + params.join(';') + '\r\n';
210
+ }
211
+ return line;
212
+ };
213
+
214
+ // Parses an rtcp-fb line, returns RTCPRtcpFeedback object. Sample input:
215
+ // a=rtcp-fb:98 nack rpsi
216
+ SDPUtils.parseRtcpFb = function (line) {
217
+ var parts = line.substr(line.indexOf(' ') + 1).split(' ');
218
+ return {
219
+ type: parts.shift(),
220
+ parameter: parts.join(' ')
221
+ };
222
+ };
223
+ // Generate a=rtcp-fb lines from RTCRtpCodecCapability or RTCRtpCodecParameters.
224
+ SDPUtils.writeRtcpFb = function (codec) {
225
+ var lines = '';
226
+ var pt = codec.payloadType;
227
+ if (codec.preferredPayloadType !== undefined) {
228
+ pt = codec.preferredPayloadType;
229
+ }
230
+ if (codec.rtcpFeedback && codec.rtcpFeedback.length) {
231
+ // FIXME: special handling for trr-int?
232
+ codec.rtcpFeedback.forEach(function (fb) {
233
+ lines += 'a=rtcp-fb:' + pt + ' ' + fb.type + (fb.parameter && fb.parameter.length ? ' ' + fb.parameter : '') + '\r\n';
234
+ });
235
+ }
236
+ return lines;
237
+ };
238
+
239
+ // Parses an RFC 5576 ssrc media attribute. Sample input:
240
+ // a=ssrc:3735928559 cname:something
241
+ SDPUtils.parseSsrcMedia = function (line) {
242
+ var sp = line.indexOf(' ');
243
+ var parts = {
244
+ ssrc: parseInt(line.substr(7, sp - 7), 10)
245
+ };
246
+ var colon = line.indexOf(':', sp);
247
+ if (colon > -1) {
248
+ parts.attribute = line.substr(sp + 1, colon - sp - 1);
249
+ parts.value = line.substr(colon + 1);
250
+ } else {
251
+ parts.attribute = line.substr(sp + 1);
252
+ }
253
+ return parts;
254
+ };
255
+
256
+ // Extracts DTLS parameters from SDP media section or sessionpart.
257
+ // FIXME: for consistency with other functions this should only
258
+ // get the fingerprint line as input. See also getIceParameters.
259
+ SDPUtils.getDtlsParameters = function (mediaSection, sessionpart) {
260
+ var lines = SDPUtils.splitLines(mediaSection);
261
+ // Search in session part, too.
262
+ lines = lines.concat(SDPUtils.splitLines(sessionpart));
263
+ var fpLine = lines.filter(function (line) {
264
+ return line.indexOf('a=fingerprint:') === 0;
265
+ })[0].substr(14);
266
+ // Note: a=setup line is ignored since we use the 'auto' role.
267
+ var dtlsParameters = {
268
+ role: 'auto',
269
+ fingerprints: [{
270
+ algorithm: fpLine.split(' ')[0],
271
+ value: fpLine.split(' ')[1]
272
+ }]
273
+ };
274
+ return dtlsParameters;
275
+ };
276
+
277
+ // Serializes DTLS parameters to SDP.
278
+ SDPUtils.writeDtlsParameters = function (params, setupType) {
279
+ var sdp = 'a=setup:' + setupType + '\r\n';
280
+ params.fingerprints.forEach(function (fp) {
281
+ sdp += 'a=fingerprint:' + fp.algorithm + ' ' + fp.value + '\r\n';
282
+ });
283
+ return sdp;
284
+ };
285
+ // Parses ICE information from SDP media section or sessionpart.
286
+ // FIXME: for consistency with other functions this should only
287
+ // get the ice-ufrag and ice-pwd lines as input.
288
+ SDPUtils.getIceParameters = function (mediaSection, sessionpart) {
289
+ var lines = SDPUtils.splitLines(mediaSection);
290
+ // Search in session part, too.
291
+ lines = lines.concat(SDPUtils.splitLines(sessionpart));
292
+ var iceParameters = {
293
+ usernameFragment: lines.filter(function (line) {
294
+ return line.indexOf('a=ice-ufrag:') === 0;
295
+ })[0].substr(12),
296
+ password: lines.filter(function (line) {
297
+ return line.indexOf('a=ice-pwd:') === 0;
298
+ })[0].substr(10)
299
+ };
300
+ return iceParameters;
301
+ };
302
+
303
+ // Serializes ICE parameters to SDP.
304
+ SDPUtils.writeIceParameters = function (params) {
305
+ return 'a=ice-ufrag:' + params.usernameFragment + '\r\n' + 'a=ice-pwd:' + params.password + '\r\n';
306
+ };
307
+
308
+ // Parses the SDP media section and returns RTCRtpParameters.
309
+ SDPUtils.parseRtpParameters = function (mediaSection) {
310
+ var description = {
311
+ codecs: [],
312
+ headerExtensions: [],
313
+ fecMechanisms: [],
314
+ rtcp: []
315
+ };
316
+ var lines = SDPUtils.splitLines(mediaSection);
317
+ var mline = lines[0].split(' ');
318
+ for (var i = 3; i < mline.length; i++) {
319
+ // find all codecs from mline[3..]
320
+ var pt = mline[i];
321
+ var rtpmapline = SDPUtils.matchPrefix(mediaSection, 'a=rtpmap:' + pt + ' ')[0];
322
+ if (rtpmapline) {
323
+ var codec = SDPUtils.parseRtpMap(rtpmapline);
324
+ var fmtps = SDPUtils.matchPrefix(mediaSection, 'a=fmtp:' + pt + ' ');
325
+ // Only the first a=fmtp:<pt> is considered.
326
+ codec.parameters = fmtps.length ? SDPUtils.parseFmtp(fmtps[0]) : {};
327
+ codec.rtcpFeedback = SDPUtils.matchPrefix(mediaSection, 'a=rtcp-fb:' + pt + ' ').map(SDPUtils.parseRtcpFb);
328
+ description.codecs.push(codec);
329
+ // parse FEC mechanisms from rtpmap lines.
330
+ switch (codec.name.toUpperCase()) {
331
+ case 'RED':
332
+ case 'ULPFEC':
333
+ description.fecMechanisms.push(codec.name.toUpperCase());
334
+ break;
335
+ default:
336
+ // only RED and ULPFEC are recognized as FEC mechanisms.
337
+ break;
338
+ }
339
+ }
340
+ }
341
+ SDPUtils.matchPrefix(mediaSection, 'a=extmap:').forEach(function (line) {
342
+ description.headerExtensions.push(SDPUtils.parseExtmap(line));
343
+ });
344
+ // FIXME: parse rtcp.
345
+ return description;
346
+ };
347
+
348
+ // Generates parts of the SDP media section describing the capabilities /
349
+ // parameters.
350
+ SDPUtils.writeRtpDescription = function (kind, caps) {
351
+ var sdp = '';
352
+
353
+ // Build the mline.
354
+ sdp += 'm=' + kind + ' ';
355
+ sdp += caps.codecs.length > 0 ? '9' : '0'; // reject if no codecs.
356
+ sdp += ' UDP/TLS/RTP/SAVPF ';
357
+ sdp += caps.codecs.map(function (codec) {
358
+ if (codec.preferredPayloadType !== undefined) {
359
+ return codec.preferredPayloadType;
360
+ }
361
+ return codec.payloadType;
362
+ }).join(' ') + '\r\n';
363
+
364
+ sdp += 'c=IN IP4 0.0.0.0\r\n';
365
+ sdp += 'a=rtcp:9 IN IP4 0.0.0.0\r\n';
366
+
367
+ // Add a=rtpmap lines for each codec. Also fmtp and rtcp-fb.
368
+ caps.codecs.forEach(function (codec) {
369
+ sdp += SDPUtils.writeRtpMap(codec);
370
+ sdp += SDPUtils.writeFmtp(codec);
371
+ sdp += SDPUtils.writeRtcpFb(codec);
372
+ });
373
+ // FIXME: add headerExtensions, fecMechanismş and rtcp.
374
+ sdp += 'a=rtcp-mux\r\n';
375
+ return sdp;
376
+ };
377
+
378
+ // Parses the SDP media section and returns an array of
379
+ // RTCRtpEncodingParameters.
380
+ SDPUtils.parseRtpEncodingParameters = function (mediaSection) {
381
+ var encodingParameters = [];
382
+ var description = SDPUtils.parseRtpParameters(mediaSection);
383
+ var hasRed = description.fecMechanisms.indexOf('RED') !== -1;
384
+ var hasUlpfec = description.fecMechanisms.indexOf('ULPFEC') !== -1;
385
+
386
+ // filter a=ssrc:... cname:, ignore PlanB-msid
387
+ var ssrcs = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:').map(function (line) {
388
+ return SDPUtils.parseSsrcMedia(line);
389
+ }).filter(function (parts) {
390
+ return parts.attribute === 'cname';
391
+ });
392
+ var primarySsrc = ssrcs.length > 0 && ssrcs[0].ssrc;
393
+ var secondarySsrc;
394
+
395
+ var flows = SDPUtils.matchPrefix(mediaSection, 'a=ssrc-group:FID').map(function (line) {
396
+ var parts = line.split(' ');
397
+ parts.shift();
398
+ return parts.map(function (part) {
399
+ return parseInt(part, 10);
400
+ });
401
+ });
402
+ if (flows.length > 0 && flows[0].length > 1 && flows[0][0] === primarySsrc) {
403
+ secondarySsrc = flows[0][1];
404
+ }
405
+
406
+ description.codecs.forEach(function (codec) {
407
+ if (codec.name.toUpperCase() === 'RTX' && codec.parameters.apt) {
408
+ var encParam = {
409
+ ssrc: primarySsrc,
410
+ codecPayloadType: parseInt(codec.parameters.apt, 10),
411
+ rtx: {
412
+ payloadType: codec.payloadType,
413
+ ssrc: secondarySsrc
414
+ }
415
+ };
416
+ encodingParameters.push(encParam);
417
+ if (hasRed) {
418
+ encParam = JSON.parse(JSON.stringify(encParam));
419
+ encParam.fec = {
420
+ ssrc: secondarySsrc,
421
+ mechanism: hasUlpfec ? 'red+ulpfec' : 'red'
422
+ };
423
+ encodingParameters.push(encParam);
424
+ }
425
+ }
426
+ });
427
+ if (encodingParameters.length === 0 && primarySsrc) {
428
+ encodingParameters.push({
429
+ ssrc: primarySsrc
430
+ });
431
+ }
432
+
433
+ // we support both b=AS and b=TIAS but interpret AS as TIAS.
434
+ var bandwidth = SDPUtils.matchPrefix(mediaSection, 'b=');
435
+ if (bandwidth.length) {
436
+ if (bandwidth[0].indexOf('b=TIAS:') === 0) {
437
+ bandwidth = parseInt(bandwidth[0].substr(7), 10);
438
+ } else if (bandwidth[0].indexOf('b=AS:') === 0) {
439
+ bandwidth = parseInt(bandwidth[0].substr(5), 10);
440
+ }
441
+ encodingParameters.forEach(function (params) {
442
+ params.maxBitrate = bandwidth;
443
+ });
444
+ }
445
+ return encodingParameters;
446
+ };
447
+
448
+ SDPUtils.writeSessionBoilerplate = function () {
449
+ // FIXME: sess-id should be an NTP timestamp.
450
+ return 'v=0\r\n' + 'o=thisisadapterortc 8169639915646943137 2 IN IP4 127.0.0.1\r\n' + 's=-\r\n' + 't=0 0\r\n';
451
+ };
452
+
453
+ SDPUtils.writeMediaSection = function (transceiver, caps, type, stream) {
454
+ var sdp = SDPUtils.writeRtpDescription(transceiver.kind, caps);
455
+
456
+ // Map ICE parameters (ufrag, pwd) to SDP.
457
+ sdp += SDPUtils.writeIceParameters(transceiver.iceGatherer.getLocalParameters());
458
+
459
+ // Map DTLS parameters to SDP.
460
+ sdp += SDPUtils.writeDtlsParameters(transceiver.dtlsTransport.getLocalParameters(), type === 'offer' ? 'actpass' : 'active');
461
+
462
+ sdp += 'a=mid:' + transceiver.mid + '\r\n';
463
+
464
+ if (transceiver.rtpSender && transceiver.rtpReceiver) {
465
+ sdp += 'a=sendrecv\r\n';
466
+ } else if (transceiver.rtpSender) {
467
+ sdp += 'a=sendonly\r\n';
468
+ } else if (transceiver.rtpReceiver) {
469
+ sdp += 'a=recvonly\r\n';
470
+ } else {
471
+ sdp += 'a=inactive\r\n';
472
+ }
473
+
474
+ // FIXME: for RTX there might be multiple SSRCs. Not implemented in Edge yet.
475
+ if (transceiver.rtpSender) {
476
+ var msid = 'msid:' + stream.id + ' ' + transceiver.rtpSender.track.id + '\r\n';
477
+ sdp += 'a=' + msid;
478
+ sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].ssrc + ' ' + msid;
479
+ }
480
+ // FIXME: this should be written by writeRtpDescription.
481
+ sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].ssrc + ' cname:' + SDPUtils.localCName + '\r\n';
482
+ return sdp;
483
+ };
484
+
485
+ // Gets the direction from the mediaSection or the sessionpart.
486
+ SDPUtils.getDirection = function (mediaSection, sessionpart) {
487
+ // Look for sendrecv, sendonly, recvonly, inactive, default to sendrecv.
488
+ var lines = SDPUtils.splitLines(mediaSection);
489
+ for (var i = 0; i < lines.length; i++) {
490
+ switch (lines[i]) {
491
+ case 'a=sendrecv':
492
+ case 'a=sendonly':
493
+ case 'a=recvonly':
494
+ case 'a=inactive':
495
+ return lines[i].substr(2);
496
+ default:
497
+ // FIXME: What should happen here?
498
+ }
499
+ }
500
+ if (sessionpart) {
501
+ return SDPUtils.getDirection(sessionpart);
502
+ }
503
+ return 'sendrecv';
504
+ };
505
+
506
+ // Expose public methods.
507
+ module.exports = SDPUtils;
508
+ }, {}], 2: [function (require, module, exports) {
509
+ /*
510
+ * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
511
+ *
512
+ * Use of this source code is governed by a BSD-style license
513
+ * that can be found in the LICENSE file in the root of the source
514
+ * tree.
515
+ */
516
+ /* eslint-env node */
517
+
518
+ 'use strict';
519
+
520
+ // Shimming starts here.
521
+
522
+ (function () {
523
+ // Utils.
524
+ var logging = require('./utils').log;
525
+ var browserDetails = require('./utils').browserDetails;
526
+ // Export to the adapter global object visible in the browser.
527
+ module.exports.browserDetails = browserDetails;
528
+ module.exports.extractVersion = require('./utils').extractVersion;
529
+ module.exports.disableLog = require('./utils').disableLog;
530
+
531
+ // Uncomment the line below if you want logging to occur, including logging
532
+ // for the switch statement below. Can also be turned on in the browser via
533
+ // adapter.disableLog(false), but then logging from the switch statement below
534
+ // will not appear.
535
+ // require('./utils').disableLog(false);
536
+
537
+ // Browser shims.
538
+ var chromeShim = require('./chrome/chrome_shim') || null;
539
+ var edgeShim = require('./edge/edge_shim') || null;
540
+ var firefoxShim = require('./firefox/firefox_shim') || null;
541
+ var safariShim = require('./safari/safari_shim') || null;
542
+
543
+ // Shim browser if found.
544
+ switch (browserDetails.browser) {
545
+ case 'opera': // fallthrough as it uses chrome shims
546
+ case 'chrome':
547
+ if (!chromeShim || !chromeShim.shimPeerConnection) {
548
+ logging('Chrome shim is not included in this adapter release.');
549
+ return;
550
+ }
551
+ logging('adapter.js shimming chrome.');
552
+ // Export to the adapter global object visible in the browser.
553
+ module.exports.browserShim = chromeShim;
554
+
555
+ chromeShim.shimGetUserMedia();
556
+ chromeShim.shimMediaStream();
557
+ chromeShim.shimSourceObject();
558
+ chromeShim.shimPeerConnection();
559
+ chromeShim.shimOnTrack();
560
+ break;
561
+ case 'firefox':
562
+ if (!firefoxShim || !firefoxShim.shimPeerConnection) {
563
+ logging('Firefox shim is not included in this adapter release.');
564
+ return;
565
+ }
566
+ logging('adapter.js shimming firefox.');
567
+ // Export to the adapter global object visible in the browser.
568
+ module.exports.browserShim = firefoxShim;
569
+
570
+ firefoxShim.shimGetUserMedia();
571
+ firefoxShim.shimSourceObject();
572
+ firefoxShim.shimPeerConnection();
573
+ firefoxShim.shimOnTrack();
574
+ break;
575
+ case 'edge':
576
+ if (!edgeShim || !edgeShim.shimPeerConnection) {
577
+ logging('MS edge shim is not included in this adapter release.');
578
+ return;
579
+ }
580
+ logging('adapter.js shimming edge.');
581
+ // Export to the adapter global object visible in the browser.
582
+ module.exports.browserShim = edgeShim;
583
+
584
+ edgeShim.shimGetUserMedia();
585
+ edgeShim.shimPeerConnection();
586
+ break;
587
+ case 'safari':
588
+ if (!safariShim) {
589
+ logging('Safari shim is not included in this adapter release.');
590
+ return;
591
+ }
592
+ logging('adapter.js shimming safari.');
593
+ // Export to the adapter global object visible in the browser.
594
+ module.exports.browserShim = safariShim;
595
+
596
+ safariShim.shimGetUserMedia();
597
+ break;
598
+ default:
599
+ logging('Unsupported browser!');
600
+ }
601
+ })();
602
+ }, { "./chrome/chrome_shim": 3, "./edge/edge_shim": 5, "./firefox/firefox_shim": 7, "./safari/safari_shim": 9, "./utils": 10 }], 3: [function (require, module, exports) {
603
+
604
+ /*
605
+ * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
606
+ *
607
+ * Use of this source code is governed by a BSD-style license
608
+ * that can be found in the LICENSE file in the root of the source
609
+ * tree.
610
+ */
611
+ /* eslint-env node */
612
+ 'use strict';
613
+
614
+ var logging = require('../utils.js').log;
615
+ var browserDetails = require('../utils.js').browserDetails;
616
+
617
+ var chromeShim = {
618
+ shimMediaStream: function shimMediaStream() {
619
+ window.MediaStream = window.MediaStream || window.webkitMediaStream;
620
+ },
621
+
622
+ shimOnTrack: function shimOnTrack() {
623
+ if ((typeof window === "undefined" ? "undefined" : _typeof(window)) === 'object' && window.RTCPeerConnection && !('ontrack' in window.RTCPeerConnection.prototype)) {
624
+ Object.defineProperty(window.RTCPeerConnection.prototype, 'ontrack', {
625
+ get: function get() {
626
+ return this._ontrack;
627
+ },
628
+ set: function set(f) {
629
+ var self = this;
630
+ if (this._ontrack) {
631
+ this.removeEventListener('track', this._ontrack);
632
+ this.removeEventListener('addstream', this._ontrackpoly);
633
+ }
634
+ this.addEventListener('track', this._ontrack = f);
635
+ this.addEventListener('addstream', this._ontrackpoly = function (e) {
636
+ // onaddstream does not fire when a track is added to an existing
637
+ // stream. But stream.onaddtrack is implemented so we use that.
638
+ e.stream.addEventListener('addtrack', function (te) {
639
+ var event = new Event('track');
640
+ event.track = te.track;
641
+ event.receiver = { track: te.track };
642
+ event.streams = [e.stream];
643
+ self.dispatchEvent(event);
644
+ });
645
+ e.stream.getTracks().forEach(function (track) {
646
+ var event = new Event('track');
647
+ event.track = track;
648
+ event.receiver = { track: track };
649
+ event.streams = [e.stream];
650
+ this.dispatchEvent(event);
651
+ }.bind(this));
652
+ }.bind(this));
653
+ }
654
+ });
655
+ }
656
+ },
657
+
658
+ shimSourceObject: function shimSourceObject() {
659
+ if ((typeof window === "undefined" ? "undefined" : _typeof(window)) === 'object') {
660
+ if (window.HTMLMediaElement && !('srcObject' in window.HTMLMediaElement.prototype)) {
661
+ // Shim the srcObject property, once, when HTMLMediaElement is found.
662
+ Object.defineProperty(window.HTMLMediaElement.prototype, 'srcObject', {
663
+ get: function get() {
664
+ return this._srcObject;
665
+ },
666
+ set: function set(stream) {
667
+ var self = this;
668
+ // Use _srcObject as a private property for this shim
669
+ this._srcObject = stream;
670
+ if (this.src) {
671
+ URL.revokeObjectURL(this.src);
672
+ }
673
+
674
+ if (!stream) {
675
+ this.src = '';
676
+ return;
677
+ }
678
+ this.src = URL.createObjectURL(stream);
679
+ // We need to recreate the blob url when a track is added or
680
+ // removed. Doing it manually since we want to avoid a recursion.
681
+ stream.addEventListener('addtrack', function () {
682
+ if (self.src) {
683
+ URL.revokeObjectURL(self.src);
684
+ }
685
+ self.src = URL.createObjectURL(stream);
686
+ });
687
+ stream.addEventListener('removetrack', function () {
688
+ if (self.src) {
689
+ URL.revokeObjectURL(self.src);
690
+ }
691
+ self.src = URL.createObjectURL(stream);
692
+ });
693
+ }
694
+ });
695
+ }
696
+ }
697
+ },
698
+
699
+ shimPeerConnection: function shimPeerConnection() {
700
+ // The RTCPeerConnection object.
701
+ window.RTCPeerConnection = function (pcConfig, pcConstraints) {
702
+ // Translate iceTransportPolicy to iceTransports,
703
+ // see https://code.google.com/p/webrtc/issues/detail?id=4869
704
+ logging('PeerConnection');
705
+ if (pcConfig && pcConfig.iceTransportPolicy) {
706
+ pcConfig.iceTransports = pcConfig.iceTransportPolicy;
707
+ }
708
+
709
+ var pc = new webkitRTCPeerConnection(pcConfig, pcConstraints);
710
+ var origGetStats = pc.getStats.bind(pc);
711
+ pc.getStats = function (selector, successCallback, errorCallback) {
712
+ var self = this;
713
+ var args = arguments;
714
+
715
+ // If selector is a function then we are in the old style stats so just
716
+ // pass back the original getStats format to avoid breaking old users.
717
+ if (arguments.length > 0 && typeof selector === 'function') {
718
+ return origGetStats(selector, successCallback);
719
+ }
720
+
721
+ var fixChromeStats_ = function fixChromeStats_(response) {
722
+ var standardReport = {};
723
+ var reports = response.result();
724
+ reports.forEach(function (report) {
725
+ var standardStats = {
726
+ id: report.id,
727
+ timestamp: report.timestamp,
728
+ type: report.type
729
+ };
730
+ report.names().forEach(function (name) {
731
+ standardStats[name] = report.stat(name);
732
+ });
733
+ standardReport[standardStats.id] = standardStats;
734
+ });
735
+
736
+ return standardReport;
737
+ };
738
+
739
+ // shim getStats with maplike support
740
+ var makeMapStats = function makeMapStats(stats, legacyStats) {
741
+ var map = new Map(Object.keys(stats).map(function (key) {
742
+ return [key, stats[key]];
743
+ }));
744
+ legacyStats = legacyStats || stats;
745
+ Object.keys(legacyStats).forEach(function (key) {
746
+ map[key] = legacyStats[key];
747
+ });
748
+ return map;
749
+ };
750
+
751
+ if (arguments.length >= 2) {
752
+ var successCallbackWrapper_ = function successCallbackWrapper_(response) {
753
+ args[1](makeMapStats(fixChromeStats_(response)));
754
+ };
755
+
756
+ return origGetStats.apply(this, [successCallbackWrapper_, arguments[0]]);
757
+ }
758
+
759
+ // promise-support
760
+ return new Promise(function (resolve, reject) {
761
+ if (args.length === 1 && (typeof selector === "undefined" ? "undefined" : _typeof(selector)) === 'object') {
762
+ origGetStats.apply(self, [function (response) {
763
+ resolve(makeMapStats(fixChromeStats_(response)));
764
+ }, reject]);
765
+ } else {
766
+ // Preserve legacy chrome stats only on legacy access of stats obj
767
+ origGetStats.apply(self, [function (response) {
768
+ resolve(makeMapStats(fixChromeStats_(response), response.result()));
769
+ }, reject]);
770
+ }
771
+ }).then(successCallback, errorCallback);
772
+ };
773
+
774
+ return pc;
775
+ };
776
+ window.RTCPeerConnection.prototype = webkitRTCPeerConnection.prototype;
777
+
778
+ // wrap static methods. Currently just generateCertificate.
779
+ if (webkitRTCPeerConnection.generateCertificate) {
780
+ Object.defineProperty(window.RTCPeerConnection, 'generateCertificate', {
781
+ get: function get() {
782
+ return webkitRTCPeerConnection.generateCertificate;
783
+ }
784
+ });
785
+ }
786
+
787
+ ['createOffer', 'createAnswer'].forEach(function (method) {
788
+ var nativeMethod = webkitRTCPeerConnection.prototype[method];
789
+ webkitRTCPeerConnection.prototype[method] = function () {
790
+ var self = this;
791
+ if (arguments.length < 1 || arguments.length === 1 && _typeof(arguments[0]) === 'object') {
792
+ var opts = arguments.length === 1 ? arguments[0] : undefined;
793
+ return new Promise(function (resolve, reject) {
794
+ nativeMethod.apply(self, [resolve, reject, opts]);
795
+ });
796
+ }
797
+ return nativeMethod.apply(this, arguments);
798
+ };
799
+ });
800
+
801
+ // add promise support -- natively available in Chrome 51
802
+ if (browserDetails.version < 51) {
803
+ ['setLocalDescription', 'setRemoteDescription', 'addIceCandidate'].forEach(function (method) {
804
+ var nativeMethod = webkitRTCPeerConnection.prototype[method];
805
+ webkitRTCPeerConnection.prototype[method] = function () {
806
+ var args = arguments;
807
+ var self = this;
808
+ var promise = new Promise(function (resolve, reject) {
809
+ nativeMethod.apply(self, [args[0], resolve, reject]);
810
+ });
811
+ if (args.length < 2) {
812
+ return promise;
813
+ }
814
+ return promise.then(function () {
815
+ args[1].apply(null, []);
816
+ }, function (err) {
817
+ if (args.length >= 3) {
818
+ args[2].apply(null, [err]);
819
+ }
820
+ });
821
+ };
822
+ });
823
+ }
824
+
825
+ // shim implicit creation of RTCSessionDescription/RTCIceCandidate
826
+ ['setLocalDescription', 'setRemoteDescription', 'addIceCandidate'].forEach(function (method) {
827
+ var nativeMethod = webkitRTCPeerConnection.prototype[method];
828
+ webkitRTCPeerConnection.prototype[method] = function () {
829
+ arguments[0] = new (method === 'addIceCandidate' ? RTCIceCandidate : RTCSessionDescription)(arguments[0]);
830
+ return nativeMethod.apply(this, arguments);
831
+ };
832
+ });
833
+
834
+ // support for addIceCandidate(null or undefined)
835
+ var nativeAddIceCandidate = RTCPeerConnection.prototype.addIceCandidate;
836
+ RTCPeerConnection.prototype.addIceCandidate = function () {
837
+ if (!arguments[0]) {
838
+ if (arguments[1]) {
839
+ arguments[1].apply(null);
840
+ }
841
+ return Promise.resolve();
842
+ }
843
+ return nativeAddIceCandidate.apply(this, arguments);
844
+ };
845
+ }
846
+ };
847
+
848
+ // Expose public methods.
849
+ module.exports = {
850
+ shimMediaStream: chromeShim.shimMediaStream,
851
+ shimOnTrack: chromeShim.shimOnTrack,
852
+ shimSourceObject: chromeShim.shimSourceObject,
853
+ shimPeerConnection: chromeShim.shimPeerConnection,
854
+ shimGetUserMedia: require('./getusermedia')
855
+ };
856
+ }, { "../utils.js": 10, "./getusermedia": 4 }], 4: [function (require, module, exports) {
857
+ /*
858
+ * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
859
+ *
860
+ * Use of this source code is governed by a BSD-style license
861
+ * that can be found in the LICENSE file in the root of the source
862
+ * tree.
863
+ */
864
+ /* eslint-env node */
865
+ 'use strict';
866
+
867
+ var logging = require('../utils.js').log;
868
+
869
+ // Expose public methods.
870
+ module.exports = function () {
871
+ var constraintsToChrome_ = function constraintsToChrome_(c) {
872
+ if ((typeof c === "undefined" ? "undefined" : _typeof(c)) !== 'object' || c.mandatory || c.optional) {
873
+ return c;
874
+ }
875
+ var cc = {};
876
+ Object.keys(c).forEach(function (key) {
877
+ if (key === 'require' || key === 'advanced' || key === 'mediaSource') {
878
+ return;
879
+ }
880
+ var r = _typeof(c[key]) === 'object' ? c[key] : { ideal: c[key] };
881
+ if (r.exact !== undefined && typeof r.exact === 'number') {
882
+ r.min = r.max = r.exact;
883
+ }
884
+ var oldname_ = function oldname_(prefix, name) {
885
+ if (prefix) {
886
+ return prefix + name.charAt(0).toUpperCase() + name.slice(1);
887
+ }
888
+ return name === 'deviceId' ? 'sourceId' : name;
889
+ };
890
+ if (r.ideal !== undefined) {
891
+ cc.optional = cc.optional || [];
892
+ var oc = {};
893
+ if (typeof r.ideal === 'number') {
894
+ oc[oldname_('min', key)] = r.ideal;
895
+ cc.optional.push(oc);
896
+ oc = {};
897
+ oc[oldname_('max', key)] = r.ideal;
898
+ cc.optional.push(oc);
899
+ } else {
900
+ oc[oldname_('', key)] = r.ideal;
901
+ cc.optional.push(oc);
902
+ }
903
+ }
904
+ if (r.exact !== undefined && typeof r.exact !== 'number') {
905
+ cc.mandatory = cc.mandatory || {};
906
+ cc.mandatory[oldname_('', key)] = r.exact;
907
+ } else {
908
+ ['min', 'max'].forEach(function (mix) {
909
+ if (r[mix] !== undefined) {
910
+ cc.mandatory = cc.mandatory || {};
911
+ cc.mandatory[oldname_(mix, key)] = r[mix];
912
+ }
913
+ });
914
+ }
915
+ });
916
+ if (c.advanced) {
917
+ cc.optional = (cc.optional || []).concat(c.advanced);
918
+ }
919
+ return cc;
920
+ };
921
+
922
+ var shimConstraints_ = function shimConstraints_(constraints, func) {
923
+ constraints = JSON.parse(JSON.stringify(constraints));
924
+ if (constraints && constraints.audio) {
925
+ constraints.audio = constraintsToChrome_(constraints.audio);
926
+ }
927
+ if (constraints && _typeof(constraints.video) === 'object') {
928
+ // Shim facingMode for mobile, where it defaults to "user".
929
+ var face = constraints.video.facingMode;
930
+ face = face && ((typeof face === "undefined" ? "undefined" : _typeof(face)) === 'object' ? face : { ideal: face });
931
+
932
+ if (face && (face.exact === 'user' || face.exact === 'environment' || face.ideal === 'user' || face.ideal === 'environment') && !(navigator.mediaDevices.getSupportedConstraints && navigator.mediaDevices.getSupportedConstraints().facingMode)) {
933
+ delete constraints.video.facingMode;
934
+ if (face.exact === 'environment' || face.ideal === 'environment') {
935
+ // Look for "back" in label, or use last cam (typically back cam).
936
+ return navigator.mediaDevices.enumerateDevices().then(function (devices) {
937
+ devices = devices.filter(function (d) {
938
+ return d.kind === 'videoinput';
939
+ });
940
+ var back = devices.find(function (d) {
941
+ return d.label.toLowerCase().indexOf('back') !== -1;
942
+ }) || devices.length && devices[devices.length - 1];
943
+ if (back) {
944
+ constraints.video.deviceId = face.exact ? { exact: back.deviceId } : { ideal: back.deviceId };
945
+ }
946
+ constraints.video = constraintsToChrome_(constraints.video);
947
+ logging('chrome: ' + JSON.stringify(constraints));
948
+ return func(constraints);
949
+ });
950
+ }
951
+ }
952
+ constraints.video = constraintsToChrome_(constraints.video);
953
+ }
954
+ logging('chrome: ' + JSON.stringify(constraints));
955
+ return func(constraints);
956
+ };
957
+
958
+ var shimError_ = function shimError_(e) {
959
+ return {
960
+ name: {
961
+ PermissionDeniedError: 'NotAllowedError',
962
+ ConstraintNotSatisfiedError: 'OverconstrainedError'
963
+ }[e.name] || e.name,
964
+ message: e.message,
965
+ constraint: e.constraintName,
966
+ toString: function toString() {
967
+ return this.name + (this.message && ': ') + this.message;
968
+ }
969
+ };
970
+ };
971
+
972
+ var getUserMedia_ = function getUserMedia_(constraints, onSuccess, onError) {
973
+ shimConstraints_(constraints, function (c) {
974
+ navigator.webkitGetUserMedia(c, onSuccess, function (e) {
975
+ onError(shimError_(e));
976
+ });
977
+ });
978
+ };
979
+
980
+ navigator.getUserMedia = getUserMedia_;
981
+
982
+ // Returns the result of getUserMedia as a Promise.
983
+ var getUserMediaPromise_ = function getUserMediaPromise_(constraints) {
984
+ return new Promise(function (resolve, reject) {
985
+ navigator.getUserMedia(constraints, resolve, reject);
986
+ });
987
+ };
988
+
989
+ if (!navigator.mediaDevices) {
990
+ navigator.mediaDevices = {
991
+ getUserMedia: getUserMediaPromise_,
992
+ enumerateDevices: function enumerateDevices() {
993
+ return new Promise(function (resolve) {
994
+ var kinds = { audio: 'audioinput', video: 'videoinput' };
995
+ return MediaStreamTrack.getSources(function (devices) {
996
+ resolve(devices.map(function (device) {
997
+ return { label: device.label,
998
+ kind: kinds[device.kind],
999
+ deviceId: device.id,
1000
+ groupId: '' };
1001
+ }));
1002
+ });
1003
+ });
1004
+ }
1005
+ };
1006
+ }
1007
+
1008
+ // A shim for getUserMedia method on the mediaDevices object.
1009
+ // TODO(KaptenJansson) remove once implemented in Chrome stable.
1010
+ if (!navigator.mediaDevices.getUserMedia) {
1011
+ navigator.mediaDevices.getUserMedia = function (constraints) {
1012
+ return getUserMediaPromise_(constraints);
1013
+ };
1014
+ } else {
1015
+ // Even though Chrome 45 has navigator.mediaDevices and a getUserMedia
1016
+ // function which returns a Promise, it does not accept spec-style
1017
+ // constraints.
1018
+ var origGetUserMedia = navigator.mediaDevices.getUserMedia.bind(navigator.mediaDevices);
1019
+ navigator.mediaDevices.getUserMedia = function (cs) {
1020
+ return shimConstraints_(cs, function (c) {
1021
+ return origGetUserMedia(c).then(function (stream) {
1022
+ if (c.audio && !stream.getAudioTracks().length || c.video && !stream.getVideoTracks().length) {
1023
+ stream.getTracks().forEach(function (track) {
1024
+ track.stop();
1025
+ });
1026
+ throw new DOMException('', 'NotFoundError');
1027
+ }
1028
+ return stream;
1029
+ }, function (e) {
1030
+ return Promise.reject(shimError_(e));
1031
+ });
1032
+ });
1033
+ };
1034
+ }
1035
+
1036
+ // Dummy devicechange event methods.
1037
+ // TODO(KaptenJansson) remove once implemented in Chrome stable.
1038
+ if (typeof navigator.mediaDevices.addEventListener === 'undefined') {
1039
+ navigator.mediaDevices.addEventListener = function () {
1040
+ logging('Dummy mediaDevices.addEventListener called.');
1041
+ };
1042
+ }
1043
+ if (typeof navigator.mediaDevices.removeEventListener === 'undefined') {
1044
+ navigator.mediaDevices.removeEventListener = function () {
1045
+ logging('Dummy mediaDevices.removeEventListener called.');
1046
+ };
1047
+ }
1048
+ };
1049
+ }, { "../utils.js": 10 }], 5: [function (require, module, exports) {
1050
+ /*
1051
+ * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
1052
+ *
1053
+ * Use of this source code is governed by a BSD-style license
1054
+ * that can be found in the LICENSE file in the root of the source
1055
+ * tree.
1056
+ */
1057
+ /* eslint-env node */
1058
+ 'use strict';
1059
+
1060
+ var SDPUtils = require('sdp');
1061
+ var browserDetails = require('../utils').browserDetails;
1062
+
1063
+ var edgeShim = {
1064
+ shimPeerConnection: function shimPeerConnection() {
1065
+ if (window.RTCIceGatherer) {
1066
+ // ORTC defines an RTCIceCandidate object but no constructor.
1067
+ // Not implemented in Edge.
1068
+ if (!window.RTCIceCandidate) {
1069
+ window.RTCIceCandidate = function (args) {
1070
+ return args;
1071
+ };
1072
+ }
1073
+ // ORTC does not have a session description object but
1074
+ // other browsers (i.e. Chrome) that will support both PC and ORTC
1075
+ // in the future might have this defined already.
1076
+ if (!window.RTCSessionDescription) {
1077
+ window.RTCSessionDescription = function (args) {
1078
+ return args;
1079
+ };
1080
+ }
1081
+ // this adds an additional event listener to MediaStrackTrack that signals
1082
+ // when a tracks enabled property was changed.
1083
+ var origMSTEnabled = Object.getOwnPropertyDescriptor(MediaStreamTrack.prototype, 'enabled');
1084
+ Object.defineProperty(MediaStreamTrack.prototype, 'enabled', {
1085
+ set: function set(value) {
1086
+ origMSTEnabled.set.call(this, value);
1087
+ var ev = new Event('enabled');
1088
+ ev.enabled = value;
1089
+ this.dispatchEvent(ev);
1090
+ }
1091
+ });
1092
+ }
1093
+
1094
+ window.RTCPeerConnection = function (config) {
1095
+ var self = this;
1096
+
1097
+ var _eventTarget = document.createDocumentFragment();
1098
+ ['addEventListener', 'removeEventListener', 'dispatchEvent'].forEach(function (method) {
1099
+ self[method] = _eventTarget[method].bind(_eventTarget);
1100
+ });
1101
+
1102
+ this.onicecandidate = null;
1103
+ this.onaddstream = null;
1104
+ this.ontrack = null;
1105
+ this.onremovestream = null;
1106
+ this.onsignalingstatechange = null;
1107
+ this.oniceconnectionstatechange = null;
1108
+ this.onnegotiationneeded = null;
1109
+ this.ondatachannel = null;
1110
+
1111
+ this.localStreams = [];
1112
+ this.remoteStreams = [];
1113
+ this.getLocalStreams = function () {
1114
+ return self.localStreams;
1115
+ };
1116
+ this.getRemoteStreams = function () {
1117
+ return self.remoteStreams;
1118
+ };
1119
+
1120
+ this.localDescription = new RTCSessionDescription({
1121
+ type: '',
1122
+ sdp: ''
1123
+ });
1124
+ this.remoteDescription = new RTCSessionDescription({
1125
+ type: '',
1126
+ sdp: ''
1127
+ });
1128
+ this.signalingState = 'stable';
1129
+ this.iceConnectionState = 'new';
1130
+ this.iceGatheringState = 'new';
1131
+
1132
+ this.iceOptions = {
1133
+ gatherPolicy: 'all',
1134
+ iceServers: []
1135
+ };
1136
+ if (config && config.iceTransportPolicy) {
1137
+ switch (config.iceTransportPolicy) {
1138
+ case 'all':
1139
+ case 'relay':
1140
+ this.iceOptions.gatherPolicy = config.iceTransportPolicy;
1141
+ break;
1142
+ case 'none':
1143
+ // FIXME: remove once implementation and spec have added this.
1144
+ throw new TypeError('iceTransportPolicy "none" not supported');
1145
+ default:
1146
+ // don't set iceTransportPolicy.
1147
+ break;
1148
+ }
1149
+ }
1150
+ this.usingBundle = config && config.bundlePolicy === 'max-bundle';
1151
+
1152
+ if (config && config.iceServers) {
1153
+ // Edge does not like
1154
+ // 1) stun:
1155
+ // 2) turn: that does not have all of turn:host:port?transport=udp
1156
+ // 3) turn: with ipv6 addresses
1157
+ var iceServers = JSON.parse(JSON.stringify(config.iceServers));
1158
+ this.iceOptions.iceServers = iceServers.filter(function (server) {
1159
+ if (server && server.urls) {
1160
+ var urls = server.urls;
1161
+ if (typeof urls === 'string') {
1162
+ urls = [urls];
1163
+ }
1164
+ urls = urls.filter(function (url) {
1165
+ return url.indexOf('turn:') === 0 && url.indexOf('transport=udp') !== -1 && url.indexOf('turn:[') === -1 || url.indexOf('stun:') === 0 && browserDetails.version >= 14393;
1166
+ })[0];
1167
+ return !!urls;
1168
+ }
1169
+ return false;
1170
+ });
1171
+ }
1172
+ this._config = config;
1173
+
1174
+ // per-track iceGathers, iceTransports, dtlsTransports, rtpSenders, ...
1175
+ // everything that is needed to describe a SDP m-line.
1176
+ this.transceivers = [];
1177
+
1178
+ // since the iceGatherer is currently created in createOffer but we
1179
+ // must not emit candidates until after setLocalDescription we buffer
1180
+ // them in this array.
1181
+ this._localIceCandidatesBuffer = [];
1182
+ };
1183
+
1184
+ window.RTCPeerConnection.prototype._emitBufferedCandidates = function () {
1185
+ var self = this;
1186
+ var sections = SDPUtils.splitSections(self.localDescription.sdp);
1187
+ // FIXME: need to apply ice candidates in a way which is async but
1188
+ // in-order
1189
+ this._localIceCandidatesBuffer.forEach(function (event) {
1190
+ var end = !event.candidate || Object.keys(event.candidate).length === 0;
1191
+ if (end) {
1192
+ for (var j = 1; j < sections.length; j++) {
1193
+ if (sections[j].indexOf('\r\na=end-of-candidates\r\n') === -1) {
1194
+ sections[j] += 'a=end-of-candidates\r\n';
1195
+ }
1196
+ }
1197
+ } else if (event.candidate.candidate.indexOf('typ endOfCandidates') === -1) {
1198
+ sections[event.candidate.sdpMLineIndex + 1] += 'a=' + event.candidate.candidate + '\r\n';
1199
+ }
1200
+ self.localDescription.sdp = sections.join('');
1201
+ self.dispatchEvent(event);
1202
+ if (self.onicecandidate !== null) {
1203
+ self.onicecandidate(event);
1204
+ }
1205
+ if (!event.candidate && self.iceGatheringState !== 'complete') {
1206
+ var complete = self.transceivers.every(function (transceiver) {
1207
+ return transceiver.iceGatherer && transceiver.iceGatherer.state === 'completed';
1208
+ });
1209
+ if (complete) {
1210
+ self.iceGatheringState = 'complete';
1211
+ }
1212
+ }
1213
+ });
1214
+ this._localIceCandidatesBuffer = [];
1215
+ };
1216
+
1217
+ window.RTCPeerConnection.prototype.getConfiguration = function () {
1218
+ return this._config;
1219
+ };
1220
+
1221
+ window.RTCPeerConnection.prototype.addStream = function (stream) {
1222
+ // Clone is necessary for local demos mostly, attaching directly
1223
+ // to two different senders does not work (build 10547).
1224
+ var clonedStream = stream.clone();
1225
+ stream.getTracks().forEach(function (track, idx) {
1226
+ var clonedTrack = clonedStream.getTracks()[idx];
1227
+ track.addEventListener('enabled', function (event) {
1228
+ clonedTrack.enabled = event.enabled;
1229
+ });
1230
+ });
1231
+ this.localStreams.push(clonedStream);
1232
+ this._maybeFireNegotiationNeeded();
1233
+ };
1234
+
1235
+ window.RTCPeerConnection.prototype.removeStream = function (stream) {
1236
+ var idx = this.localStreams.indexOf(stream);
1237
+ if (idx > -1) {
1238
+ this.localStreams.splice(idx, 1);
1239
+ this._maybeFireNegotiationNeeded();
1240
+ }
1241
+ };
1242
+
1243
+ window.RTCPeerConnection.prototype.getSenders = function () {
1244
+ return this.transceivers.filter(function (transceiver) {
1245
+ return !!transceiver.rtpSender;
1246
+ }).map(function (transceiver) {
1247
+ return transceiver.rtpSender;
1248
+ });
1249
+ };
1250
+
1251
+ window.RTCPeerConnection.prototype.getReceivers = function () {
1252
+ return this.transceivers.filter(function (transceiver) {
1253
+ return !!transceiver.rtpReceiver;
1254
+ }).map(function (transceiver) {
1255
+ return transceiver.rtpReceiver;
1256
+ });
1257
+ };
1258
+
1259
+ // Determines the intersection of local and remote capabilities.
1260
+ window.RTCPeerConnection.prototype._getCommonCapabilities = function (localCapabilities, remoteCapabilities) {
1261
+ var commonCapabilities = {
1262
+ codecs: [],
1263
+ headerExtensions: [],
1264
+ fecMechanisms: []
1265
+ };
1266
+ localCapabilities.codecs.forEach(function (lCodec) {
1267
+ for (var i = 0; i < remoteCapabilities.codecs.length; i++) {
1268
+ var rCodec = remoteCapabilities.codecs[i];
1269
+ if (lCodec.name.toLowerCase() === rCodec.name.toLowerCase() && lCodec.clockRate === rCodec.clockRate) {
1270
+ // number of channels is the highest common number of channels
1271
+ rCodec.numChannels = Math.min(lCodec.numChannels, rCodec.numChannels);
1272
+ // push rCodec so we reply with offerer payload type
1273
+ commonCapabilities.codecs.push(rCodec);
1274
+
1275
+ // determine common feedback mechanisms
1276
+ rCodec.rtcpFeedback = rCodec.rtcpFeedback.filter(function (fb) {
1277
+ for (var j = 0; j < lCodec.rtcpFeedback.length; j++) {
1278
+ if (lCodec.rtcpFeedback[j].type === fb.type && lCodec.rtcpFeedback[j].parameter === fb.parameter) {
1279
+ return true;
1280
+ }
1281
+ }
1282
+ return false;
1283
+ });
1284
+ // FIXME: also need to determine .parameters
1285
+ // see https://github.com/openpeer/ortc/issues/569
1286
+ break;
1287
+ }
1288
+ }
1289
+ });
1290
+
1291
+ localCapabilities.headerExtensions.forEach(function (lHeaderExtension) {
1292
+ for (var i = 0; i < remoteCapabilities.headerExtensions.length; i++) {
1293
+ var rHeaderExtension = remoteCapabilities.headerExtensions[i];
1294
+ if (lHeaderExtension.uri === rHeaderExtension.uri) {
1295
+ commonCapabilities.headerExtensions.push(rHeaderExtension);
1296
+ break;
1297
+ }
1298
+ }
1299
+ });
1300
+
1301
+ // FIXME: fecMechanisms
1302
+ return commonCapabilities;
1303
+ };
1304
+
1305
+ // Create ICE gatherer, ICE transport and DTLS transport.
1306
+ window.RTCPeerConnection.prototype._createIceAndDtlsTransports = function (mid, sdpMLineIndex) {
1307
+ var self = this;
1308
+ var iceGatherer = new RTCIceGatherer(self.iceOptions);
1309
+ var iceTransport = new RTCIceTransport(iceGatherer);
1310
+ iceGatherer.onlocalcandidate = function (evt) {
1311
+ var event = new Event('icecandidate');
1312
+ event.candidate = { sdpMid: mid, sdpMLineIndex: sdpMLineIndex };
1313
+
1314
+ var cand = evt.candidate;
1315
+ var end = !cand || Object.keys(cand).length === 0;
1316
+ // Edge emits an empty object for RTCIceCandidateComplete‥
1317
+ if (end) {
1318
+ // polyfill since RTCIceGatherer.state is not implemented in
1319
+ // Edge 10547 yet.
1320
+ if (iceGatherer.state === undefined) {
1321
+ iceGatherer.state = 'completed';
1322
+ }
1323
+
1324
+ // Emit a candidate with type endOfCandidates to make the samples
1325
+ // work. Edge requires addIceCandidate with this empty candidate
1326
+ // to start checking. The real solution is to signal
1327
+ // end-of-candidates to the other side when getting the null
1328
+ // candidate but some apps (like the samples) don't do that.
1329
+ event.candidate.candidate = 'candidate:1 1 udp 1 0.0.0.0 9 typ endOfCandidates';
1330
+ } else {
1331
+ // RTCIceCandidate doesn't have a component, needs to be added
1332
+ cand.component = iceTransport.component === 'RTCP' ? 2 : 1;
1333
+ event.candidate.candidate = SDPUtils.writeCandidate(cand);
1334
+ }
1335
+
1336
+ // update local description.
1337
+ var sections = SDPUtils.splitSections(self.localDescription.sdp);
1338
+ if (event.candidate.candidate.indexOf('typ endOfCandidates') === -1) {
1339
+ sections[event.candidate.sdpMLineIndex + 1] += 'a=' + event.candidate.candidate + '\r\n';
1340
+ } else {
1341
+ sections[event.candidate.sdpMLineIndex + 1] += 'a=end-of-candidates\r\n';
1342
+ }
1343
+ self.localDescription.sdp = sections.join('');
1344
+
1345
+ var complete = self.transceivers.every(function (transceiver) {
1346
+ return transceiver.iceGatherer && transceiver.iceGatherer.state === 'completed';
1347
+ });
1348
+
1349
+ // Emit candidate if localDescription is set.
1350
+ // Also emits null candidate when all gatherers are complete.
1351
+ switch (self.iceGatheringState) {
1352
+ case 'new':
1353
+ self._localIceCandidatesBuffer.push(event);
1354
+ if (end && complete) {
1355
+ self._localIceCandidatesBuffer.push(new Event('icecandidate'));
1356
+ }
1357
+ break;
1358
+ case 'gathering':
1359
+ self._emitBufferedCandidates();
1360
+ self.dispatchEvent(event);
1361
+ if (self.onicecandidate !== null) {
1362
+ self.onicecandidate(event);
1363
+ }
1364
+ if (complete) {
1365
+ self.dispatchEvent(new Event('icecandidate'));
1366
+ if (self.onicecandidate !== null) {
1367
+ self.onicecandidate(new Event('icecandidate'));
1368
+ }
1369
+ self.iceGatheringState = 'complete';
1370
+ }
1371
+ break;
1372
+ case 'complete':
1373
+ // should not happen... currently!
1374
+ break;
1375
+ default:
1376
+ // no-op.
1377
+ break;
1378
+ }
1379
+ };
1380
+ iceTransport.onicestatechange = function () {
1381
+ self._updateConnectionState();
1382
+ };
1383
+
1384
+ var dtlsTransport = new RTCDtlsTransport(iceTransport);
1385
+ dtlsTransport.ondtlsstatechange = function () {
1386
+ self._updateConnectionState();
1387
+ };
1388
+ dtlsTransport.onerror = function () {
1389
+ // onerror does not set state to failed by itself.
1390
+ dtlsTransport.state = 'failed';
1391
+ self._updateConnectionState();
1392
+ };
1393
+
1394
+ return {
1395
+ iceGatherer: iceGatherer,
1396
+ iceTransport: iceTransport,
1397
+ dtlsTransport: dtlsTransport
1398
+ };
1399
+ };
1400
+
1401
+ // Start the RTP Sender and Receiver for a transceiver.
1402
+ window.RTCPeerConnection.prototype._transceive = function (transceiver, send, recv) {
1403
+ var params = this._getCommonCapabilities(transceiver.localCapabilities, transceiver.remoteCapabilities);
1404
+ if (send && transceiver.rtpSender) {
1405
+ params.encodings = transceiver.sendEncodingParameters;
1406
+ params.rtcp = {
1407
+ cname: SDPUtils.localCName
1408
+ };
1409
+ if (transceiver.recvEncodingParameters.length) {
1410
+ params.rtcp.ssrc = transceiver.recvEncodingParameters[0].ssrc;
1411
+ }
1412
+ transceiver.rtpSender.send(params);
1413
+ }
1414
+ if (recv && transceiver.rtpReceiver) {
1415
+ // remove RTX field in Edge 14942
1416
+ if (transceiver.kind === 'video' && transceiver.recvEncodingParameters) {
1417
+ transceiver.recvEncodingParameters.forEach(function (p) {
1418
+ delete p.rtx;
1419
+ });
1420
+ }
1421
+ params.encodings = transceiver.recvEncodingParameters;
1422
+ params.rtcp = {
1423
+ cname: transceiver.cname
1424
+ };
1425
+ if (transceiver.sendEncodingParameters.length) {
1426
+ params.rtcp.ssrc = transceiver.sendEncodingParameters[0].ssrc;
1427
+ }
1428
+ transceiver.rtpReceiver.receive(params);
1429
+ }
1430
+ };
1431
+
1432
+ window.RTCPeerConnection.prototype.setLocalDescription = function (description) {
1433
+ var self = this;
1434
+ var sections;
1435
+ var sessionpart;
1436
+ if (description.type === 'offer') {
1437
+ // FIXME: What was the purpose of this empty if statement?
1438
+ // if (!this._pendingOffer) {
1439
+ // } else {
1440
+ if (this._pendingOffer) {
1441
+ // VERY limited support for SDP munging. Limited to:
1442
+ // * changing the order of codecs
1443
+ sections = SDPUtils.splitSections(description.sdp);
1444
+ sessionpart = sections.shift();
1445
+ sections.forEach(function (mediaSection, sdpMLineIndex) {
1446
+ var caps = SDPUtils.parseRtpParameters(mediaSection);
1447
+ self._pendingOffer[sdpMLineIndex].localCapabilities = caps;
1448
+ });
1449
+ this.transceivers = this._pendingOffer;
1450
+ delete this._pendingOffer;
1451
+ }
1452
+ } else if (description.type === 'answer') {
1453
+ sections = SDPUtils.splitSections(self.remoteDescription.sdp);
1454
+ sessionpart = sections.shift();
1455
+ var isIceLite = SDPUtils.matchPrefix(sessionpart, 'a=ice-lite').length > 0;
1456
+ sections.forEach(function (mediaSection, sdpMLineIndex) {
1457
+ var transceiver = self.transceivers[sdpMLineIndex];
1458
+ var iceGatherer = transceiver.iceGatherer;
1459
+ var iceTransport = transceiver.iceTransport;
1460
+ var dtlsTransport = transceiver.dtlsTransport;
1461
+ var localCapabilities = transceiver.localCapabilities;
1462
+ var remoteCapabilities = transceiver.remoteCapabilities;
1463
+
1464
+ var rejected = mediaSection.split('\n', 1)[0].split(' ', 2)[1] === '0';
1465
+
1466
+ if (!rejected && !transceiver.isDatachannel) {
1467
+ var remoteIceParameters = SDPUtils.getIceParameters(mediaSection, sessionpart);
1468
+ if (isIceLite) {
1469
+ var cands = SDPUtils.matchPrefix(mediaSection, 'a=candidate:').map(function (cand) {
1470
+ return SDPUtils.parseCandidate(cand);
1471
+ }).filter(function (cand) {
1472
+ return cand.component === '1';
1473
+ });
1474
+ // ice-lite only includes host candidates in the SDP so we can
1475
+ // use setRemoteCandidates (which implies an
1476
+ // RTCIceCandidateComplete)
1477
+ if (cands.length) {
1478
+ iceTransport.setRemoteCandidates(cands);
1479
+ }
1480
+ }
1481
+ var remoteDtlsParameters = SDPUtils.getDtlsParameters(mediaSection, sessionpart);
1482
+ if (isIceLite) {
1483
+ remoteDtlsParameters.role = 'server';
1484
+ }
1485
+
1486
+ if (!self.usingBundle || sdpMLineIndex === 0) {
1487
+ iceTransport.start(iceGatherer, remoteIceParameters, isIceLite ? 'controlling' : 'controlled');
1488
+ dtlsTransport.start(remoteDtlsParameters);
1489
+ }
1490
+
1491
+ // Calculate intersection of capabilities.
1492
+ var params = self._getCommonCapabilities(localCapabilities, remoteCapabilities);
1493
+
1494
+ // Start the RTCRtpSender. The RTCRtpReceiver for this
1495
+ // transceiver has already been started in setRemoteDescription.
1496
+ self._transceive(transceiver, params.codecs.length > 0, false);
1497
+ }
1498
+ });
1499
+ }
1500
+
1501
+ this.localDescription = {
1502
+ type: description.type,
1503
+ sdp: description.sdp
1504
+ };
1505
+ switch (description.type) {
1506
+ case 'offer':
1507
+ this._updateSignalingState('have-local-offer');
1508
+ break;
1509
+ case 'answer':
1510
+ this._updateSignalingState('stable');
1511
+ break;
1512
+ default:
1513
+ throw new TypeError('unsupported type "' + description.type + '"');
1514
+ }
1515
+
1516
+ // If a success callback was provided, emit ICE candidates after it
1517
+ // has been executed. Otherwise, emit callback after the Promise is
1518
+ // resolved.
1519
+ var hasCallback = arguments.length > 1 && typeof arguments[1] === 'function';
1520
+ if (hasCallback) {
1521
+ var cb = arguments[1];
1522
+ window.setTimeout(function () {
1523
+ cb();
1524
+ if (self.iceGatheringState === 'new') {
1525
+ self.iceGatheringState = 'gathering';
1526
+ }
1527
+ self._emitBufferedCandidates();
1528
+ }, 0);
1529
+ }
1530
+ var p = Promise.resolve();
1531
+ p.then(function () {
1532
+ if (!hasCallback) {
1533
+ if (self.iceGatheringState === 'new') {
1534
+ self.iceGatheringState = 'gathering';
1535
+ }
1536
+ // Usually candidates will be emitted earlier.
1537
+ window.setTimeout(self._emitBufferedCandidates.bind(self), 500);
1538
+ }
1539
+ });
1540
+ return p;
1541
+ };
1542
+
1543
+ window.RTCPeerConnection.prototype.setRemoteDescription = function (description) {
1544
+ var self = this;
1545
+ var stream = new MediaStream();
1546
+ var receiverList = [];
1547
+ var sections = SDPUtils.splitSections(description.sdp);
1548
+ var sessionpart = sections.shift();
1549
+ var isIceLite = SDPUtils.matchPrefix(sessionpart, 'a=ice-lite').length > 0;
1550
+ this.usingBundle = SDPUtils.matchPrefix(sessionpart, 'a=group:BUNDLE ').length > 0;
1551
+ sections.forEach(function (mediaSection, sdpMLineIndex) {
1552
+ var lines = SDPUtils.splitLines(mediaSection);
1553
+ var mline = lines[0].substr(2).split(' ');
1554
+ var kind = mline[0];
1555
+ var rejected = mline[1] === '0';
1556
+ var direction = SDPUtils.getDirection(mediaSection, sessionpart);
1557
+
1558
+ var mid = SDPUtils.matchPrefix(mediaSection, 'a=mid:');
1559
+ if (mid.length) {
1560
+ mid = mid[0].substr(6);
1561
+ } else {
1562
+ mid = SDPUtils.generateIdentifier();
1563
+ }
1564
+
1565
+ // Reject datachannels which are not implemented yet.
1566
+ if (kind === 'application' && mline[2] === 'DTLS/SCTP') {
1567
+ self.transceivers[sdpMLineIndex] = {
1568
+ mid: mid,
1569
+ isDatachannel: true
1570
+ };
1571
+ return;
1572
+ }
1573
+
1574
+ var transceiver;
1575
+ var iceGatherer;
1576
+ var iceTransport;
1577
+ var dtlsTransport;
1578
+ var rtpSender;
1579
+ var rtpReceiver;
1580
+ var sendEncodingParameters;
1581
+ var recvEncodingParameters;
1582
+ var localCapabilities;
1583
+
1584
+ var track;
1585
+ // FIXME: ensure the mediaSection has rtcp-mux set.
1586
+ var remoteCapabilities = SDPUtils.parseRtpParameters(mediaSection);
1587
+ var remoteIceParameters;
1588
+ var remoteDtlsParameters;
1589
+ if (!rejected) {
1590
+ remoteIceParameters = SDPUtils.getIceParameters(mediaSection, sessionpart);
1591
+ remoteDtlsParameters = SDPUtils.getDtlsParameters(mediaSection, sessionpart);
1592
+ remoteDtlsParameters.role = 'client';
1593
+ }
1594
+ recvEncodingParameters = SDPUtils.parseRtpEncodingParameters(mediaSection);
1595
+
1596
+ var cname;
1597
+ // Gets the first SSRC. Note that with RTX there might be multiple
1598
+ // SSRCs.
1599
+ var remoteSsrc = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:').map(function (line) {
1600
+ return SDPUtils.parseSsrcMedia(line);
1601
+ }).filter(function (obj) {
1602
+ return obj.attribute === 'cname';
1603
+ })[0];
1604
+ if (remoteSsrc) {
1605
+ cname = remoteSsrc.value;
1606
+ }
1607
+
1608
+ var isComplete = SDPUtils.matchPrefix(mediaSection, 'a=end-of-candidates', sessionpart).length > 0;
1609
+ var cands = SDPUtils.matchPrefix(mediaSection, 'a=candidate:').map(function (cand) {
1610
+ return SDPUtils.parseCandidate(cand);
1611
+ }).filter(function (cand) {
1612
+ return cand.component === '1';
1613
+ });
1614
+ if (description.type === 'offer' && !rejected) {
1615
+ var transports = self.usingBundle && sdpMLineIndex > 0 ? {
1616
+ iceGatherer: self.transceivers[0].iceGatherer,
1617
+ iceTransport: self.transceivers[0].iceTransport,
1618
+ dtlsTransport: self.transceivers[0].dtlsTransport
1619
+ } : self._createIceAndDtlsTransports(mid, sdpMLineIndex);
1620
+
1621
+ if (isComplete) {
1622
+ transports.iceTransport.setRemoteCandidates(cands);
1623
+ }
1624
+
1625
+ localCapabilities = RTCRtpReceiver.getCapabilities(kind);
1626
+
1627
+ // filter RTX until additional stuff needed for RTX is implemented
1628
+ // in adapter.js
1629
+ localCapabilities.codecs = localCapabilities.codecs.filter(function (codec) {
1630
+ return codec.name !== 'rtx';
1631
+ });
1632
+
1633
+ sendEncodingParameters = [{
1634
+ ssrc: (2 * sdpMLineIndex + 2) * 1001
1635
+ }];
1636
+
1637
+ rtpReceiver = new RTCRtpReceiver(transports.dtlsTransport, kind);
1638
+
1639
+ track = rtpReceiver.track;
1640
+ receiverList.push([track, rtpReceiver]);
1641
+ // FIXME: not correct when there are multiple streams but that is
1642
+ // not currently supported in this shim.
1643
+ stream.addTrack(track);
1644
+
1645
+ // FIXME: look at direction.
1646
+ if (self.localStreams.length > 0 && self.localStreams[0].getTracks().length >= sdpMLineIndex) {
1647
+ var localTrack;
1648
+ if (kind === 'audio') {
1649
+ localTrack = self.localStreams[0].getAudioTracks()[0];
1650
+ } else if (kind === 'video') {
1651
+ localTrack = self.localStreams[0].getVideoTracks()[0];
1652
+ }
1653
+ if (localTrack) {
1654
+ rtpSender = new RTCRtpSender(localTrack, transports.dtlsTransport);
1655
+ }
1656
+ }
1657
+
1658
+ self.transceivers[sdpMLineIndex] = {
1659
+ iceGatherer: transports.iceGatherer,
1660
+ iceTransport: transports.iceTransport,
1661
+ dtlsTransport: transports.dtlsTransport,
1662
+ localCapabilities: localCapabilities,
1663
+ remoteCapabilities: remoteCapabilities,
1664
+ rtpSender: rtpSender,
1665
+ rtpReceiver: rtpReceiver,
1666
+ kind: kind,
1667
+ mid: mid,
1668
+ cname: cname,
1669
+ sendEncodingParameters: sendEncodingParameters,
1670
+ recvEncodingParameters: recvEncodingParameters
1671
+ };
1672
+ // Start the RTCRtpReceiver now. The RTPSender is started in
1673
+ // setLocalDescription.
1674
+ self._transceive(self.transceivers[sdpMLineIndex], false, direction === 'sendrecv' || direction === 'sendonly');
1675
+ } else if (description.type === 'answer' && !rejected) {
1676
+ transceiver = self.transceivers[sdpMLineIndex];
1677
+ iceGatherer = transceiver.iceGatherer;
1678
+ iceTransport = transceiver.iceTransport;
1679
+ dtlsTransport = transceiver.dtlsTransport;
1680
+ rtpSender = transceiver.rtpSender;
1681
+ rtpReceiver = transceiver.rtpReceiver;
1682
+ sendEncodingParameters = transceiver.sendEncodingParameters;
1683
+ localCapabilities = transceiver.localCapabilities;
1684
+
1685
+ self.transceivers[sdpMLineIndex].recvEncodingParameters = recvEncodingParameters;
1686
+ self.transceivers[sdpMLineIndex].remoteCapabilities = remoteCapabilities;
1687
+ self.transceivers[sdpMLineIndex].cname = cname;
1688
+
1689
+ if ((isIceLite || isComplete) && cands.length) {
1690
+ iceTransport.setRemoteCandidates(cands);
1691
+ }
1692
+ if (!self.usingBundle || sdpMLineIndex === 0) {
1693
+ iceTransport.start(iceGatherer, remoteIceParameters, 'controlling');
1694
+ dtlsTransport.start(remoteDtlsParameters);
1695
+ }
1696
+
1697
+ self._transceive(transceiver, direction === 'sendrecv' || direction === 'recvonly', direction === 'sendrecv' || direction === 'sendonly');
1698
+
1699
+ if (rtpReceiver && (direction === 'sendrecv' || direction === 'sendonly')) {
1700
+ track = rtpReceiver.track;
1701
+ receiverList.push([track, rtpReceiver]);
1702
+ stream.addTrack(track);
1703
+ } else {
1704
+ // FIXME: actually the receiver should be created later.
1705
+ delete transceiver.rtpReceiver;
1706
+ }
1707
+ }
1708
+ });
1709
+
1710
+ this.remoteDescription = {
1711
+ type: description.type,
1712
+ sdp: description.sdp
1713
+ };
1714
+ switch (description.type) {
1715
+ case 'offer':
1716
+ this._updateSignalingState('have-remote-offer');
1717
+ break;
1718
+ case 'answer':
1719
+ this._updateSignalingState('stable');
1720
+ break;
1721
+ default:
1722
+ throw new TypeError('unsupported type "' + description.type + '"');
1723
+ }
1724
+ if (stream.getTracks().length) {
1725
+ self.remoteStreams.push(stream);
1726
+ window.setTimeout(function () {
1727
+ var event = new Event('addstream');
1728
+ event.stream = stream;
1729
+ self.dispatchEvent(event);
1730
+ if (self.onaddstream !== null) {
1731
+ window.setTimeout(function () {
1732
+ self.onaddstream(event);
1733
+ }, 0);
1734
+ }
1735
+
1736
+ receiverList.forEach(function (item) {
1737
+ var track = item[0];
1738
+ var receiver = item[1];
1739
+ var trackEvent = new Event('track');
1740
+ trackEvent.track = track;
1741
+ trackEvent.receiver = receiver;
1742
+ trackEvent.streams = [stream];
1743
+ self.dispatchEvent(event);
1744
+ if (self.ontrack !== null) {
1745
+ window.setTimeout(function () {
1746
+ self.ontrack(trackEvent);
1747
+ }, 0);
1748
+ }
1749
+ });
1750
+ }, 0);
1751
+ }
1752
+ if (arguments.length > 1 && typeof arguments[1] === 'function') {
1753
+ window.setTimeout(arguments[1], 0);
1754
+ }
1755
+ return Promise.resolve();
1756
+ };
1757
+
1758
+ window.RTCPeerConnection.prototype.close = function () {
1759
+ this.transceivers.forEach(function (transceiver) {
1760
+ /* not yet
1761
+ if (transceiver.iceGatherer) {
1762
+ transceiver.iceGatherer.close();
1763
+ }
1764
+ */
1765
+ if (transceiver.iceTransport) {
1766
+ transceiver.iceTransport.stop();
1767
+ }
1768
+ if (transceiver.dtlsTransport) {
1769
+ transceiver.dtlsTransport.stop();
1770
+ }
1771
+ if (transceiver.rtpSender) {
1772
+ transceiver.rtpSender.stop();
1773
+ }
1774
+ if (transceiver.rtpReceiver) {
1775
+ transceiver.rtpReceiver.stop();
1776
+ }
1777
+ });
1778
+ // FIXME: clean up tracks, local streams, remote streams, etc
1779
+ this._updateSignalingState('closed');
1780
+ };
1781
+
1782
+ // Update the signaling state.
1783
+ window.RTCPeerConnection.prototype._updateSignalingState = function (newState) {
1784
+ this.signalingState = newState;
1785
+ var event = new Event('signalingstatechange');
1786
+ this.dispatchEvent(event);
1787
+ if (this.onsignalingstatechange !== null) {
1788
+ this.onsignalingstatechange(event);
1789
+ }
1790
+ };
1791
+
1792
+ // Determine whether to fire the negotiationneeded event.
1793
+ window.RTCPeerConnection.prototype._maybeFireNegotiationNeeded = function () {
1794
+ // Fire away (for now).
1795
+ var event = new Event('negotiationneeded');
1796
+ this.dispatchEvent(event);
1797
+ if (this.onnegotiationneeded !== null) {
1798
+ this.onnegotiationneeded(event);
1799
+ }
1800
+ };
1801
+
1802
+ // Update the connection state.
1803
+ window.RTCPeerConnection.prototype._updateConnectionState = function () {
1804
+ var self = this;
1805
+ var newState;
1806
+ var states = {
1807
+ 'new': 0,
1808
+ closed: 0,
1809
+ connecting: 0,
1810
+ checking: 0,
1811
+ connected: 0,
1812
+ completed: 0,
1813
+ failed: 0
1814
+ };
1815
+ this.transceivers.forEach(function (transceiver) {
1816
+ states[transceiver.iceTransport.state]++;
1817
+ states[transceiver.dtlsTransport.state]++;
1818
+ });
1819
+ // ICETransport.completed and connected are the same for this purpose.
1820
+ states.connected += states.completed;
1821
+
1822
+ newState = 'new';
1823
+ if (states.failed > 0) {
1824
+ newState = 'failed';
1825
+ } else if (states.connecting > 0 || states.checking > 0) {
1826
+ newState = 'connecting';
1827
+ } else if (states.disconnected > 0) {
1828
+ newState = 'disconnected';
1829
+ } else if (states.new > 0) {
1830
+ newState = 'new';
1831
+ } else if (states.connected > 0 || states.completed > 0) {
1832
+ newState = 'connected';
1833
+ }
1834
+
1835
+ if (newState !== self.iceConnectionState) {
1836
+ self.iceConnectionState = newState;
1837
+ var event = new Event('iceconnectionstatechange');
1838
+ this.dispatchEvent(event);
1839
+ if (this.oniceconnectionstatechange !== null) {
1840
+ this.oniceconnectionstatechange(event);
1841
+ }
1842
+ }
1843
+ };
1844
+
1845
+ window.RTCPeerConnection.prototype.createOffer = function () {
1846
+ var self = this;
1847
+ if (this._pendingOffer) {
1848
+ throw new Error('createOffer called while there is a pending offer.');
1849
+ }
1850
+ var offerOptions;
1851
+ if (arguments.length === 1 && typeof arguments[0] !== 'function') {
1852
+ offerOptions = arguments[0];
1853
+ } else if (arguments.length === 3) {
1854
+ offerOptions = arguments[2];
1855
+ }
1856
+
1857
+ var tracks = [];
1858
+ var numAudioTracks = 0;
1859
+ var numVideoTracks = 0;
1860
+ // Default to sendrecv.
1861
+ if (this.localStreams.length) {
1862
+ numAudioTracks = this.localStreams[0].getAudioTracks().length;
1863
+ numVideoTracks = this.localStreams[0].getVideoTracks().length;
1864
+ }
1865
+ // Determine number of audio and video tracks we need to send/recv.
1866
+ if (offerOptions) {
1867
+ // Reject Chrome legacy constraints.
1868
+ if (offerOptions.mandatory || offerOptions.optional) {
1869
+ throw new TypeError('Legacy mandatory/optional constraints not supported.');
1870
+ }
1871
+ if (offerOptions.offerToReceiveAudio !== undefined) {
1872
+ numAudioTracks = offerOptions.offerToReceiveAudio;
1873
+ }
1874
+ if (offerOptions.offerToReceiveVideo !== undefined) {
1875
+ numVideoTracks = offerOptions.offerToReceiveVideo;
1876
+ }
1877
+ }
1878
+ if (this.localStreams.length) {
1879
+ // Push local streams.
1880
+ this.localStreams[0].getTracks().forEach(function (track) {
1881
+ tracks.push({
1882
+ kind: track.kind,
1883
+ track: track,
1884
+ wantReceive: track.kind === 'audio' ? numAudioTracks > 0 : numVideoTracks > 0
1885
+ });
1886
+ if (track.kind === 'audio') {
1887
+ numAudioTracks--;
1888
+ } else if (track.kind === 'video') {
1889
+ numVideoTracks--;
1890
+ }
1891
+ });
1892
+ }
1893
+ // Create M-lines for recvonly streams.
1894
+ while (numAudioTracks > 0 || numVideoTracks > 0) {
1895
+ if (numAudioTracks > 0) {
1896
+ tracks.push({
1897
+ kind: 'audio',
1898
+ wantReceive: true
1899
+ });
1900
+ numAudioTracks--;
1901
+ }
1902
+ if (numVideoTracks > 0) {
1903
+ tracks.push({
1904
+ kind: 'video',
1905
+ wantReceive: true
1906
+ });
1907
+ numVideoTracks--;
1908
+ }
1909
+ }
1910
+
1911
+ var sdp = SDPUtils.writeSessionBoilerplate();
1912
+ var transceivers = [];
1913
+ tracks.forEach(function (mline, sdpMLineIndex) {
1914
+ // For each track, create an ice gatherer, ice transport,
1915
+ // dtls transport, potentially rtpsender and rtpreceiver.
1916
+ var track = mline.track;
1917
+ var kind = mline.kind;
1918
+ var mid = SDPUtils.generateIdentifier();
1919
+
1920
+ var transports = self.usingBundle && sdpMLineIndex > 0 ? {
1921
+ iceGatherer: transceivers[0].iceGatherer,
1922
+ iceTransport: transceivers[0].iceTransport,
1923
+ dtlsTransport: transceivers[0].dtlsTransport
1924
+ } : self._createIceAndDtlsTransports(mid, sdpMLineIndex);
1925
+
1926
+ var localCapabilities = RTCRtpSender.getCapabilities(kind);
1927
+ // filter RTX until additional stuff needed for RTX is implemented
1928
+ // in adapter.js
1929
+ localCapabilities.codecs = localCapabilities.codecs.filter(function (codec) {
1930
+ return codec.name !== 'rtx';
1931
+ });
1932
+ localCapabilities.codecs.forEach(function (codec) {
1933
+ // work around https://bugs.chromium.org/p/webrtc/issues/detail?id=6552
1934
+ // by adding level-asymmetry-allowed=1
1935
+ if (codec.name === 'H264' && codec.parameters['level-asymmetry-allowed'] === undefined) {
1936
+ codec.parameters['level-asymmetry-allowed'] = '1';
1937
+ }
1938
+ });
1939
+
1940
+ var rtpSender;
1941
+ var rtpReceiver;
1942
+
1943
+ // generate an ssrc now, to be used later in rtpSender.send
1944
+ var sendEncodingParameters = [{
1945
+ ssrc: (2 * sdpMLineIndex + 1) * 1001
1946
+ }];
1947
+ if (track) {
1948
+ rtpSender = new RTCRtpSender(track, transports.dtlsTransport);
1949
+ }
1950
+
1951
+ if (mline.wantReceive) {
1952
+ rtpReceiver = new RTCRtpReceiver(transports.dtlsTransport, kind);
1953
+ }
1954
+
1955
+ transceivers[sdpMLineIndex] = {
1956
+ iceGatherer: transports.iceGatherer,
1957
+ iceTransport: transports.iceTransport,
1958
+ dtlsTransport: transports.dtlsTransport,
1959
+ localCapabilities: localCapabilities,
1960
+ remoteCapabilities: null,
1961
+ rtpSender: rtpSender,
1962
+ rtpReceiver: rtpReceiver,
1963
+ kind: kind,
1964
+ mid: mid,
1965
+ sendEncodingParameters: sendEncodingParameters,
1966
+ recvEncodingParameters: null
1967
+ };
1968
+ });
1969
+ if (this.usingBundle) {
1970
+ sdp += 'a=group:BUNDLE ' + transceivers.map(function (t) {
1971
+ return t.mid;
1972
+ }).join(' ') + '\r\n';
1973
+ }
1974
+ tracks.forEach(function (mline, sdpMLineIndex) {
1975
+ var transceiver = transceivers[sdpMLineIndex];
1976
+ sdp += SDPUtils.writeMediaSection(transceiver, transceiver.localCapabilities, 'offer', self.localStreams[0]);
1977
+ });
1978
+
1979
+ this._pendingOffer = transceivers;
1980
+ var desc = new RTCSessionDescription({
1981
+ type: 'offer',
1982
+ sdp: sdp
1983
+ });
1984
+ if (arguments.length && typeof arguments[0] === 'function') {
1985
+ window.setTimeout(arguments[0], 0, desc);
1986
+ }
1987
+ return Promise.resolve(desc);
1988
+ };
1989
+
1990
+ window.RTCPeerConnection.prototype.createAnswer = function () {
1991
+ var self = this;
1992
+
1993
+ var sdp = SDPUtils.writeSessionBoilerplate();
1994
+ if (this.usingBundle) {
1995
+ sdp += 'a=group:BUNDLE ' + this.transceivers.map(function (t) {
1996
+ return t.mid;
1997
+ }).join(' ') + '\r\n';
1998
+ }
1999
+ this.transceivers.forEach(function (transceiver) {
2000
+ if (transceiver.isDatachannel) {
2001
+ sdp += 'm=application 0 DTLS/SCTP 5000\r\n' + 'c=IN IP4 0.0.0.0\r\n' + 'a=mid:' + transceiver.mid + '\r\n';
2002
+ return;
2003
+ }
2004
+ // Calculate intersection of capabilities.
2005
+ var commonCapabilities = self._getCommonCapabilities(transceiver.localCapabilities, transceiver.remoteCapabilities);
2006
+
2007
+ sdp += SDPUtils.writeMediaSection(transceiver, commonCapabilities, 'answer', self.localStreams[0]);
2008
+ });
2009
+
2010
+ var desc = new RTCSessionDescription({
2011
+ type: 'answer',
2012
+ sdp: sdp
2013
+ });
2014
+ if (arguments.length && typeof arguments[0] === 'function') {
2015
+ window.setTimeout(arguments[0], 0, desc);
2016
+ }
2017
+ return Promise.resolve(desc);
2018
+ };
2019
+
2020
+ window.RTCPeerConnection.prototype.addIceCandidate = function (candidate) {
2021
+ if (!candidate) {
2022
+ this.transceivers.forEach(function (transceiver) {
2023
+ transceiver.iceTransport.addRemoteCandidate({});
2024
+ });
2025
+ } else {
2026
+ var mLineIndex = candidate.sdpMLineIndex;
2027
+ if (candidate.sdpMid) {
2028
+ for (var i = 0; i < this.transceivers.length; i++) {
2029
+ if (this.transceivers[i].mid === candidate.sdpMid) {
2030
+ mLineIndex = i;
2031
+ break;
2032
+ }
2033
+ }
2034
+ }
2035
+ var transceiver = this.transceivers[mLineIndex];
2036
+ if (transceiver) {
2037
+ var cand = Object.keys(candidate.candidate).length > 0 ? SDPUtils.parseCandidate(candidate.candidate) : {};
2038
+ // Ignore Chrome's invalid candidates since Edge does not like them.
2039
+ if (cand.protocol === 'tcp' && (cand.port === 0 || cand.port === 9)) {
2040
+ return;
2041
+ }
2042
+ // Ignore RTCP candidates, we assume RTCP-MUX.
2043
+ if (cand.component !== '1') {
2044
+ return;
2045
+ }
2046
+ // A dirty hack to make samples work.
2047
+ if (cand.type === 'endOfCandidates') {
2048
+ cand = {};
2049
+ }
2050
+ transceiver.iceTransport.addRemoteCandidate(cand);
2051
+
2052
+ // update the remoteDescription.
2053
+ var sections = SDPUtils.splitSections(this.remoteDescription.sdp);
2054
+ sections[mLineIndex + 1] += (cand.type ? candidate.candidate.trim() : 'a=end-of-candidates') + '\r\n';
2055
+ this.remoteDescription.sdp = sections.join('');
2056
+ }
2057
+ }
2058
+ if (arguments.length > 1 && typeof arguments[1] === 'function') {
2059
+ window.setTimeout(arguments[1], 0);
2060
+ }
2061
+ return Promise.resolve();
2062
+ };
2063
+
2064
+ window.RTCPeerConnection.prototype.getStats = function () {
2065
+ var promises = [];
2066
+ this.transceivers.forEach(function (transceiver) {
2067
+ ['rtpSender', 'rtpReceiver', 'iceGatherer', 'iceTransport', 'dtlsTransport'].forEach(function (method) {
2068
+ if (transceiver[method]) {
2069
+ promises.push(transceiver[method].getStats());
2070
+ }
2071
+ });
2072
+ });
2073
+ var cb = arguments.length > 1 && typeof arguments[1] === 'function' && arguments[1];
2074
+ return new Promise(function (resolve) {
2075
+ // shim getStats with maplike support
2076
+ var results = new Map();
2077
+ Promise.all(promises).then(function (res) {
2078
+ res.forEach(function (result) {
2079
+ Object.keys(result).forEach(function (id) {
2080
+ results.set(id, result[id]);
2081
+ results[id] = result[id];
2082
+ });
2083
+ });
2084
+ if (cb) {
2085
+ window.setTimeout(cb, 0, results);
2086
+ }
2087
+ resolve(results);
2088
+ });
2089
+ });
2090
+ };
2091
+ }
2092
+ };
2093
+
2094
+ // Expose public methods.
2095
+ module.exports = {
2096
+ shimPeerConnection: edgeShim.shimPeerConnection,
2097
+ shimGetUserMedia: require('./getusermedia')
2098
+ };
2099
+ }, { "../utils": 10, "./getusermedia": 6, "sdp": 1 }], 6: [function (require, module, exports) {
2100
+ /*
2101
+ * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
2102
+ *
2103
+ * Use of this source code is governed by a BSD-style license
2104
+ * that can be found in the LICENSE file in the root of the source
2105
+ * tree.
2106
+ */
2107
+ /* eslint-env node */
2108
+ 'use strict';
2109
+
2110
+ // Expose public methods.
2111
+
2112
+ module.exports = function () {
2113
+ var shimError_ = function shimError_(e) {
2114
+ return {
2115
+ name: { PermissionDeniedError: 'NotAllowedError' }[e.name] || e.name,
2116
+ message: e.message,
2117
+ constraint: e.constraint,
2118
+ toString: function toString() {
2119
+ return this.name;
2120
+ }
2121
+ };
2122
+ };
2123
+
2124
+ // getUserMedia error shim.
2125
+ var origGetUserMedia = navigator.mediaDevices.getUserMedia.bind(navigator.mediaDevices);
2126
+ navigator.mediaDevices.getUserMedia = function (c) {
2127
+ return origGetUserMedia(c).catch(function (e) {
2128
+ return Promise.reject(shimError_(e));
2129
+ });
2130
+ };
2131
+ };
2132
+ }, {}], 7: [function (require, module, exports) {
2133
+ /*
2134
+ * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
2135
+ *
2136
+ * Use of this source code is governed by a BSD-style license
2137
+ * that can be found in the LICENSE file in the root of the source
2138
+ * tree.
2139
+ */
2140
+ /* eslint-env node */
2141
+ 'use strict';
2142
+
2143
+ var browserDetails = require('../utils').browserDetails;
2144
+
2145
+ var firefoxShim = {
2146
+ shimOnTrack: function shimOnTrack() {
2147
+ if ((typeof window === "undefined" ? "undefined" : _typeof(window)) === 'object' && window.RTCPeerConnection && !('ontrack' in window.RTCPeerConnection.prototype)) {
2148
+ Object.defineProperty(window.RTCPeerConnection.prototype, 'ontrack', {
2149
+ get: function get() {
2150
+ return this._ontrack;
2151
+ },
2152
+ set: function set(f) {
2153
+ if (this._ontrack) {
2154
+ this.removeEventListener('track', this._ontrack);
2155
+ this.removeEventListener('addstream', this._ontrackpoly);
2156
+ }
2157
+ this.addEventListener('track', this._ontrack = f);
2158
+ this.addEventListener('addstream', this._ontrackpoly = function (e) {
2159
+ e.stream.getTracks().forEach(function (track) {
2160
+ var event = new Event('track');
2161
+ event.track = track;
2162
+ event.receiver = { track: track };
2163
+ event.streams = [e.stream];
2164
+ this.dispatchEvent(event);
2165
+ }.bind(this));
2166
+ }.bind(this));
2167
+ }
2168
+ });
2169
+ }
2170
+ },
2171
+
2172
+ shimSourceObject: function shimSourceObject() {
2173
+ // Firefox has supported mozSrcObject since FF22, unprefixed in 42.
2174
+ if ((typeof window === "undefined" ? "undefined" : _typeof(window)) === 'object') {
2175
+ if (window.HTMLMediaElement && !('srcObject' in window.HTMLMediaElement.prototype)) {
2176
+ // Shim the srcObject property, once, when HTMLMediaElement is found.
2177
+ Object.defineProperty(window.HTMLMediaElement.prototype, 'srcObject', {
2178
+ get: function get() {
2179
+ return this.mozSrcObject;
2180
+ },
2181
+ set: function set(stream) {
2182
+ this.mozSrcObject = stream;
2183
+ }
2184
+ });
2185
+ }
2186
+ }
2187
+ },
2188
+
2189
+ shimPeerConnection: function shimPeerConnection() {
2190
+ if ((typeof window === "undefined" ? "undefined" : _typeof(window)) !== 'object' || !(window.RTCPeerConnection || window.mozRTCPeerConnection)) {
2191
+ return; // probably media.peerconnection.enabled=false in about:config
2192
+ }
2193
+ // The RTCPeerConnection object.
2194
+ if (!window.RTCPeerConnection) {
2195
+ window.RTCPeerConnection = function (pcConfig, pcConstraints) {
2196
+ if (browserDetails.version < 38) {
2197
+ // .urls is not supported in FF < 38.
2198
+ // create RTCIceServers with a single url.
2199
+ if (pcConfig && pcConfig.iceServers) {
2200
+ var newIceServers = [];
2201
+ for (var i = 0; i < pcConfig.iceServers.length; i++) {
2202
+ var server = pcConfig.iceServers[i];
2203
+ if (server.hasOwnProperty('urls')) {
2204
+ for (var j = 0; j < server.urls.length; j++) {
2205
+ var newServer = {
2206
+ url: server.urls[j]
2207
+ };
2208
+ if (server.urls[j].indexOf('turn') === 0) {
2209
+ newServer.username = server.username;
2210
+ newServer.credential = server.credential;
2211
+ }
2212
+ newIceServers.push(newServer);
2213
+ }
2214
+ } else {
2215
+ newIceServers.push(pcConfig.iceServers[i]);
2216
+ }
2217
+ }
2218
+ pcConfig.iceServers = newIceServers;
2219
+ }
2220
+ }
2221
+ return new mozRTCPeerConnection(pcConfig, pcConstraints);
2222
+ };
2223
+ window.RTCPeerConnection.prototype = mozRTCPeerConnection.prototype;
2224
+
2225
+ // wrap static methods. Currently just generateCertificate.
2226
+ if (mozRTCPeerConnection.generateCertificate) {
2227
+ Object.defineProperty(window.RTCPeerConnection, 'generateCertificate', {
2228
+ get: function get() {
2229
+ return mozRTCPeerConnection.generateCertificate;
2230
+ }
2231
+ });
2232
+ }
2233
+
2234
+ window.RTCSessionDescription = mozRTCSessionDescription;
2235
+ window.RTCIceCandidate = mozRTCIceCandidate;
2236
+ }
2237
+
2238
+ // shim away need for obsolete RTCIceCandidate/RTCSessionDescription.
2239
+ ['setLocalDescription', 'setRemoteDescription', 'addIceCandidate'].forEach(function (method) {
2240
+ var nativeMethod = RTCPeerConnection.prototype[method];
2241
+ RTCPeerConnection.prototype[method] = function () {
2242
+ arguments[0] = new (method === 'addIceCandidate' ? RTCIceCandidate : RTCSessionDescription)(arguments[0]);
2243
+ return nativeMethod.apply(this, arguments);
2244
+ };
2245
+ });
2246
+
2247
+ // support for addIceCandidate(null or undefined)
2248
+ var nativeAddIceCandidate = RTCPeerConnection.prototype.addIceCandidate;
2249
+ RTCPeerConnection.prototype.addIceCandidate = function () {
2250
+ if (!arguments[0]) {
2251
+ if (arguments[1]) {
2252
+ arguments[1].apply(null);
2253
+ }
2254
+ return Promise.resolve();
2255
+ }
2256
+ return nativeAddIceCandidate.apply(this, arguments);
2257
+ };
2258
+
2259
+ if (browserDetails.version < 48) {
2260
+ // shim getStats with maplike support
2261
+ var makeMapStats = function makeMapStats(stats) {
2262
+ var map = new Map();
2263
+ Object.keys(stats).forEach(function (key) {
2264
+ map.set(key, stats[key]);
2265
+ map[key] = stats[key];
2266
+ });
2267
+ return map;
2268
+ };
2269
+
2270
+ var nativeGetStats = RTCPeerConnection.prototype.getStats;
2271
+ RTCPeerConnection.prototype.getStats = function (selector, onSucc, onErr) {
2272
+ return nativeGetStats.apply(this, [selector || null]).then(function (stats) {
2273
+ return makeMapStats(stats);
2274
+ }).then(onSucc, onErr);
2275
+ };
2276
+ }
2277
+ }
2278
+ };
2279
+
2280
+ // Expose public methods.
2281
+ module.exports = {
2282
+ shimOnTrack: firefoxShim.shimOnTrack,
2283
+ shimSourceObject: firefoxShim.shimSourceObject,
2284
+ shimPeerConnection: firefoxShim.shimPeerConnection,
2285
+ shimGetUserMedia: require('./getusermedia')
2286
+ };
2287
+ }, { "../utils": 10, "./getusermedia": 8 }], 8: [function (require, module, exports) {
2288
+ /*
2289
+ * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
2290
+ *
2291
+ * Use of this source code is governed by a BSD-style license
2292
+ * that can be found in the LICENSE file in the root of the source
2293
+ * tree.
2294
+ */
2295
+ /* eslint-env node */
2296
+ 'use strict';
2297
+
2298
+ var logging = require('../utils').log;
2299
+ var browserDetails = require('../utils').browserDetails;
2300
+
2301
+ // Expose public methods.
2302
+ module.exports = function () {
2303
+ var shimError_ = function shimError_(e) {
2304
+ return {
2305
+ name: {
2306
+ SecurityError: 'NotAllowedError',
2307
+ PermissionDeniedError: 'NotAllowedError'
2308
+ }[e.name] || e.name,
2309
+ message: {
2310
+ 'The operation is insecure.': 'The request is not allowed by the ' + 'user agent or the platform in the current context.'
2311
+ }[e.message] || e.message,
2312
+ constraint: e.constraint,
2313
+ toString: function toString() {
2314
+ return this.name + (this.message && ': ') + this.message;
2315
+ }
2316
+ };
2317
+ };
2318
+
2319
+ // getUserMedia constraints shim.
2320
+ var getUserMedia_ = function getUserMedia_(constraints, onSuccess, onError) {
2321
+ var constraintsToFF37_ = function constraintsToFF37_(c) {
2322
+ if ((typeof c === "undefined" ? "undefined" : _typeof(c)) !== 'object' || c.require) {
2323
+ return c;
2324
+ }
2325
+ var require = [];
2326
+ Object.keys(c).forEach(function (key) {
2327
+ if (key === 'require' || key === 'advanced' || key === 'mediaSource') {
2328
+ return;
2329
+ }
2330
+ var r = c[key] = _typeof(c[key]) === 'object' ? c[key] : { ideal: c[key] };
2331
+ if (r.min !== undefined || r.max !== undefined || r.exact !== undefined) {
2332
+ require.push(key);
2333
+ }
2334
+ if (r.exact !== undefined) {
2335
+ if (typeof r.exact === 'number') {
2336
+ r.min = r.max = r.exact;
2337
+ } else {
2338
+ c[key] = r.exact;
2339
+ }
2340
+ delete r.exact;
2341
+ }
2342
+ if (r.ideal !== undefined) {
2343
+ c.advanced = c.advanced || [];
2344
+ var oc = {};
2345
+ if (typeof r.ideal === 'number') {
2346
+ oc[key] = { min: r.ideal, max: r.ideal };
2347
+ } else {
2348
+ oc[key] = r.ideal;
2349
+ }
2350
+ c.advanced.push(oc);
2351
+ delete r.ideal;
2352
+ if (!Object.keys(r).length) {
2353
+ delete c[key];
2354
+ }
2355
+ }
2356
+ });
2357
+ if (require.length) {
2358
+ c.require = require;
2359
+ }
2360
+ return c;
2361
+ };
2362
+ constraints = JSON.parse(JSON.stringify(constraints));
2363
+ if (browserDetails.version < 38) {
2364
+ logging('spec: ' + JSON.stringify(constraints));
2365
+ if (constraints.audio) {
2366
+ constraints.audio = constraintsToFF37_(constraints.audio);
2367
+ }
2368
+ if (constraints.video) {
2369
+ constraints.video = constraintsToFF37_(constraints.video);
2370
+ }
2371
+ logging('ff37: ' + JSON.stringify(constraints));
2372
+ }
2373
+ return navigator.mozGetUserMedia(constraints, onSuccess, function (e) {
2374
+ onError(shimError_(e));
2375
+ });
2376
+ };
2377
+
2378
+ // Returns the result of getUserMedia as a Promise.
2379
+ var getUserMediaPromise_ = function getUserMediaPromise_(constraints) {
2380
+ return new Promise(function (resolve, reject) {
2381
+ getUserMedia_(constraints, resolve, reject);
2382
+ });
2383
+ };
2384
+
2385
+ // Shim for mediaDevices on older versions.
2386
+ if (!navigator.mediaDevices) {
2387
+ navigator.mediaDevices = { getUserMedia: getUserMediaPromise_,
2388
+ addEventListener: function addEventListener() {},
2389
+ removeEventListener: function removeEventListener() {}
2390
+ };
2391
+ }
2392
+ navigator.mediaDevices.enumerateDevices = navigator.mediaDevices.enumerateDevices || function () {
2393
+ return new Promise(function (resolve) {
2394
+ var infos = [{ kind: 'audioinput', deviceId: 'default', label: '', groupId: '' }, { kind: 'videoinput', deviceId: 'default', label: '', groupId: '' }];
2395
+ resolve(infos);
2396
+ });
2397
+ };
2398
+
2399
+ if (browserDetails.version < 41) {
2400
+ // Work around http://bugzil.la/1169665
2401
+ var orgEnumerateDevices = navigator.mediaDevices.enumerateDevices.bind(navigator.mediaDevices);
2402
+ navigator.mediaDevices.enumerateDevices = function () {
2403
+ return orgEnumerateDevices().then(undefined, function (e) {
2404
+ if (e.name === 'NotFoundError') {
2405
+ return [];
2406
+ }
2407
+ throw e;
2408
+ });
2409
+ };
2410
+ }
2411
+ if (browserDetails.version < 49) {
2412
+ var origGetUserMedia = navigator.mediaDevices.getUserMedia.bind(navigator.mediaDevices);
2413
+ navigator.mediaDevices.getUserMedia = function (c) {
2414
+ return origGetUserMedia(c).then(function (stream) {
2415
+ // Work around https://bugzil.la/802326
2416
+ if (c.audio && !stream.getAudioTracks().length || c.video && !stream.getVideoTracks().length) {
2417
+ stream.getTracks().forEach(function (track) {
2418
+ track.stop();
2419
+ });
2420
+ throw new DOMException('The object can not be found here.', 'NotFoundError');
2421
+ }
2422
+ return stream;
2423
+ }, function (e) {
2424
+ return Promise.reject(shimError_(e));
2425
+ });
2426
+ };
2427
+ }
2428
+ navigator.getUserMedia = function (constraints, onSuccess, onError) {
2429
+ if (browserDetails.version < 44) {
2430
+ return getUserMedia_(constraints, onSuccess, onError);
2431
+ }
2432
+ // Replace Firefox 44+'s deprecation warning with unprefixed version.
2433
+ console.warn('navigator.getUserMedia has been replaced by ' + 'navigator.mediaDevices.getUserMedia');
2434
+ navigator.mediaDevices.getUserMedia(constraints).then(onSuccess, onError);
2435
+ };
2436
+ };
2437
+ }, { "../utils": 10 }], 9: [function (require, module, exports) {
2438
+ /*
2439
+ * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
2440
+ *
2441
+ * Use of this source code is governed by a BSD-style license
2442
+ * that can be found in the LICENSE file in the root of the source
2443
+ * tree.
2444
+ */
2445
+ 'use strict';
2446
+
2447
+ var safariShim = {
2448
+ // TODO: DrAlex, should be here, double check against LayoutTests
2449
+ // shimOnTrack: function() { },
2450
+
2451
+ // TODO: once the back-end for the mac port is done, add.
2452
+ // TODO: check for webkitGTK+
2453
+ // shimPeerConnection: function() { },
2454
+
2455
+ shimGetUserMedia: function shimGetUserMedia() {
2456
+ navigator.getUserMedia = navigator.webkitGetUserMedia;
2457
+ }
2458
+ };
2459
+
2460
+ // Expose public methods.
2461
+ module.exports = {
2462
+ shimGetUserMedia: safariShim.shimGetUserMedia
2463
+ // TODO
2464
+ // shimOnTrack: safariShim.shimOnTrack,
2465
+ // shimPeerConnection: safariShim.shimPeerConnection
2466
+ };
2467
+ }, {}], 10: [function (require, module, exports) {
2468
+ /*
2469
+ * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
2470
+ *
2471
+ * Use of this source code is governed by a BSD-style license
2472
+ * that can be found in the LICENSE file in the root of the source
2473
+ * tree.
2474
+ */
2475
+ /* eslint-env node */
2476
+ 'use strict';
2477
+
2478
+ var logDisabled_ = true;
2479
+
2480
+ // Utility methods.
2481
+ var utils = {
2482
+ disableLog: function disableLog(bool) {
2483
+ if (typeof bool !== 'boolean') {
2484
+ return new Error('Argument type: ' + (typeof bool === "undefined" ? "undefined" : _typeof(bool)) + '. Please use a boolean.');
2485
+ }
2486
+ logDisabled_ = bool;
2487
+ return bool ? 'adapter.js logging disabled' : 'adapter.js logging enabled';
2488
+ },
2489
+
2490
+ log: function log() {
2491
+ if ((typeof window === "undefined" ? "undefined" : _typeof(window)) === 'object') {
2492
+ if (logDisabled_) {
2493
+ return;
2494
+ }
2495
+ if (typeof console !== 'undefined' && typeof console.log === 'function') {
2496
+ console.log.apply(console, arguments);
2497
+ }
2498
+ }
2499
+ },
2500
+
2501
+ /**
2502
+ * Extract browser version out of the provided user agent string.
2503
+ *
2504
+ * @param {!string} uastring userAgent string.
2505
+ * @param {!string} expr Regular expression used as match criteria.
2506
+ * @param {!number} pos position in the version string to be returned.
2507
+ * @return {!number} browser version.
2508
+ */
2509
+ extractVersion: function extractVersion(uastring, expr, pos) {
2510
+ var match = uastring.match(expr);
2511
+ return match && match.length >= pos && parseInt(match[pos], 10);
2512
+ },
2513
+
2514
+ /**
2515
+ * Browser detector.
2516
+ *
2517
+ * @return {object} result containing browser and version
2518
+ * properties.
2519
+ */
2520
+ detectBrowser: function detectBrowser() {
2521
+ // Returned result object.
2522
+ var result = {};
2523
+ result.browser = null;
2524
+ result.version = null;
2525
+
2526
+ // Fail early if it's not a browser
2527
+ if (typeof window === 'undefined' || !window.navigator) {
2528
+ result.browser = 'Not a browser.';
2529
+ return result;
2530
+ }
2531
+
2532
+ // Firefox.
2533
+ if (navigator.mozGetUserMedia) {
2534
+ result.browser = 'firefox';
2535
+ result.version = this.extractVersion(navigator.userAgent, /Firefox\/([0-9]+)\./, 1);
2536
+
2537
+ // all webkit-based browsers
2538
+ } else if (navigator.webkitGetUserMedia) {
2539
+ // Chrome, Chromium, Webview, Opera, all use the chrome shim for now
2540
+ if (window.webkitRTCPeerConnection) {
2541
+ result.browser = 'chrome';
2542
+ result.version = this.extractVersion(navigator.userAgent, /Chrom(e|ium)\/([0-9]+)\./, 2);
2543
+
2544
+ // Safari or unknown webkit-based
2545
+ // for the time being Safari has support for MediaStreams but not webRTC
2546
+ } else {
2547
+ // Safari UA substrings of interest for reference:
2548
+ // - webkit version: AppleWebKit/602.1.25 (also used in Op,Cr)
2549
+ // - safari UI version: Version/9.0.3 (unique to Safari)
2550
+ // - safari UI webkit version: Safari/601.4.4 (also used in Op,Cr)
2551
+ //
2552
+ // if the webkit version and safari UI webkit versions are equals,
2553
+ // ... this is a stable version.
2554
+ //
2555
+ // only the internal webkit version is important today to know if
2556
+ // media streams are supported
2557
+ //
2558
+ if (navigator.userAgent.match(/Version\/(\d+).(\d+)/)) {
2559
+ result.browser = 'safari';
2560
+ result.version = this.extractVersion(navigator.userAgent, /AppleWebKit\/([0-9]+)\./, 1);
2561
+
2562
+ // unknown webkit-based browser
2563
+ } else {
2564
+ result.browser = 'Unsupported webkit-based browser ' + 'with GUM support but no WebRTC support.';
2565
+ return result;
2566
+ }
2567
+ }
2568
+
2569
+ // Edge.
2570
+ } else if (navigator.mediaDevices && navigator.userAgent.match(/Edge\/(\d+).(\d+)$/)) {
2571
+ result.browser = 'edge';
2572
+ result.version = this.extractVersion(navigator.userAgent, /Edge\/(\d+).(\d+)$/, 2);
2573
+
2574
+ // Default fallthrough: not supported.
2575
+ } else {
2576
+ result.browser = 'Not a supported browser.';
2577
+ return result;
2578
+ }
2579
+
2580
+ return result;
2581
+ }
2582
+ };
2583
+
2584
+ // Export.
2585
+ module.exports = {
2586
+ log: utils.log,
2587
+ disableLog: utils.disableLog,
2588
+ browserDetails: utils.detectBrowser(),
2589
+ extractVersion: utils.extractVersion
2590
+ };
2591
+ }, {}] }, {}, [2])(2);
2592
+ });