talking_stick 1.0.0 → 2.0.0

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