GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — master ( 0ca11f...288e4c )
by Jesus
01:43
created

n.addStream   A

Complexity

Conditions 2
Paths 8

Size

Total Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 2
c 1
b 0
f 1
nc 8
nop 1
dl 0
loc 13
rs 9.4285
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){
0 ignored issues
show
Unused Code introduced by
The variable define seems to be never used. Consider removing it.
Loading history...
Bug introduced by
The variable define seems to be never declared. If this is a global, consider adding a /** global: define */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
Comprehensibility introduced by
Usage of the sequence operator is discouraged, since it may lead to obfuscated code.

The sequence or comma operator allows the inclusion of multiple expressions where only is permitted. The result of the sequence is the value of the last expression.

This operator is most often used in for statements.

Used in another places it can make code hard to read, especially when people do not realize it even exists as a seperate operator.

This check looks for usage of the sequence operator in locations where it is not necessary and could be replaced by a series of expressions or statements.

var a,b,c;

a = 1, b = 1,  c= 3;

could just as well be written as:

var a,b,c;

a = 1;
b = 1;
c = 3;

To learn more about the sequence operator, please refer to the MDN.

Loading history...
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
Bug introduced by
The variable self seems to be never declared. If this is a global, consider adding a /** global: self */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
Unused Code introduced by
The variable module seems to be never used. Consider removing it.
Loading history...
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: parseInt(parts[1], 10),
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: // extension handling, in particular ufrag
73
        candidate[parts[i]] = parts[i + 1];
74
        break;
75
    }
76
  }
77
  return candidate;
78
};
79
80
// Translates a candidate object into SDP candidate attribute.
81
SDPUtils.writeCandidate = function(candidate) {
82
  var sdp = [];
83
  sdp.push(candidate.foundation);
84
  sdp.push(candidate.component);
85
  sdp.push(candidate.protocol.toUpperCase());
86
  sdp.push(candidate.priority);
87
  sdp.push(candidate.ip);
88
  sdp.push(candidate.port);
89
90
  var type = candidate.type;
91
  sdp.push('typ');
92
  sdp.push(type);
93
  if (type !== 'host' && candidate.relatedAddress &&
94
      candidate.relatedPort) {
95
    sdp.push('raddr');
96
    sdp.push(candidate.relatedAddress); // was: relAddr
97
    sdp.push('rport');
98
    sdp.push(candidate.relatedPort); // was: relPort
99
  }
100
  if (candidate.tcpType && candidate.protocol.toLowerCase() === 'tcp') {
101
    sdp.push('tcptype');
102
    sdp.push(candidate.tcpType);
103
  }
104
  if (candidate.ufrag) {
105
    sdp.push('ufrag');
106
    sdp.push(candidate.ufrag);
107
  }
108
  return 'candidate:' + sdp.join(' ');
109
};
110
111
// Parses an ice-options line, returns an array of option tags.
112
// a=ice-options:foo bar
113
SDPUtils.parseIceOptions = function(line) {
114
  return line.substr(14).split(' ');
115
}
116
117
// Parses an rtpmap line, returns RTCRtpCoddecParameters. Sample input:
118
// a=rtpmap:111 opus/48000/2
119
SDPUtils.parseRtpMap = function(line) {
120
  var parts = line.substr(9).split(' ');
121
  var parsed = {
122
    payloadType: parseInt(parts.shift(), 10) // was: id
123
  };
124
125
  parts = parts[0].split('/');
126
127
  parsed.name = parts[0];
128
  parsed.clockRate = parseInt(parts[1], 10); // was: clockrate
129
  // was: channels
130
  parsed.numChannels = parts.length === 3 ? parseInt(parts[2], 10) : 1;
131
  return parsed;
132
};
133
134
// Generate an a=rtpmap line from RTCRtpCodecCapability or
135
// RTCRtpCodecParameters.
136
SDPUtils.writeRtpMap = function(codec) {
137
  var pt = codec.payloadType;
138
  if (codec.preferredPayloadType !== undefined) {
139
    pt = codec.preferredPayloadType;
140
  }
141
  return 'a=rtpmap:' + pt + ' ' + codec.name + '/' + codec.clockRate +
142
      (codec.numChannels !== 1 ? '/' + codec.numChannels : '') + '\r\n';
143
};
144
145
// Parses an a=extmap line (headerextension from RFC 5285). Sample input:
146
// a=extmap:2 urn:ietf:params:rtp-hdrext:toffset
147
// a=extmap:2/sendonly urn:ietf:params:rtp-hdrext:toffset
148
SDPUtils.parseExtmap = function(line) {
149
  var parts = line.substr(9).split(' ');
150
  return {
151
    id: parseInt(parts[0], 10),
152
    direction: parts[0].indexOf('/') > 0 ? parts[0].split('/')[1] : 'sendrecv',
153
    uri: parts[1]
154
  };
155
};
156
157
// Generates a=extmap line from RTCRtpHeaderExtensionParameters or
158
// RTCRtpHeaderExtension.
159
SDPUtils.writeExtmap = function(headerExtension) {
160
  return 'a=extmap:' + (headerExtension.id || headerExtension.preferredId) +
161
      (headerExtension.direction && headerExtension.direction !== 'sendrecv'
162
          ? '/' + headerExtension.direction
163
          : '') +
164
      ' ' + headerExtension.uri + '\r\n';
165
};
166
167
// Parses an ftmp line, returns dictionary. Sample input:
168
// a=fmtp:96 vbr=on;cng=on
169
// Also deals with vbr=on; cng=on
170
SDPUtils.parseFmtp = function(line) {
171
  var parsed = {};
172
  var kv;
173
  var parts = line.substr(line.indexOf(' ') + 1).split(';');
174
  for (var j = 0; j < parts.length; j++) {
175
    kv = parts[j].trim().split('=');
176
    parsed[kv[0].trim()] = kv[1];
177
  }
178
  return parsed;
179
};
180
181
// Generates an a=ftmp line from RTCRtpCodecCapability or RTCRtpCodecParameters.
182
SDPUtils.writeFmtp = function(codec) {
183
  var line = '';
184
  var pt = codec.payloadType;
185
  if (codec.preferredPayloadType !== undefined) {
186
    pt = codec.preferredPayloadType;
187
  }
188
  if (codec.parameters && Object.keys(codec.parameters).length) {
189
    var params = [];
190
    Object.keys(codec.parameters).forEach(function(param) {
191
      params.push(param + '=' + codec.parameters[param]);
192
    });
193
    line += 'a=fmtp:' + pt + ' ' + params.join(';') + '\r\n';
194
  }
195
  return line;
196
};
197
198
// Parses an rtcp-fb line, returns RTCPRtcpFeedback object. Sample input:
199
// a=rtcp-fb:98 nack rpsi
200
SDPUtils.parseRtcpFb = function(line) {
201
  var parts = line.substr(line.indexOf(' ') + 1).split(' ');
202
  return {
203
    type: parts.shift(),
204
    parameter: parts.join(' ')
205
  };
206
};
207
// Generate a=rtcp-fb lines from RTCRtpCodecCapability or RTCRtpCodecParameters.
208
SDPUtils.writeRtcpFb = function(codec) {
209
  var lines = '';
210
  var pt = codec.payloadType;
211
  if (codec.preferredPayloadType !== undefined) {
212
    pt = codec.preferredPayloadType;
213
  }
214
  if (codec.rtcpFeedback && codec.rtcpFeedback.length) {
215
    // FIXME: special handling for trr-int?
216
    codec.rtcpFeedback.forEach(function(fb) {
217
      lines += 'a=rtcp-fb:' + pt + ' ' + fb.type +
218
      (fb.parameter && fb.parameter.length ? ' ' + fb.parameter : '') +
219
          '\r\n';
220
    });
221
  }
222
  return lines;
223
};
224
225
// Parses an RFC 5576 ssrc media attribute. Sample input:
226
// a=ssrc:3735928559 cname:something
227
SDPUtils.parseSsrcMedia = function(line) {
228
  var sp = line.indexOf(' ');
229
  var parts = {
230
    ssrc: parseInt(line.substr(7, sp - 7), 10)
231
  };
232
  var colon = line.indexOf(':', sp);
233
  if (colon > -1) {
234
    parts.attribute = line.substr(sp + 1, colon - sp - 1);
235
    parts.value = line.substr(colon + 1);
236
  } else {
237
    parts.attribute = line.substr(sp + 1);
238
  }
239
  return parts;
240
};
241
242
// Extracts the MID (RFC 5888) from a media section.
243
// returns the MID or undefined if no mid line was found.
244
SDPUtils.getMid = function(mediaSection) {
245
  var mid = SDPUtils.matchPrefix(mediaSection, 'a=mid:')[0];
246
  if (mid) {
0 ignored issues
show
Complexity Best Practice introduced by
There is no return statement if mid is false. Are you sure this is correct? If so, consider adding return; explicitly.

This check looks for functions where a return statement is found in some execution paths, but not in all.

Consider this little piece of code

function isBig(a) {
    if (a > 5000) {
        return "yes";
    }
}

console.log(isBig(5001)); //returns yes
console.log(isBig(42)); //returns undefined

The function isBig will only return a specific value when its parameter is bigger than 5000. In any other case, it will implicitly return undefined.

This behaviour may not be what you had intended. In any case, you can add a return undefined to the other execution path to make the return value explicit.

Loading history...
247
    return mid.substr(6);
248
  }
249
}
250
251
SDPUtils.parseFingerprint = function(line) {
252
  var parts = line.substr(14).split(' ');
253
  return {
254
    algorithm: parts[0].toLowerCase(), // algorithm is case-sensitive in Edge.
255
    value: parts[1]
256
  };
257
};
258
259
// Extracts DTLS parameters from SDP media section or sessionpart.
260
// FIXME: for consistency with other functions this should only
261
//   get the fingerprint line as input. See also getIceParameters.
262
SDPUtils.getDtlsParameters = function(mediaSection, sessionpart) {
263
  var lines = SDPUtils.matchPrefix(mediaSection + sessionpart,
264
      'a=fingerprint:');
265
  // Note: a=setup line is ignored since we use the 'auto' role.
266
  // Note2: 'algorithm' is not case sensitive except in Edge.
267
  return {
268
    role: 'auto',
269
    fingerprints: lines.map(SDPUtils.parseFingerprint)
270
  };
271
};
272
273
// Serializes DTLS parameters to SDP.
274
SDPUtils.writeDtlsParameters = function(params, setupType) {
275
  var sdp = 'a=setup:' + setupType + '\r\n';
276
  params.fingerprints.forEach(function(fp) {
277
    sdp += 'a=fingerprint:' + fp.algorithm + ' ' + fp.value + '\r\n';
278
  });
279
  return sdp;
280
};
281
// Parses ICE information from SDP media section or sessionpart.
282
// FIXME: for consistency with other functions this should only
283
//   get the ice-ufrag and ice-pwd lines as input.
284
SDPUtils.getIceParameters = function(mediaSection, sessionpart) {
285
  var lines = SDPUtils.splitLines(mediaSection);
286
  // Search in session part, too.
287
  lines = lines.concat(SDPUtils.splitLines(sessionpart));
288
  var iceParameters = {
289
    usernameFragment: lines.filter(function(line) {
290
      return line.indexOf('a=ice-ufrag:') === 0;
291
    })[0].substr(12),
292
    password: lines.filter(function(line) {
293
      return line.indexOf('a=ice-pwd:') === 0;
294
    })[0].substr(10)
295
  };
296
  return iceParameters;
297
};
298
299
// Serializes ICE parameters to SDP.
300
SDPUtils.writeIceParameters = function(params) {
301
  return 'a=ice-ufrag:' + params.usernameFragment + '\r\n' +
302
      'a=ice-pwd:' + params.password + '\r\n';
303
};
304
305
// Parses the SDP media section and returns RTCRtpParameters.
306
SDPUtils.parseRtpParameters = function(mediaSection) {
307
  var description = {
308
    codecs: [],
309
    headerExtensions: [],
310
    fecMechanisms: [],
311
    rtcp: []
312
  };
313
  var lines = SDPUtils.splitLines(mediaSection);
314
  var mline = lines[0].split(' ');
315
  for (var i = 3; i < mline.length; i++) { // find all codecs from mline[3..]
316
    var pt = mline[i];
317
    var rtpmapline = SDPUtils.matchPrefix(
318
        mediaSection, 'a=rtpmap:' + pt + ' ')[0];
319
    if (rtpmapline) {
320
      var codec = SDPUtils.parseRtpMap(rtpmapline);
321
      var fmtps = SDPUtils.matchPrefix(
322
          mediaSection, 'a=fmtp:' + pt + ' ');
323
      // Only the first a=fmtp:<pt> is considered.
324
      codec.parameters = fmtps.length ? SDPUtils.parseFmtp(fmtps[0]) : {};
325
      codec.rtcpFeedback = SDPUtils.matchPrefix(
326
          mediaSection, 'a=rtcp-fb:' + pt + ' ')
327
        .map(SDPUtils.parseRtcpFb);
328
      description.codecs.push(codec);
329
      // parse FEC mechanisms from rtpmap lines.
330
      switch (codec.name.toUpperCase()) {
331
        case 'RED':
332
        case 'ULPFEC':
333
          description.fecMechanisms.push(codec.name.toUpperCase());
334
          break;
335
        default: // only RED and ULPFEC are recognized as FEC mechanisms.
336
          break;
337
      }
338
    }
339
  }
340
  SDPUtils.matchPrefix(mediaSection, 'a=extmap:').forEach(function(line) {
341
    description.headerExtensions.push(SDPUtils.parseExtmap(line));
342
  });
343
  // FIXME: parse rtcp.
344
  return description;
345
};
346
347
// Generates parts of the SDP media section describing the capabilities /
348
// parameters.
349
SDPUtils.writeRtpDescription = function(kind, caps) {
350
  var sdp = '';
351
352
  // Build the mline.
353
  sdp += 'm=' + kind + ' ';
354
  sdp += caps.codecs.length > 0 ? '9' : '0'; // reject if no codecs.
355
  sdp += ' UDP/TLS/RTP/SAVPF ';
356
  sdp += caps.codecs.map(function(codec) {
357
    if (codec.preferredPayloadType !== undefined) {
358
      return codec.preferredPayloadType;
359
    }
360
    return codec.payloadType;
361
  }).join(' ') + '\r\n';
362
363
  sdp += 'c=IN IP4 0.0.0.0\r\n';
364
  sdp += 'a=rtcp:9 IN IP4 0.0.0.0\r\n';
365
366
  // Add a=rtpmap lines for each codec. Also fmtp and rtcp-fb.
367
  caps.codecs.forEach(function(codec) {
368
    sdp += SDPUtils.writeRtpMap(codec);
369
    sdp += SDPUtils.writeFmtp(codec);
370
    sdp += SDPUtils.writeRtcpFb(codec);
371
  });
372
  var maxptime = 0;
373
  caps.codecs.forEach(function(codec) {
374
    if (codec.maxptime > maxptime) {
375
      maxptime = codec.maxptime;
376
    }
377
  });
378
  if (maxptime > 0) {
379
    sdp += 'a=maxptime:' + maxptime + '\r\n';
380
  }
381
  sdp += 'a=rtcp-mux\r\n';
382
383
  caps.headerExtensions.forEach(function(extension) {
384
    sdp += SDPUtils.writeExtmap(extension);
385
  });
386
  // FIXME: write fecMechanisms.
387
  return sdp;
388
};
389
390
// Parses the SDP media section and returns an array of
391
// RTCRtpEncodingParameters.
392
SDPUtils.parseRtpEncodingParameters = function(mediaSection) {
393
  var encodingParameters = [];
394
  var description = SDPUtils.parseRtpParameters(mediaSection);
395
  var hasRed = description.fecMechanisms.indexOf('RED') !== -1;
396
  var hasUlpfec = description.fecMechanisms.indexOf('ULPFEC') !== -1;
397
398
  // filter a=ssrc:... cname:, ignore PlanB-msid
399
  var ssrcs = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:')
400
  .map(function(line) {
401
    return SDPUtils.parseSsrcMedia(line);
402
  })
403
  .filter(function(parts) {
404
    return parts.attribute === 'cname';
405
  });
406
  var primarySsrc = ssrcs.length > 0 && ssrcs[0].ssrc;
407
  var secondarySsrc;
408
409
  var flows = SDPUtils.matchPrefix(mediaSection, 'a=ssrc-group:FID')
410
  .map(function(line) {
411
    var parts = line.split(' ');
412
    parts.shift();
413
    return parts.map(function(part) {
414
      return parseInt(part, 10);
415
    });
416
  });
417
  if (flows.length > 0 && flows[0].length > 1 && flows[0][0] === primarySsrc) {
418
    secondarySsrc = flows[0][1];
419
  }
420
421
  description.codecs.forEach(function(codec) {
422
    if (codec.name.toUpperCase() === 'RTX' && codec.parameters.apt) {
423
      var encParam = {
424
        ssrc: primarySsrc,
425
        codecPayloadType: parseInt(codec.parameters.apt, 10),
426
        rtx: {
427
          ssrc: secondarySsrc
0 ignored issues
show
Bug introduced by
The variable secondarySsrc does not seem to be initialized in case flows.length > 0 && flow...ows.0.0 === primarySsrc on line 417 is false. Are you sure this can never be the case?
Loading history...
428
        }
429
      };
430
      encodingParameters.push(encParam);
431
      if (hasRed) {
432
        encParam = JSON.parse(JSON.stringify(encParam));
433
        encParam.fec = {
434
          ssrc: secondarySsrc,
435
          mechanism: hasUlpfec ? 'red+ulpfec' : 'red'
436
        };
437
        encodingParameters.push(encParam);
438
      }
439
    }
440
  });
441
  if (encodingParameters.length === 0 && primarySsrc) {
442
    encodingParameters.push({
443
      ssrc: primarySsrc
444
    });
445
  }
446
447
  // we support both b=AS and b=TIAS but interpret AS as TIAS.
448
  var bandwidth = SDPUtils.matchPrefix(mediaSection, 'b=');
449
  if (bandwidth.length) {
450
    if (bandwidth[0].indexOf('b=TIAS:') === 0) {
451
      bandwidth = parseInt(bandwidth[0].substr(7), 10);
452
    } else if (bandwidth[0].indexOf('b=AS:') === 0) {
453
      // use formula from JSEP to convert b=AS to TIAS value.
454
      bandwidth = parseInt(bandwidth[0].substr(5), 10) * 1000 * 0.95
455
          - (50 * 40 * 8);
456
    } else {
457
      bandwidth = undefined;
458
    }
459
    encodingParameters.forEach(function(params) {
460
      params.maxBitrate = bandwidth;
461
    });
462
  }
463
  return encodingParameters;
464
};
465
466
// parses http://draft.ortc.org/#rtcrtcpparameters*
467
SDPUtils.parseRtcpParameters = function(mediaSection) {
468
  var rtcpParameters = {};
469
470
  var cname;
471
  // Gets the first SSRC. Note that with RTX there might be multiple
472
  // SSRCs.
473
  var remoteSsrc = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:')
474
      .map(function(line) {
475
        return SDPUtils.parseSsrcMedia(line);
476
      })
477
      .filter(function(obj) {
478
        return obj.attribute === 'cname';
479
      })[0];
480
  if (remoteSsrc) {
481
    rtcpParameters.cname = remoteSsrc.value;
482
    rtcpParameters.ssrc = remoteSsrc.ssrc;
483
  }
484
485
  // Edge uses the compound attribute instead of reducedSize
486
  // compound is !reducedSize
487
  var rsize = SDPUtils.matchPrefix(mediaSection, 'a=rtcp-rsize');
488
  rtcpParameters.reducedSize = rsize.length > 0;
489
  rtcpParameters.compound = rsize.length === 0;
490
491
  // parses the rtcp-mux attrіbute.
492
  // Note that Edge does not support unmuxed RTCP.
493
  var mux = SDPUtils.matchPrefix(mediaSection, 'a=rtcp-mux');
494
  rtcpParameters.mux = mux.length > 0;
495
496
  return rtcpParameters;
497
};
498
499
// parses either a=msid: or a=ssrc:... msid lines and returns
500
// the id of the MediaStream and MediaStreamTrack.
501
SDPUtils.parseMsid = function(mediaSection) {
502
  var parts;
503
  var spec = SDPUtils.matchPrefix(mediaSection, 'a=msid:');
504
  if (spec.length === 1) {
505
    parts = spec[0].substr(7).split(' ');
506
    return {stream: parts[0], track: parts[1]};
507
  }
508
  var planB = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:')
509
  .map(function(line) {
510
    return SDPUtils.parseSsrcMedia(line);
511
  })
512
  .filter(function(parts) {
513
    return parts.attribute === 'msid';
514
  });
515
  if (planB.length > 0) {
0 ignored issues
show
Complexity Best Practice introduced by
There is no return statement if planB.length > 0 is false. Are you sure this is correct? If so, consider adding return; explicitly.

This check looks for functions where a return statement is found in some execution paths, but not in all.

Consider this little piece of code

function isBig(a) {
    if (a > 5000) {
        return "yes";
    }
}

console.log(isBig(5001)); //returns yes
console.log(isBig(42)); //returns undefined

The function isBig will only return a specific value when its parameter is bigger than 5000. In any other case, it will implicitly return undefined.

This behaviour may not be what you had intended. In any case, you can add a return undefined to the other execution path to make the return value explicit.

Loading history...
516
    parts = planB[0].value.split(' ');
517
    return {stream: parts[0], track: parts[1]};
518
  }
519
};
520
521
// Generate a session ID for SDP.
522
// https://tools.ietf.org/html/draft-ietf-rtcweb-jsep-20#section-5.2.1
523
// recommends using a cryptographically random +ve 64-bit value
524
// but right now this should be acceptable and within the right range
525
SDPUtils.generateSessionId = function() {
526
  return Math.random().toString().substr(2, 21);
527
};
528
529
// Write boilder plate for start of SDP
530
// sessId argument is optional - if not supplied it will
531
// be generated randomly
532
SDPUtils.writeSessionBoilerplate = function(sessId) {
533
  var sessionId;
534
  if (sessId) {
535
    sessionId = sessId;
536
  } else {
537
    sessionId = SDPUtils.generateSessionId();
538
  }
539
  // FIXME: sess-id should be an NTP timestamp.
540
  return 'v=0\r\n' +
541
      'o=thisisadapterortc ' + sessionId + ' 2 IN IP4 127.0.0.1\r\n' +
542
      's=-\r\n' +
543
      't=0 0\r\n';
544
};
545
546
SDPUtils.writeMediaSection = function(transceiver, caps, type, stream) {
547
  var sdp = SDPUtils.writeRtpDescription(transceiver.kind, caps);
548
549
  // Map ICE parameters (ufrag, pwd) to SDP.
550
  sdp += SDPUtils.writeIceParameters(
551
      transceiver.iceGatherer.getLocalParameters());
552
553
  // Map DTLS parameters to SDP.
554
  sdp += SDPUtils.writeDtlsParameters(
555
      transceiver.dtlsTransport.getLocalParameters(),
556
      type === 'offer' ? 'actpass' : 'active');
557
558
  sdp += 'a=mid:' + transceiver.mid + '\r\n';
559
560
  if (transceiver.direction) {
561
    sdp += 'a=' + transceiver.direction + '\r\n';
562
  } else if (transceiver.rtpSender && transceiver.rtpReceiver) {
563
    sdp += 'a=sendrecv\r\n';
564
  } else if (transceiver.rtpSender) {
565
    sdp += 'a=sendonly\r\n';
566
  } else if (transceiver.rtpReceiver) {
567
    sdp += 'a=recvonly\r\n';
568
  } else {
569
    sdp += 'a=inactive\r\n';
570
  }
571
572
  if (transceiver.rtpSender) {
573
    // spec.
574
    var msid = 'msid:' + stream.id + ' ' +
575
        transceiver.rtpSender.track.id + '\r\n';
576
    sdp += 'a=' + msid;
577
578
    // for Chrome.
579
    sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].ssrc +
580
        ' ' + msid;
581
    if (transceiver.sendEncodingParameters[0].rtx) {
582
      sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].rtx.ssrc +
583
          ' ' + msid;
584
      sdp += 'a=ssrc-group:FID ' +
585
          transceiver.sendEncodingParameters[0].ssrc + ' ' +
586
          transceiver.sendEncodingParameters[0].rtx.ssrc +
587
          '\r\n';
588
    }
589
  }
590
  // FIXME: this should be written by writeRtpDescription.
591
  sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].ssrc +
592
      ' cname:' + SDPUtils.localCName + '\r\n';
593
  if (transceiver.rtpSender && transceiver.sendEncodingParameters[0].rtx) {
594
    sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].rtx.ssrc +
595
        ' cname:' + SDPUtils.localCName + '\r\n';
596
  }
597
  return sdp;
598
};
599
600
// Gets the direction from the mediaSection or the sessionpart.
601
SDPUtils.getDirection = function(mediaSection, sessionpart) {
602
  // Look for sendrecv, sendonly, recvonly, inactive, default to sendrecv.
603
  var lines = SDPUtils.splitLines(mediaSection);
604
  for (var i = 0; i < lines.length; i++) {
605
    switch (lines[i]) {
606
      case 'a=sendrecv':
607
      case 'a=sendonly':
608
      case 'a=recvonly':
609
      case 'a=inactive':
610
        return lines[i].substr(2);
611
      default:
612
        // FIXME: What should happen here?
613
    }
614
  }
615
  if (sessionpart) {
616
    return SDPUtils.getDirection(sessionpart);
617
  }
618
  return 'sendrecv';
619
};
620
621
SDPUtils.getKind = function(mediaSection) {
622
  var lines = SDPUtils.splitLines(mediaSection);
623
  var mline = lines[0].split(' ');
624
  return mline[0].substr(2);
625
};
626
627
SDPUtils.isRejected = function(mediaSection) {
628
  return mediaSection.split(' ', 2)[1] === '0';
629
};
630
631
// Expose public methods.
632
module.exports = SDPUtils;
633
634
},{}],2:[function(require,module,exports){
635
(function (global){
636
/*
637
 *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
638
 *
639
 *  Use of this source code is governed by a BSD-style license
640
 *  that can be found in the LICENSE file in the root of the source
641
 *  tree.
642
 */
643
 /* eslint-env node */
644
645
'use strict';
646
647
var adapterFactory = require('./adapter_factory.js');
648
module.exports = adapterFactory({window: global.window});
649
650
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
0 ignored issues
show
Bug introduced by
The variable self seems to be never declared. If this is a global, consider adding a /** global: self */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
651
},{"./adapter_factory.js":3}],3:[function(require,module,exports){
652
/*
653
 *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
654
 *
655
 *  Use of this source code is governed by a BSD-style license
656
 *  that can be found in the LICENSE file in the root of the source
657
 *  tree.
658
 */
659
 /* eslint-env node */
660
661
'use strict';
662
663
// Shimming starts here.
664
module.exports = function(dependencies, opts) {
665
  var window = dependencies && dependencies.window;
666
667
  var options = Object.assign({
668
    shimChrome: true,
669
    shimFirefox: true,
670
    shimEdge: true,
671
    shimSafari: true,
672
  }, opts);
673
674
  // Utils.
675
  var utils = require('./utils');
676
  var logging = utils.log;
677
  var browserDetails = utils.detectBrowser(window);
678
679
  // Export to the adapter global object visible in the browser.
680
  var adapter = {
681
    browserDetails: browserDetails,
682
    extractVersion: utils.extractVersion,
683
    disableLog: utils.disableLog,
684
    disableWarnings: utils.disableWarnings
685
  };
686
687
  // Uncomment the line below if you want logging to occur, including logging
688
  // for the switch statement below. Can also be turned on in the browser via
689
  // adapter.disableLog(false), but then logging from the switch statement below
690
  // will not appear.
691
  // require('./utils').disableLog(false);
692
693
  // Browser shims.
694
  var chromeShim = require('./chrome/chrome_shim') || null;
695
  var edgeShim = require('./edge/edge_shim') || null;
696
  var firefoxShim = require('./firefox/firefox_shim') || null;
697
  var safariShim = require('./safari/safari_shim') || null;
698
699
  // Shim browser if found.
700
  switch (browserDetails.browser) {
701
    case 'chrome':
702
      if (!chromeShim || !chromeShim.shimPeerConnection ||
703
          !options.shimChrome) {
704
        logging('Chrome shim is not included in this adapter release.');
705
        return adapter;
706
      }
707
      logging('adapter.js shimming chrome.');
708
      // Export to the adapter global object visible in the browser.
709
      adapter.browserShim = chromeShim;
710
711
      chromeShim.shimGetUserMedia(window);
712
      chromeShim.shimMediaStream(window);
713
      utils.shimCreateObjectURL(window);
714
      chromeShim.shimSourceObject(window);
715
      chromeShim.shimPeerConnection(window);
716
      chromeShim.shimOnTrack(window);
717
      chromeShim.shimAddTrack(window);
718
      chromeShim.shimGetSendersWithDtmf(window);
719
      break;
720
    case 'firefox':
721
      if (!firefoxShim || !firefoxShim.shimPeerConnection ||
722
          !options.shimFirefox) {
723
        logging('Firefox shim is not included in this adapter release.');
724
        return adapter;
725
      }
726
      logging('adapter.js shimming firefox.');
727
      // Export to the adapter global object visible in the browser.
728
      adapter.browserShim = firefoxShim;
729
730
      firefoxShim.shimGetUserMedia(window);
731
      utils.shimCreateObjectURL(window);
732
      firefoxShim.shimSourceObject(window);
733
      firefoxShim.shimPeerConnection(window);
734
      firefoxShim.shimOnTrack(window);
735
      break;
736
    case 'edge':
737
      if (!edgeShim || !edgeShim.shimPeerConnection || !options.shimEdge) {
738
        logging('MS edge shim is not included in this adapter release.');
739
        return adapter;
740
      }
741
      logging('adapter.js shimming edge.');
742
      // Export to the adapter global object visible in the browser.
743
      adapter.browserShim = edgeShim;
744
745
      edgeShim.shimGetUserMedia(window);
746
      utils.shimCreateObjectURL(window);
747
      edgeShim.shimPeerConnection(window);
748
      edgeShim.shimReplaceTrack(window);
749
      break;
750
    case 'safari':
751
      if (!safariShim || !options.shimSafari) {
752
        logging('Safari shim is not included in this adapter release.');
753
        return adapter;
754
      }
755
      logging('adapter.js shimming safari.');
756
      // Export to the adapter global object visible in the browser.
757
      adapter.browserShim = safariShim;
758
      // shim window.URL.createObjectURL Safari (technical preview)
759
      utils.shimCreateObjectURL(window);
760
      safariShim.shimRTCIceServerUrls(window);
761
      safariShim.shimCallbacksAPI(window);
762
      safariShim.shimLocalStreamsAPI(window);
763
      safariShim.shimRemoteStreamsAPI(window);
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
764
      safariShim.shimGetUserMedia(window);
765
      break;
766
    default:
767
      logging('Unsupported browser!');
768
      break;
769
  }
770
771
  return adapter;
772
};
773
774
},{"./chrome/chrome_shim":4,"./edge/edge_shim":6,"./firefox/firefox_shim":9,"./safari/safari_shim":11,"./utils":12}],4:[function(require,module,exports){
775
776
/*
777
 *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
778
 *
779
 *  Use of this source code is governed by a BSD-style license
780
 *  that can be found in the LICENSE file in the root of the source
781
 *  tree.
782
 */
783
 /* eslint-env node */
784
'use strict';
785
var utils = require('../utils.js');
786
var logging = utils.log;
787
788
var chromeShim = {
789
  shimMediaStream: function(window) {
790
    window.MediaStream = window.MediaStream || window.webkitMediaStream;
791
  },
792
793
  shimOnTrack: function(window) {
794
    if (typeof window === 'object' && window.RTCPeerConnection && !('ontrack' in
795
        window.RTCPeerConnection.prototype)) {
796
      Object.defineProperty(window.RTCPeerConnection.prototype, 'ontrack', {
797
        get: function() {
798
          return this._ontrack;
799
        },
800
        set: function(f) {
801
          var self = this;
802
          if (this._ontrack) {
803
            this.removeEventListener('track', this._ontrack);
804
            this.removeEventListener('addstream', this._ontrackpoly);
805
          }
806
          this.addEventListener('track', this._ontrack = f);
807
          this.addEventListener('addstream', this._ontrackpoly = function(e) {
808
            // onaddstream does not fire when a track is added to an existing
809
            // stream. But stream.onaddtrack is implemented so we use that.
810
            e.stream.addEventListener('addtrack', function(te) {
811
              var receiver;
812
              if (window.RTCPeerConnection.prototype.getReceivers) {
813
                receiver = self.getReceivers().find(function(r) {
814
                  return r.track.id === te.track.id;
815
                });
816
              } else {
817
                receiver = {track: te.track};
818
              }
819
820
              var event = new Event('track');
0 ignored issues
show
Bug introduced by
The variable Event seems to be never declared. If this is a global, consider adding a /** global: Event */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
821
              event.track = te.track;
822
              event.receiver = receiver;
823
              event.streams = [e.stream];
824
              self.dispatchEvent(event);
825
            });
826
            e.stream.getTracks().forEach(function(track) {
827
              var receiver;
828
              if (window.RTCPeerConnection.prototype.getReceivers) {
829
                receiver = self.getReceivers().find(function(r) {
830
                  return r.track.id === track.id;
831
                });
832
              } else {
833
                receiver = {track: track};
834
              }
835
              var event = new Event('track');
0 ignored issues
show
Bug introduced by
The variable Event seems to be never declared. If this is a global, consider adding a /** global: Event */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
836
              event.track = track;
837
              event.receiver = receiver;
838
              event.streams = [e.stream];
839
              this.dispatchEvent(event);
840
            }.bind(this));
841
          }.bind(this));
842
        }
843
      });
844
    }
845
  },
846
847
  shimGetSendersWithDtmf: function(window) {
848
    if (typeof window === 'object' && window.RTCPeerConnection &&
849
        !('getSenders' in window.RTCPeerConnection.prototype) &&
850
        'createDTMFSender' in window.RTCPeerConnection.prototype) {
851
      var shimSenderWithDtmf = function(pc, track) {
852
        return {
853
          track: track,
854
          get dtmf() {
855
            if (this._dtmf === undefined) {
856
              if (track.kind === 'audio') {
857
                this._dtmf = pc.createDTMFSender(track);
858
              } else {
859
                this._dtmf = null;
860
              }
861
            }
862
            return this._dtmf;
863
          }
864
        };
865
      };
866
867
      // shim addTrack when getSenders is not available.
868
      if (!window.RTCPeerConnection.prototype.getSenders) {
869
        window.RTCPeerConnection.prototype.getSenders = function() {
870
          return this._senders || [];
871
        };
872
        var origAddTrack = window.RTCPeerConnection.prototype.addTrack;
873
        window.RTCPeerConnection.prototype.addTrack = function(track, stream) {
0 ignored issues
show
Unused Code introduced by
The parameter stream is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
874
          var pc = this;
875
          var sender = origAddTrack.apply(pc, arguments);
876
          if (!sender) {
877
            sender = shimSenderWithDtmf(pc, track);
878
            pc._senders.push(sender);
879
          }
880
          return sender;
881
        };
882
      }
883
      var origAddStream = window.RTCPeerConnection.prototype.addStream;
884
      window.RTCPeerConnection.prototype.addStream = function(stream) {
885
        var pc = this;
886
        pc._senders = pc._senders || [];
887
        origAddStream.apply(pc, [stream]);
888
        stream.getTracks().forEach(function(track) {
889
          pc._senders.push(shimSenderWithDtmf(pc, track));
890
        });
891
      };
892
893
      var origRemoveStream = window.RTCPeerConnection.prototype.removeStream;
894
      window.RTCPeerConnection.prototype.removeStream = function(stream) {
895
        var pc = this;
896
        pc._senders = pc._senders || [];
897
        origRemoveStream.apply(pc, [(pc._streams[stream.id] || stream)]);
898
899
        stream.getTracks().forEach(function(track) {
900
          var sender = pc._senders.find(function(s) {
901
            return s.track === track;
902
          });
903
          if (sender) {
904
            pc._senders.splice(pc._senders.indexOf(sender), 1); // remove sender
905
          }
906
        });
907
      };
908
    } else if (typeof window === 'object' && window.RTCPeerConnection &&
909
               'getSenders' in window.RTCPeerConnection.prototype &&
910
               'createDTMFSender' in window.RTCPeerConnection.prototype &&
911
               window.RTCRtpSender &&
912
               !('dtmf' in window.RTCRtpSender.prototype)) {
913
      var origGetSenders = window.RTCPeerConnection.prototype.getSenders;
914
      window.RTCPeerConnection.prototype.getSenders = function() {
915
        var pc = this;
916
        var senders = origGetSenders.apply(pc, []);
917
        senders.forEach(function(sender) {
918
          sender._pc = pc;
919
        });
920
        return senders;
921
      };
922
923
      Object.defineProperty(window.RTCRtpSender.prototype, 'dtmf', {
924
        get: function() {
925
          if (this._dtmf === undefined) {
926
            if (this.track.kind === 'audio') {
927
              this._dtmf = this._pc.createDTMFSender(this.track);
928
            } else {
929
              this._dtmf = null;
930
            }
931
          }
932
          return this._dtmf;
933
        },
934
      });
935
    }
936
  },
937
938
  shimSourceObject: function(window) {
939
    var URL = window && window.URL;
940
941
    if (typeof window === 'object') {
942
      if (window.HTMLMediaElement &&
943
        !('srcObject' in window.HTMLMediaElement.prototype)) {
944
        // Shim the srcObject property, once, when HTMLMediaElement is found.
945
        Object.defineProperty(window.HTMLMediaElement.prototype, 'srcObject', {
946
          get: function() {
947
            return this._srcObject;
948
          },
949
          set: function(stream) {
950
            var self = this;
951
            // Use _srcObject as a private property for this shim
952
            this._srcObject = stream;
953
            if (this.src) {
954
              URL.revokeObjectURL(this.src);
955
            }
956
957
            if (!stream) {
958
              this.src = '';
959
              return undefined;
960
            }
961
            this.src = URL.createObjectURL(stream);
962
            // We need to recreate the blob url when a track is added or
963
            // removed. Doing it manually since we want to avoid a recursion.
964
            stream.addEventListener('addtrack', function() {
965
              if (self.src) {
966
                URL.revokeObjectURL(self.src);
967
              }
968
              self.src = URL.createObjectURL(stream);
969
            });
970
            stream.addEventListener('removetrack', function() {
971
              if (self.src) {
972
                URL.revokeObjectURL(self.src);
973
              }
974
              self.src = URL.createObjectURL(stream);
975
            });
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
976
          }
977
        });
978
      }
979
    }
980
  },
981
982
  shimAddTrack: function(window) {
983
    // shim addTrack (when getSenders is available)
984
    if (window.RTCPeerConnection.prototype.addTrack) {
985
      return;
986
    }
987
988
    // also shim pc.getLocalStreams when addTrack is shimmed
989
    // to return the original streams.
990
    var origGetLocalStreams = window.RTCPeerConnection.prototype
991
        .getLocalStreams;
992
    window.RTCPeerConnection.prototype.getLocalStreams = function() {
993
      var self = this;
994
      var nativeStreams = origGetLocalStreams.apply(this);
995
      self._reverseStreams = self._reverseStreams || {};
996
      return nativeStreams.map(function(stream) {
997
        return self._reverseStreams[stream.id];
998
      });
999
    };
1000
1001
    var origAddStream = window.RTCPeerConnection.prototype.addStream;
1002
    window.RTCPeerConnection.prototype.addStream = function(stream) {
1003
      var pc = this;
1004
      pc._streams = pc._streams || {};
1005
      pc._reverseStreams = pc._reverseStreams || {};
1006
1007
      // Add identity mapping for consistency with addTrack.
1008
      // Unless this is being used with a stream from addTrack.
1009
      if (!pc._reverseStreams[stream.id]) {
1010
        pc._streams[stream.id] = stream;
1011
        pc._reverseStreams[stream.id] = stream;
1012
      }
1013
      origAddStream.apply(pc, [stream]);
1014
    };
1015
1016
    var origRemoveStream = window.RTCPeerConnection.prototype.removeStream;
1017
    window.RTCPeerConnection.prototype.removeStream = function(stream) {
1018
      var pc = this;
1019
      pc._streams = pc._streams || {};
1020
      pc._reverseStreams = pc._reverseStreams || {};
1021
1022
      origRemoveStream.apply(pc, [(pc._streams[stream.id] || stream)]);
1023
      delete pc._reverseStreams[(pc._streams[stream.id] ?
1024
          pc._streams[stream.id].id : stream.id)];
1025
      delete pc._streams[stream.id];
1026
    };
1027
1028
    window.RTCPeerConnection.prototype.addTrack = function(track, stream) {
1029
      var pc = this;
1030
      if (pc.signalingState === 'closed') {
1031
        throw new DOMException(
0 ignored issues
show
Bug introduced by
The variable DOMException seems to be never declared. If this is a global, consider adding a /** global: DOMException */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
1032
          'The RTCPeerConnection\'s signalingState is \'closed\'.',
1033
          'InvalidStateError');
1034
      }
1035
      var streams = [].slice.call(arguments, 1);
1036
      if (streams.length !== 1 ||
1037
          !streams[0].getTracks().find(function(t) {
1038
            return t === track;
1039
          })) {
1040
        // this is not fully correct but all we can manage without
1041
        // [[associated MediaStreams]] internal slot.
1042
        throw new DOMException(
0 ignored issues
show
Bug introduced by
The variable DOMException seems to be never declared. If this is a global, consider adding a /** global: DOMException */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
1043
          'The adapter.js addTrack polyfill only supports a single ' +
1044
          ' stream which is associated with the specified track.',
1045
          'NotSupportedError');
1046
      }
1047
1048
      var alreadyExists = pc.getSenders().find(function(s) {
1049
        return s.track === track;
1050
      });
1051
      if (alreadyExists) {
1052
        throw new DOMException('Track already exists.',
0 ignored issues
show
Bug introduced by
The variable DOMException seems to be never declared. If this is a global, consider adding a /** global: DOMException */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
1053
            'InvalidAccessError');
1054
      }
1055
1056
      pc._streams = pc._streams || {};
1057
      pc._reverseStreams = pc._reverseStreams || {};
1058
      var oldStream = pc._streams[stream.id];
1059
      if (oldStream) {
1060
        // this is using odd Chrome behaviour, use with caution:
1061
        // https://bugs.chromium.org/p/webrtc/issues/detail?id=7815
1062
        oldStream.addTrack(track);
1063
        pc.dispatchEvent(new Event('negotiationneeded'));
0 ignored issues
show
Bug introduced by
The variable Event seems to be never declared. If this is a global, consider adding a /** global: Event */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
1064
      } else {
1065
        var newStream = new window.MediaStream([track]);
1066
        pc._streams[stream.id] = newStream;
1067
        pc._reverseStreams[newStream.id] = stream;
1068
        pc.addStream(newStream);
1069
      }
1070
      return pc.getSenders().find(function(s) {
1071
        return s.track === track;
1072
      });
1073
    };
1074
  },
1075
1076
  shimPeerConnection: function(window) {
1077
    var browserDetails = utils.detectBrowser(window);
1078
1079
    // The RTCPeerConnection object.
1080
    if (!window.RTCPeerConnection) {
1081
      window.RTCPeerConnection = function(pcConfig, pcConstraints) {
1082
        // Translate iceTransportPolicy to iceTransports,
1083
        // see https://code.google.com/p/webrtc/issues/detail?id=4869
1084
        // this was fixed in M56 along with unprefixing RTCPeerConnection.
1085
        logging('PeerConnection');
1086
        if (pcConfig && pcConfig.iceTransportPolicy) {
1087
          pcConfig.iceTransports = pcConfig.iceTransportPolicy;
1088
        }
1089
1090
        return new window.webkitRTCPeerConnection(pcConfig, pcConstraints);
1091
      };
1092
      window.RTCPeerConnection.prototype =
1093
          window.webkitRTCPeerConnection.prototype;
1094
      // wrap static methods. Currently just generateCertificate.
1095
      if (window.webkitRTCPeerConnection.generateCertificate) {
1096
        Object.defineProperty(window.RTCPeerConnection, 'generateCertificate', {
1097
          get: function() {
1098
            return window.webkitRTCPeerConnection.generateCertificate;
1099
          }
1100
        });
1101
      }
1102
    } else {
1103
      // migrate from non-spec RTCIceServer.url to RTCIceServer.urls
1104
      var OrigPeerConnection = window.RTCPeerConnection;
1105
      window.RTCPeerConnection = function(pcConfig, pcConstraints) {
1106
        if (pcConfig && pcConfig.iceServers) {
1107
          var newIceServers = [];
1108
          for (var i = 0; i < pcConfig.iceServers.length; i++) {
1109
            var server = pcConfig.iceServers[i];
1110
            if (!server.hasOwnProperty('urls') &&
1111
                server.hasOwnProperty('url')) {
1112
              console.warn('RTCIceServer.url is deprecated! Use urls instead.');
1113
              server = JSON.parse(JSON.stringify(server));
1114
              server.urls = server.url;
1115
              newIceServers.push(server);
1116
            } else {
1117
              newIceServers.push(pcConfig.iceServers[i]);
1118
            }
1119
          }
1120
          pcConfig.iceServers = newIceServers;
1121
        }
1122
        return new OrigPeerConnection(pcConfig, pcConstraints);
1123
      };
1124
      window.RTCPeerConnection.prototype = OrigPeerConnection.prototype;
1125
      // wrap static methods. Currently just generateCertificate.
1126
      Object.defineProperty(window.RTCPeerConnection, 'generateCertificate', {
1127
        get: function() {
1128
          return OrigPeerConnection.generateCertificate;
1129
        }
1130
      });
1131
    }
1132
1133
    var origGetStats = window.RTCPeerConnection.prototype.getStats;
1134
    window.RTCPeerConnection.prototype.getStats = function(selector,
1135
        successCallback, errorCallback) {
1136
      var self = this;
1137
      var args = arguments;
1138
1139
      // If selector is a function then we are in the old style stats so just
1140
      // pass back the original getStats format to avoid breaking old users.
1141
      if (arguments.length > 0 && typeof selector === 'function') {
1142
        return origGetStats.apply(this, arguments);
1143
      }
1144
1145
      // When spec-style getStats is supported, return those when called with
1146
      // either no arguments or the selector argument is null.
1147
      if (origGetStats.length === 0 && (arguments.length === 0 ||
1148
          typeof arguments[0] !== 'function')) {
1149
        return origGetStats.apply(this, []);
1150
      }
1151
1152
      var fixChromeStats_ = function(response) {
1153
        var standardReport = {};
1154
        var reports = response.result();
1155
        reports.forEach(function(report) {
1156
          var standardStats = {
1157
            id: report.id,
1158
            timestamp: report.timestamp,
1159
            type: {
1160
              localcandidate: 'local-candidate',
1161
              remotecandidate: 'remote-candidate'
1162
            }[report.type] || report.type
1163
          };
1164
          report.names().forEach(function(name) {
1165
            standardStats[name] = report.stat(name);
1166
          });
1167
          standardReport[standardStats.id] = standardStats;
1168
        });
1169
1170
        return standardReport;
1171
      };
1172
1173
      // shim getStats with maplike support
1174
      var makeMapStats = function(stats) {
1175
        return new Map(Object.keys(stats).map(function(key) {
1176
          return [key, stats[key]];
1177
        }));
1178
      };
1179
1180
      if (arguments.length >= 2) {
1181
        var successCallbackWrapper_ = function(response) {
1182
          args[1](makeMapStats(fixChromeStats_(response)));
1183
        };
1184
1185
        return origGetStats.apply(this, [successCallbackWrapper_,
1186
          arguments[0]]);
1187
      }
1188
1189
      // promise-support
1190
      return new Promise(function(resolve, reject) {
1191
        origGetStats.apply(self, [
1192
          function(response) {
1193
            resolve(makeMapStats(fixChromeStats_(response)));
1194
          }, reject]);
1195
      }).then(successCallback, errorCallback);
1196
    };
1197
1198
    // add promise support -- natively available in Chrome 51
1199
    if (browserDetails.version < 51) {
1200
      ['setLocalDescription', 'setRemoteDescription', 'addIceCandidate']
1201
          .forEach(function(method) {
1202
            var nativeMethod = window.RTCPeerConnection.prototype[method];
1203
            window.RTCPeerConnection.prototype[method] = function() {
1204
              var args = arguments;
1205
              var self = this;
1206
              var promise = new Promise(function(resolve, reject) {
1207
                nativeMethod.apply(self, [args[0], resolve, reject]);
1208
              });
1209
              if (args.length < 2) {
1210
                return promise;
1211
              }
1212
              return promise.then(function() {
1213
                args[1].apply(null, []);
1214
              },
1215
              function(err) {
1216
                if (args.length >= 3) {
1217
                  args[2].apply(null, [err]);
1218
                }
1219
              });
1220
            };
1221
          });
1222
    }
1223
1224
    // promise support for createOffer and createAnswer. Available (without
1225
    // bugs) since M52: crbug/619289
1226
    if (browserDetails.version < 52) {
1227
      ['createOffer', 'createAnswer'].forEach(function(method) {
1228
        var nativeMethod = window.RTCPeerConnection.prototype[method];
1229
        window.RTCPeerConnection.prototype[method] = function() {
1230
          var self = this;
1231
          if (arguments.length < 1 || (arguments.length === 1 &&
1232
              typeof arguments[0] === 'object')) {
1233
            var opts = arguments.length === 1 ? arguments[0] : undefined;
1234
            return new Promise(function(resolve, reject) {
1235
              nativeMethod.apply(self, [resolve, reject, opts]);
1236
            });
1237
          }
1238
          return nativeMethod.apply(this, arguments);
1239
        };
1240
      });
1241
    }
1242
1243
    // shim implicit creation of RTCSessionDescription/RTCIceCandidate
1244
    ['setLocalDescription', 'setRemoteDescription', 'addIceCandidate']
1245
        .forEach(function(method) {
1246
          var nativeMethod = window.RTCPeerConnection.prototype[method];
1247
          window.RTCPeerConnection.prototype[method] = function() {
1248
            arguments[0] = new ((method === 'addIceCandidate') ?
1249
                window.RTCIceCandidate :
1250
                window.RTCSessionDescription)(arguments[0]);
1251
            return nativeMethod.apply(this, arguments);
1252
          };
1253
        });
1254
1255
    // support for addIceCandidate(null or undefined)
1256
    var nativeAddIceCandidate =
1257
        window.RTCPeerConnection.prototype.addIceCandidate;
1258
    window.RTCPeerConnection.prototype.addIceCandidate = function() {
1259
      if (!arguments[0]) {
1260
        if (arguments[1]) {
1261
          arguments[1].apply(null);
1262
        }
1263
        return Promise.resolve();
1264
      }
1265
      return nativeAddIceCandidate.apply(this, arguments);
1266
    };
1267
  }
1268
};
1269
1270
1271
// Expose public methods.
1272
module.exports = {
1273
  shimMediaStream: chromeShim.shimMediaStream,
1274
  shimOnTrack: chromeShim.shimOnTrack,
1275
  shimAddTrack: chromeShim.shimAddTrack,
1276
  shimGetSendersWithDtmf: chromeShim.shimGetSendersWithDtmf,
1277
  shimSourceObject: chromeShim.shimSourceObject,
1278
  shimPeerConnection: chromeShim.shimPeerConnection,
1279
  shimGetUserMedia: require('./getusermedia')
1280
};
1281
1282
},{"../utils.js":12,"./getusermedia":5}],5:[function(require,module,exports){
1283
/*
1284
 *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
1285
 *
1286
 *  Use of this source code is governed by a BSD-style license
1287
 *  that can be found in the LICENSE file in the root of the source
1288
 *  tree.
1289
 */
1290
 /* eslint-env node */
1291
'use strict';
1292
var utils = require('../utils.js');
1293
var logging = utils.log;
1294
1295
// Expose public methods.
1296
module.exports = function(window) {
1297
  var browserDetails = utils.detectBrowser(window);
1298
  var navigator = window && window.navigator;
1299
1300
  var constraintsToChrome_ = function(c) {
1301
    if (typeof c !== 'object' || c.mandatory || c.optional) {
1302
      return c;
1303
    }
1304
    var cc = {};
1305
    Object.keys(c).forEach(function(key) {
1306
      if (key === 'require' || key === 'advanced' || key === 'mediaSource') {
1307
        return;
1308
      }
1309
      var r = (typeof c[key] === 'object') ? c[key] : {ideal: c[key]};
1310
      if (r.exact !== undefined && typeof r.exact === 'number') {
1311
        r.min = r.max = r.exact;
1312
      }
1313
      var oldname_ = function(prefix, name) {
1314
        if (prefix) {
1315
          return prefix + name.charAt(0).toUpperCase() + name.slice(1);
1316
        }
1317
        return (name === 'deviceId') ? 'sourceId' : name;
1318
      };
1319
      if (r.ideal !== undefined) {
1320
        cc.optional = cc.optional || [];
1321
        var oc = {};
1322
        if (typeof r.ideal === 'number') {
1323
          oc[oldname_('min', key)] = r.ideal;
1324
          cc.optional.push(oc);
1325
          oc = {};
1326
          oc[oldname_('max', key)] = r.ideal;
1327
          cc.optional.push(oc);
1328
        } else {
1329
          oc[oldname_('', key)] = r.ideal;
1330
          cc.optional.push(oc);
1331
        }
1332
      }
1333
      if (r.exact !== undefined && typeof r.exact !== 'number') {
1334
        cc.mandatory = cc.mandatory || {};
1335
        cc.mandatory[oldname_('', key)] = r.exact;
1336
      } else {
1337
        ['min', 'max'].forEach(function(mix) {
1338
          if (r[mix] !== undefined) {
1339
            cc.mandatory = cc.mandatory || {};
1340
            cc.mandatory[oldname_(mix, key)] = r[mix];
1341
          }
1342
        });
1343
      }
1344
    });
1345
    if (c.advanced) {
1346
      cc.optional = (cc.optional || []).concat(c.advanced);
1347
    }
1348
    return cc;
1349
  };
1350
1351
  var shimConstraints_ = function(constraints, func) {
1352
    constraints = JSON.parse(JSON.stringify(constraints));
1353
    if (constraints && typeof constraints.audio === 'object') {
1354
      var remap = function(obj, a, b) {
1355
        if (a in obj && !(b in obj)) {
1356
          obj[b] = obj[a];
1357
          delete obj[a];
1358
        }
1359
      };
1360
      constraints = JSON.parse(JSON.stringify(constraints));
1361
      remap(constraints.audio, 'autoGainControl', 'googAutoGainControl');
1362
      remap(constraints.audio, 'noiseSuppression', 'googNoiseSuppression');
1363
      constraints.audio = constraintsToChrome_(constraints.audio);
1364
    }
1365
    if (constraints && typeof constraints.video === 'object') {
1366
      // Shim facingMode for mobile & surface pro.
1367
      var face = constraints.video.facingMode;
1368
      face = face && ((typeof face === 'object') ? face : {ideal: face});
1369
      var getSupportedFacingModeLies = browserDetails.version < 61;
1370
1371
      if ((face && (face.exact === 'user' || face.exact === 'environment' ||
1372
                    face.ideal === 'user' || face.ideal === 'environment')) &&
1373
          !(navigator.mediaDevices.getSupportedConstraints &&
1374
            navigator.mediaDevices.getSupportedConstraints().facingMode &&
1375
            !getSupportedFacingModeLies)) {
1376
        delete constraints.video.facingMode;
1377
        var matches;
1378
        if (face.exact === 'environment' || face.ideal === 'environment') {
1379
          matches = ['back', 'rear'];
1380
        } else if (face.exact === 'user' || face.ideal === 'user') {
1381
          matches = ['front'];
1382
        }
1383
        if (matches) {
1384
          // Look for matches in label, or use last cam for back (typical).
1385
          return navigator.mediaDevices.enumerateDevices()
1386
          .then(function(devices) {
1387
            devices = devices.filter(function(d) {
1388
              return d.kind === 'videoinput';
1389
            });
1390
            var dev = devices.find(function(d) {
1391
              return matches.some(function(match) {
1392
                return d.label.toLowerCase().indexOf(match) !== -1;
1393
              });
1394
            });
1395
            if (!dev && devices.length && matches.indexOf('back') !== -1) {
1396
              dev = devices[devices.length - 1]; // more likely the back cam
1397
            }
1398
            if (dev) {
1399
              constraints.video.deviceId = face.exact ? {exact: dev.deviceId} :
1400
                                                        {ideal: dev.deviceId};
1401
            }
1402
            constraints.video = constraintsToChrome_(constraints.video);
1403
            logging('chrome: ' + JSON.stringify(constraints));
1404
            return func(constraints);
1405
          });
1406
        }
1407
      }
1408
      constraints.video = constraintsToChrome_(constraints.video);
1409
    }
1410
    logging('chrome: ' + JSON.stringify(constraints));
1411
    return func(constraints);
1412
  };
1413
1414
  var shimError_ = function(e) {
1415
    return {
1416
      name: {
1417
        PermissionDeniedError: 'NotAllowedError',
1418
        InvalidStateError: 'NotReadableError',
1419
        DevicesNotFoundError: 'NotFoundError',
1420
        ConstraintNotSatisfiedError: 'OverconstrainedError',
1421
        TrackStartError: 'NotReadableError',
1422
        MediaDeviceFailedDueToShutdown: 'NotReadableError',
1423
        MediaDeviceKillSwitchOn: 'NotReadableError'
1424
      }[e.name] || e.name,
1425
      message: e.message,
1426
      constraint: e.constraintName,
1427
      toString: function() {
1428
        return this.name + (this.message && ': ') + this.message;
1429
      }
1430
    };
1431
  };
1432
1433
  var getUserMedia_ = function(constraints, onSuccess, onError) {
1434
    shimConstraints_(constraints, function(c) {
1435
      navigator.webkitGetUserMedia(c, onSuccess, function(e) {
1436
        onError(shimError_(e));
1437
      });
1438
    });
1439
  };
1440
1441
  navigator.getUserMedia = getUserMedia_;
1442
1443
  // Returns the result of getUserMedia as a Promise.
1444
  var getUserMediaPromise_ = function(constraints) {
1445
    return new Promise(function(resolve, reject) {
1446
      navigator.getUserMedia(constraints, resolve, reject);
1447
    });
1448
  };
1449
1450
  if (!navigator.mediaDevices) {
1451
    navigator.mediaDevices = {
1452
      getUserMedia: getUserMediaPromise_,
1453
      enumerateDevices: function() {
1454
        return new Promise(function(resolve) {
1455
          var kinds = {audio: 'audioinput', video: 'videoinput'};
1456
          return window.MediaStreamTrack.getSources(function(devices) {
1457
            resolve(devices.map(function(device) {
1458
              return {label: device.label,
1459
                kind: kinds[device.kind],
1460
                deviceId: device.id,
1461
                groupId: ''};
1462
            }));
1463
          });
1464
        });
1465
      },
1466
      getSupportedConstraints: function() {
1467
        return {
1468
          deviceId: true, echoCancellation: true, facingMode: true,
1469
          frameRate: true, height: true, width: true
1470
        };
1471
      }
1472
    };
1473
  }
1474
1475
  // A shim for getUserMedia method on the mediaDevices object.
1476
  // TODO(KaptenJansson) remove once implemented in Chrome stable.
1477
  if (!navigator.mediaDevices.getUserMedia) {
1478
    navigator.mediaDevices.getUserMedia = function(constraints) {
1479
      return getUserMediaPromise_(constraints);
1480
    };
1481
  } else {
1482
    // Even though Chrome 45 has navigator.mediaDevices and a getUserMedia
1483
    // function which returns a Promise, it does not accept spec-style
1484
    // constraints.
1485
    var origGetUserMedia = navigator.mediaDevices.getUserMedia.
1486
        bind(navigator.mediaDevices);
1487
    navigator.mediaDevices.getUserMedia = function(cs) {
1488
      return shimConstraints_(cs, function(c) {
1489
        return origGetUserMedia(c).then(function(stream) {
1490
          if (c.audio && !stream.getAudioTracks().length ||
1491
              c.video && !stream.getVideoTracks().length) {
1492
            stream.getTracks().forEach(function(track) {
1493
              track.stop();
1494
            });
1495
            throw new DOMException('', 'NotFoundError');
0 ignored issues
show
Bug introduced by
The variable DOMException seems to be never declared. If this is a global, consider adding a /** global: DOMException */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
1496
          }
1497
          return stream;
1498
        }, function(e) {
1499
          return Promise.reject(shimError_(e));
1500
        });
1501
      });
1502
    };
1503
  }
1504
1505
  // Dummy devicechange event methods.
1506
  // TODO(KaptenJansson) remove once implemented in Chrome stable.
1507
  if (typeof navigator.mediaDevices.addEventListener === 'undefined') {
1508
    navigator.mediaDevices.addEventListener = function() {
1509
      logging('Dummy mediaDevices.addEventListener called.');
1510
    };
1511
  }
1512
  if (typeof navigator.mediaDevices.removeEventListener === 'undefined') {
1513
    navigator.mediaDevices.removeEventListener = function() {
1514
      logging('Dummy mediaDevices.removeEventListener called.');
1515
    };
1516
  }
1517
};
1518
1519
},{"../utils.js":12}],6:[function(require,module,exports){
1520
/*
1521
 *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
1522
 *
1523
 *  Use of this source code is governed by a BSD-style license
1524
 *  that can be found in the LICENSE file in the root of the source
1525
 *  tree.
1526
 */
1527
 /* eslint-env node */
1528
'use strict';
1529
1530
var utils = require('../utils');
1531
var shimRTCPeerConnection = require('./rtcpeerconnection_shim');
1532
1533
module.exports = {
1534
  shimGetUserMedia: require('./getusermedia'),
1535
  shimPeerConnection: function(window) {
1536
    var browserDetails = utils.detectBrowser(window);
1537
1538
    if (window.RTCIceGatherer) {
1539
      // ORTC defines an RTCIceCandidate object but no constructor.
1540
      // Not implemented in Edge.
1541
      if (!window.RTCIceCandidate) {
1542
        window.RTCIceCandidate = function(args) {
1543
          return args;
1544
        };
1545
      }
1546
      // ORTC does not have a session description object but
1547
      // other browsers (i.e. Chrome) that will support both PC and ORTC
1548
      // in the future might have this defined already.
1549
      if (!window.RTCSessionDescription) {
1550
        window.RTCSessionDescription = function(args) {
1551
          return args;
1552
        };
1553
      }
1554
      // this adds an additional event listener to MediaStrackTrack that signals
1555
      // when a tracks enabled property was changed. Workaround for a bug in
1556
      // addStream, see below. No longer required in 15025+
1557
      if (browserDetails.version < 15025) {
1558
        var origMSTEnabled = Object.getOwnPropertyDescriptor(
1559
            window.MediaStreamTrack.prototype, 'enabled');
1560
        Object.defineProperty(window.MediaStreamTrack.prototype, 'enabled', {
1561
          set: function(value) {
1562
            origMSTEnabled.set.call(this, value);
1563
            var ev = new Event('enabled');
0 ignored issues
show
Bug introduced by
The variable Event seems to be never declared. If this is a global, consider adding a /** global: Event */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
1564
            ev.enabled = value;
1565
            this.dispatchEvent(ev);
1566
          }
1567
        });
1568
      }
1569
    }
1570
1571
    // ORTC defines the DTMF sender a bit different.
1572
    // https://github.com/w3c/ortc/issues/714
1573
    if (window.RTCRtpSender && !('dtmf' in window.RTCRtpSender.prototype)) {
1574
      Object.defineProperty(window.RTCRtpSender.prototype, 'dtmf', {
1575
        get: function() {
1576
          if (this._dtmf === undefined) {
1577
            if (this.track.kind === 'audio') {
1578
              this._dtmf = new window.RTCDtmfSender(this);
1579
            } else if (this.track.kind === 'video') {
1580
              this._dtmf = null;
1581
            }
1582
          }
1583
          return this._dtmf;
1584
        }
1585
      });
1586
    }
1587
1588
    window.RTCPeerConnection =
1589
        shimRTCPeerConnection(window, browserDetails.version);
1590
  },
1591
  shimReplaceTrack: function(window) {
1592
    // ORTC has replaceTrack -- https://github.com/w3c/ortc/issues/614
1593
    if (window.RTCRtpSender &&
1594
        !('replaceTrack' in window.RTCRtpSender.prototype)) {
1595
      window.RTCRtpSender.prototype.replaceTrack =
1596
          window.RTCRtpSender.prototype.setTrack;
1597
    }
1598
  }
1599
};
1600
1601
},{"../utils":12,"./getusermedia":7,"./rtcpeerconnection_shim":8}],7:[function(require,module,exports){
1602
/*
1603
 *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
1604
 *
1605
 *  Use of this source code is governed by a BSD-style license
1606
 *  that can be found in the LICENSE file in the root of the source
1607
 *  tree.
1608
 */
1609
 /* eslint-env node */
1610
'use strict';
1611
1612
// Expose public methods.
1613
module.exports = function(window) {
1614
  var navigator = window && window.navigator;
1615
1616
  var shimError_ = function(e) {
1617
    return {
1618
      name: {PermissionDeniedError: 'NotAllowedError'}[e.name] || e.name,
1619
      message: e.message,
1620
      constraint: e.constraint,
1621
      toString: function() {
1622
        return this.name;
1623
      }
1624
    };
1625
  };
1626
1627
  // getUserMedia error shim.
1628
  var origGetUserMedia = navigator.mediaDevices.getUserMedia.
1629
      bind(navigator.mediaDevices);
1630
  navigator.mediaDevices.getUserMedia = function(c) {
1631
    return origGetUserMedia(c).catch(function(e) {
1632
      return Promise.reject(shimError_(e));
1633
    });
1634
  };
1635
};
1636
1637
},{}],8:[function(require,module,exports){
1638
/*
1639
 *  Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
1640
 *
1641
 *  Use of this source code is governed by a BSD-style license
1642
 *  that can be found in the LICENSE file in the root of the source
1643
 *  tree.
1644
 */
1645
 /* eslint-env node */
1646
'use strict';
1647
1648
var SDPUtils = require('sdp');
1649
1650
// sort tracks such that they follow an a-v-a-v...
1651
// pattern.
1652
function sortTracks(tracks) {
1653
  var audioTracks = tracks.filter(function(track) {
1654
    return track.kind === 'audio';
1655
  });
1656
  var videoTracks = tracks.filter(function(track) {
1657
    return track.kind === 'video';
1658
  });
1659
  tracks = [];
1660
  while (audioTracks.length || videoTracks.length) {
1661
    if (audioTracks.length) {
1662
      tracks.push(audioTracks.shift());
1663
    }
1664
    if (videoTracks.length) {
1665
      tracks.push(videoTracks.shift());
1666
    }
1667
  }
1668
  return tracks;
1669
}
1670
1671
// Edge does not like
1672
// 1) stun:
1673
// 2) turn: that does not have all of turn:host:port?transport=udp
1674
// 3) turn: with ipv6 addresses
1675
// 4) turn: occurring muliple times
1676
function filterIceServers(iceServers, edgeVersion) {
1677
  var hasTurn = false;
1678
  iceServers = JSON.parse(JSON.stringify(iceServers));
1679
  return iceServers.filter(function(server) {
1680
    if (server && (server.urls || server.url)) {
1681
      var urls = server.urls || server.url;
1682
      if (server.url && !server.urls) {
1683
        console.warn('RTCIceServer.url is deprecated! Use urls instead.');
1684
      }
1685
      var isString = typeof urls === 'string';
1686
      if (isString) {
1687
        urls = [urls];
1688
      }
1689
      urls = urls.filter(function(url) {
1690
        var validTurn = url.indexOf('turn:') === 0 &&
1691
            url.indexOf('transport=udp') !== -1 &&
1692
            url.indexOf('turn:[') === -1 &&
1693
            !hasTurn;
1694
1695
        if (validTurn) {
1696
          hasTurn = true;
1697
          return true;
1698
        }
1699
        return url.indexOf('stun:') === 0 && edgeVersion >= 14393;
1700
      });
1701
1702
      delete server.url;
1703
      server.urls = isString ? urls[0] : urls;
1704
      return !!urls.length;
1705
    }
1706
    return false;
1707
  });
1708
}
1709
1710
// Determines the intersection of local and remote capabilities.
1711
function getCommonCapabilities(localCapabilities, remoteCapabilities) {
1712
  var commonCapabilities = {
1713
    codecs: [],
1714
    headerExtensions: [],
1715
    fecMechanisms: []
1716
  };
1717
1718
  var findCodecByPayloadType = function(pt, codecs) {
1719
    pt = parseInt(pt, 10);
1720
    for (var i = 0; i < codecs.length; i++) {
1721
      if (codecs[i].payloadType === pt ||
1722
          codecs[i].preferredPayloadType === pt) {
1723
        return codecs[i];
1724
      }
1725
    }
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
1726
  };
1727
1728
  var rtxCapabilityMatches = function(lRtx, rRtx, lCodecs, rCodecs) {
1729
    var lCodec = findCodecByPayloadType(lRtx.parameters.apt, lCodecs);
1730
    var rCodec = findCodecByPayloadType(rRtx.parameters.apt, rCodecs);
1731
    return lCodec && rCodec &&
1732
        lCodec.name.toLowerCase() === rCodec.name.toLowerCase();
1733
  };
1734
1735
  localCapabilities.codecs.forEach(function(lCodec) {
1736
    for (var i = 0; i < remoteCapabilities.codecs.length; i++) {
1737
      var rCodec = remoteCapabilities.codecs[i];
1738
      if (lCodec.name.toLowerCase() === rCodec.name.toLowerCase() &&
1739
          lCodec.clockRate === rCodec.clockRate) {
1740
        if (lCodec.name.toLowerCase() === 'rtx' &&
1741
            lCodec.parameters && rCodec.parameters.apt) {
1742
          // for RTX we need to find the local rtx that has a apt
1743
          // which points to the same local codec as the remote one.
1744
          if (!rtxCapabilityMatches(lCodec, rCodec,
1745
              localCapabilities.codecs, remoteCapabilities.codecs)) {
1746
            continue;
1747
          }
1748
        }
1749
        rCodec = JSON.parse(JSON.stringify(rCodec)); // deepcopy
1750
        // number of channels is the highest common number of channels
1751
        rCodec.numChannels = Math.min(lCodec.numChannels,
1752
            rCodec.numChannels);
1753
        // push rCodec so we reply with offerer payload type
1754
        commonCapabilities.codecs.push(rCodec);
1755
1756
        // determine common feedback mechanisms
1757
        rCodec.rtcpFeedback = rCodec.rtcpFeedback.filter(function(fb) {
1758
          for (var j = 0; j < lCodec.rtcpFeedback.length; j++) {
1759
            if (lCodec.rtcpFeedback[j].type === fb.type &&
1760
                lCodec.rtcpFeedback[j].parameter === fb.parameter) {
1761
              return true;
1762
            }
1763
          }
1764
          return false;
1765
        });
1766
        // FIXME: also need to determine .parameters
1767
        //  see https://github.com/openpeer/ortc/issues/569
1768
        break;
1769
      }
1770
    }
1771
  });
1772
1773
  localCapabilities.headerExtensions.forEach(function(lHeaderExtension) {
1774
    for (var i = 0; i < remoteCapabilities.headerExtensions.length;
1775
         i++) {
1776
      var rHeaderExtension = remoteCapabilities.headerExtensions[i];
1777
      if (lHeaderExtension.uri === rHeaderExtension.uri) {
1778
        commonCapabilities.headerExtensions.push(rHeaderExtension);
1779
        break;
1780
      }
1781
    }
1782
  });
1783
1784
  // FIXME: fecMechanisms
1785
  return commonCapabilities;
1786
}
1787
1788
// is action=setLocalDescription with type allowed in signalingState
1789
function isActionAllowedInSignalingState(action, type, signalingState) {
1790
  return {
1791
    offer: {
1792
      setLocalDescription: ['stable', 'have-local-offer'],
1793
      setRemoteDescription: ['stable', 'have-remote-offer']
1794
    },
1795
    answer: {
1796
      setLocalDescription: ['have-remote-offer', 'have-local-pranswer'],
1797
      setRemoteDescription: ['have-local-offer', 'have-remote-pranswer']
1798
    }
1799
  }[type][action].indexOf(signalingState) !== -1;
1800
}
1801
1802
module.exports = function(window, edgeVersion) {
1803
  var RTCPeerConnection = function(config) {
1804
    var self = this;
1805
1806
    var _eventTarget = document.createDocumentFragment();
1807
    ['addEventListener', 'removeEventListener', 'dispatchEvent']
1808
        .forEach(function(method) {
1809
          self[method] = _eventTarget[method].bind(_eventTarget);
1810
        });
1811
1812
    this.needNegotiation = false;
1813
1814
    this.onicecandidate = null;
1815
    this.onaddstream = null;
1816
    this.ontrack = null;
1817
    this.onremovestream = null;
1818
    this.onsignalingstatechange = null;
1819
    this.oniceconnectionstatechange = null;
1820
    this.onicegatheringstatechange = null;
1821
    this.onnegotiationneeded = null;
1822
    this.ondatachannel = null;
1823
    this.canTrickleIceCandidates = null;
1824
1825
    this.localStreams = [];
1826
    this.remoteStreams = [];
1827
    this.getLocalStreams = function() {
1828
      return self.localStreams;
1829
    };
1830
    this.getRemoteStreams = function() {
1831
      return self.remoteStreams;
1832
    };
1833
1834
    this.localDescription = new window.RTCSessionDescription({
1835
      type: '',
1836
      sdp: ''
1837
    });
1838
    this.remoteDescription = new window.RTCSessionDescription({
1839
      type: '',
1840
      sdp: ''
1841
    });
1842
    this.signalingState = 'stable';
1843
    this.iceConnectionState = 'new';
1844
    this.iceGatheringState = 'new';
1845
1846
    this.iceOptions = {
1847
      gatherPolicy: 'all',
1848
      iceServers: []
1849
    };
1850
    if (config && config.iceTransportPolicy) {
1851
      switch (config.iceTransportPolicy) {
1852
        case 'all':
1853
        case 'relay':
1854
          this.iceOptions.gatherPolicy = config.iceTransportPolicy;
1855
          break;
1856
        default:
1857
          // don't set iceTransportPolicy.
1858
          break;
1859
      }
1860
    }
1861
    this.usingBundle = config && config.bundlePolicy === 'max-bundle';
1862
1863
    if (config && config.iceServers) {
1864
      this.iceOptions.iceServers = filterIceServers(config.iceServers,
1865
          edgeVersion);
1866
    }
1867
    this._config = config || {};
1868
1869
    // per-track iceGathers, iceTransports, dtlsTransports, rtpSenders, ...
1870
    // everything that is needed to describe a SDP m-line.
1871
    this.transceivers = [];
1872
1873
    // since the iceGatherer is currently created in createOffer but we
1874
    // must not emit candidates until after setLocalDescription we buffer
1875
    // them in this array.
1876
    this._localIceCandidatesBuffer = [];
1877
1878
    this._sdpSessionId = SDPUtils.generateSessionId();
1879
  };
1880
1881
  RTCPeerConnection.prototype._emitGatheringStateChange = function() {
1882
    var event = new Event('icegatheringstatechange');
0 ignored issues
show
Bug introduced by
The variable Event seems to be never declared. If this is a global, consider adding a /** global: Event */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
1883
    this.dispatchEvent(event);
1884
    if (this.onicegatheringstatechange !== null) {
1885
      this.onicegatheringstatechange(event);
1886
    }
1887
  };
1888
1889
  RTCPeerConnection.prototype._emitBufferedCandidates = function() {
1890
    var self = this;
1891
    var sections = SDPUtils.splitSections(self.localDescription.sdp);
1892
    // FIXME: need to apply ice candidates in a way which is async but
1893
    // in-order
1894
    this._localIceCandidatesBuffer.forEach(function(event) {
1895
      var end = !event.candidate || Object.keys(event.candidate).length === 0;
1896
      if (end) {
1897
        for (var j = 1; j < sections.length; j++) {
1898
          if (sections[j].indexOf('\r\na=end-of-candidates\r\n') === -1) {
1899
            sections[j] += 'a=end-of-candidates\r\n';
1900
          }
1901
        }
1902
      } else {
1903
        sections[event.candidate.sdpMLineIndex + 1] +=
1904
            'a=' + event.candidate.candidate + '\r\n';
1905
      }
1906
      self.localDescription.sdp = sections.join('');
1907
      self.dispatchEvent(event);
1908
      if (self.onicecandidate !== null) {
1909
        self.onicecandidate(event);
1910
      }
1911
      if (!event.candidate && self.iceGatheringState !== 'complete') {
1912
        var complete = self.transceivers.every(function(transceiver) {
1913
          return transceiver.iceGatherer &&
1914
              transceiver.iceGatherer.state === 'completed';
1915
        });
1916
        if (complete && self.iceGatheringStateChange !== 'complete') {
1917
          self.iceGatheringState = 'complete';
1918
          self._emitGatheringStateChange();
1919
        }
1920
      }
1921
    });
1922
    this._localIceCandidatesBuffer = [];
1923
  };
1924
1925
  RTCPeerConnection.prototype.getConfiguration = function() {
1926
    return this._config;
1927
  };
1928
1929
  // internal helper to create a transceiver object.
1930
  // (whih is not yet the same as the WebRTC 1.0 transceiver)
1931
  RTCPeerConnection.prototype._createTransceiver = function(kind) {
1932
    var hasBundleTransport = this.transceivers.length > 0;
1933
    var transceiver = {
1934
      track: null,
1935
      iceGatherer: null,
1936
      iceTransport: null,
1937
      dtlsTransport: null,
1938
      localCapabilities: null,
1939
      remoteCapabilities: null,
1940
      rtpSender: null,
1941
      rtpReceiver: null,
1942
      kind: kind,
1943
      mid: null,
1944
      sendEncodingParameters: null,
1945
      recvEncodingParameters: null,
1946
      stream: null,
1947
      wantReceive: true
1948
    };
1949
    if (this.usingBundle && hasBundleTransport) {
1950
      transceiver.iceTransport = this.transceivers[0].iceTransport;
1951
      transceiver.dtlsTransport = this.transceivers[0].dtlsTransport;
1952
    } else {
1953
      var transports = this._createIceAndDtlsTransports();
1954
      transceiver.iceTransport = transports.iceTransport;
1955
      transceiver.dtlsTransport = transports.dtlsTransport;
1956
    }
1957
    this.transceivers.push(transceiver);
1958
    return transceiver;
1959
  };
1960
1961
  RTCPeerConnection.prototype.addTrack = function(track, stream) {
1962
    var transceiver;
1963
    for (var i = 0; i < this.transceivers.length; i++) {
1964
      if (!this.transceivers[i].track &&
1965
          this.transceivers[i].kind === track.kind) {
1966
        transceiver = this.transceivers[i];
1967
      }
1968
    }
1969
    if (!transceiver) {
1970
      transceiver = this._createTransceiver(track.kind);
1971
    }
1972
1973
    transceiver.track = track;
1974
    transceiver.stream = stream;
1975
    transceiver.rtpSender = new window.RTCRtpSender(track,
1976
        transceiver.dtlsTransport);
1977
1978
    this._maybeFireNegotiationNeeded();
1979
    return transceiver.rtpSender;
1980
  };
1981
1982
  RTCPeerConnection.prototype.addStream = function(stream) {
1983
    var self = this;
1984
    if (edgeVersion >= 15025) {
1985
      this.localStreams.push(stream);
1986
      stream.getTracks().forEach(function(track) {
1987
        self.addTrack(track, stream);
1988
      });
1989
    } else {
1990
      // Clone is necessary for local demos mostly, attaching directly
1991
      // to two different senders does not work (build 10547).
1992
      // Fixed in 15025 (or earlier)
1993
      var clonedStream = stream.clone();
1994
      stream.getTracks().forEach(function(track, idx) {
1995
        var clonedTrack = clonedStream.getTracks()[idx];
1996
        track.addEventListener('enabled', function(event) {
1997
          clonedTrack.enabled = event.enabled;
1998
        });
1999
      });
2000
      clonedStream.getTracks().forEach(function(track) {
2001
        self.addTrack(track, clonedStream);
2002
      });
2003
      this.localStreams.push(clonedStream);
2004
    }
2005
    this._maybeFireNegotiationNeeded();
2006
  };
2007
2008
  RTCPeerConnection.prototype.removeStream = function(stream) {
2009
    var idx = this.localStreams.indexOf(stream);
2010
    if (idx > -1) {
2011
      this.localStreams.splice(idx, 1);
2012
      this._maybeFireNegotiationNeeded();
2013
    }
2014
  };
2015
2016
  RTCPeerConnection.prototype.getSenders = function() {
2017
    return this.transceivers.filter(function(transceiver) {
2018
      return !!transceiver.rtpSender;
2019
    })
2020
    .map(function(transceiver) {
2021
      return transceiver.rtpSender;
2022
    });
2023
  };
2024
2025
  RTCPeerConnection.prototype.getReceivers = function() {
2026
    return this.transceivers.filter(function(transceiver) {
2027
      return !!transceiver.rtpReceiver;
2028
    })
2029
    .map(function(transceiver) {
2030
      return transceiver.rtpReceiver;
2031
    });
2032
  };
2033
2034
  // Create ICE gatherer and hook it up.
2035
  RTCPeerConnection.prototype._createIceGatherer = function(mid,
2036
      sdpMLineIndex) {
2037
    var self = this;
2038
    var iceGatherer = new window.RTCIceGatherer(self.iceOptions);
2039
    iceGatherer.onlocalcandidate = function(evt) {
2040
      var event = new Event('icecandidate');
0 ignored issues
show
Bug introduced by
The variable Event seems to be never declared. If this is a global, consider adding a /** global: Event */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
2041
      event.candidate = {sdpMid: mid, sdpMLineIndex: sdpMLineIndex};
2042
2043
      var cand = evt.candidate;
2044
      var end = !cand || Object.keys(cand).length === 0;
2045
      // Edge emits an empty object for RTCIceCandidateComplete‥
2046
      if (end) {
2047
        // polyfill since RTCIceGatherer.state is not implemented in
2048
        // Edge 10547 yet.
2049
        if (iceGatherer.state === undefined) {
2050
          iceGatherer.state = 'completed';
2051
        }
2052
      } else {
2053
        // RTCIceCandidate doesn't have a component, needs to be added
2054
        cand.component = 1;
2055
        event.candidate.candidate = SDPUtils.writeCandidate(cand);
2056
      }
2057
2058
      // update local description.
2059
      var sections = SDPUtils.splitSections(self.localDescription.sdp);
2060
      if (!end) {
2061
        sections[event.candidate.sdpMLineIndex + 1] +=
2062
            'a=' + event.candidate.candidate + '\r\n';
2063
      } else {
2064
        sections[event.candidate.sdpMLineIndex + 1] +=
2065
            'a=end-of-candidates\r\n';
2066
      }
2067
      self.localDescription.sdp = sections.join('');
2068
      var transceivers = self._pendingOffer ? self._pendingOffer :
2069
          self.transceivers;
2070
      var complete = transceivers.every(function(transceiver) {
2071
        return transceiver.iceGatherer &&
2072
            transceiver.iceGatherer.state === 'completed';
2073
      });
2074
2075
      // Emit candidate if localDescription is set.
2076
      // Also emits null candidate when all gatherers are complete.
2077
      switch (self.iceGatheringState) {
2078
        case 'new':
2079
          if (!end) {
2080
            self._localIceCandidatesBuffer.push(event);
2081
          }
2082
          if (end && complete) {
2083
            self._localIceCandidatesBuffer.push(
2084
                new Event('icecandidate'));
0 ignored issues
show
Bug introduced by
The variable Event seems to be never declared. If this is a global, consider adding a /** global: Event */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
2085
          }
2086
          break;
2087
        case 'gathering':
2088
          self._emitBufferedCandidates();
2089
          if (!end) {
2090
            self.dispatchEvent(event);
2091
            if (self.onicecandidate !== null) {
2092
              self.onicecandidate(event);
2093
            }
2094
          }
2095
          if (complete) {
2096
            self.dispatchEvent(new Event('icecandidate'));
2097
            if (self.onicecandidate !== null) {
2098
              self.onicecandidate(new Event('icecandidate'));
2099
            }
2100
            self.iceGatheringState = 'complete';
2101
            self._emitGatheringStateChange();
2102
          }
2103
          break;
2104
        case 'complete':
2105
          // should not happen... currently!
2106
          break;
2107
        default: // no-op.
2108
          break;
2109
      }
2110
    };
2111
    return iceGatherer;
2112
  };
2113
2114
  // Create ICE transport and DTLS transport.
2115
  RTCPeerConnection.prototype._createIceAndDtlsTransports = function() {
2116
    var self = this;
2117
    var iceTransport = new window.RTCIceTransport(null);
2118
    iceTransport.onicestatechange = function() {
2119
      self._updateConnectionState();
2120
    };
2121
2122
    var dtlsTransport = new window.RTCDtlsTransport(iceTransport);
2123
    dtlsTransport.ondtlsstatechange = function() {
2124
      self._updateConnectionState();
2125
    };
2126
    dtlsTransport.onerror = function() {
2127
      // onerror does not set state to failed by itself.
2128
      Object.defineProperty(dtlsTransport, 'state',
2129
          {value: 'failed', writable: true});
2130
      self._updateConnectionState();
2131
    };
2132
2133
    return {
2134
      iceTransport: iceTransport,
2135
      dtlsTransport: dtlsTransport
2136
    };
2137
  };
2138
2139
  // Destroy ICE gatherer, ICE transport and DTLS transport.
2140
  // Without triggering the callbacks.
2141
  RTCPeerConnection.prototype._disposeIceAndDtlsTransports = function(
2142
      sdpMLineIndex) {
2143
    var iceGatherer = this.transceivers[sdpMLineIndex].iceGatherer;
2144
    if (iceGatherer) {
2145
      delete iceGatherer.onlocalcandidate;
2146
      delete this.transceivers[sdpMLineIndex].iceGatherer;
2147
    }
2148
    var iceTransport = this.transceivers[sdpMLineIndex].iceTransport;
2149
    if (iceTransport) {
2150
      delete iceTransport.onicestatechange;
2151
      delete this.transceivers[sdpMLineIndex].iceTransport;
2152
    }
2153
    var dtlsTransport = this.transceivers[sdpMLineIndex].dtlsTransport;
2154
    if (dtlsTransport) {
2155
      delete dtlsTransport.ondtlsstatechange;
2156
      delete dtlsTransport.onerror;
2157
      delete this.transceivers[sdpMLineIndex].dtlsTransport;
2158
    }
2159
  };
2160
2161
  // Start the RTP Sender and Receiver for a transceiver.
2162
  RTCPeerConnection.prototype._transceive = function(transceiver,
2163
      send, recv) {
2164
    var params = getCommonCapabilities(transceiver.localCapabilities,
2165
        transceiver.remoteCapabilities);
2166
    if (send && transceiver.rtpSender) {
2167
      params.encodings = transceiver.sendEncodingParameters;
2168
      params.rtcp = {
2169
        cname: SDPUtils.localCName,
2170
        compound: transceiver.rtcpParameters.compound
2171
      };
2172
      if (transceiver.recvEncodingParameters.length) {
2173
        params.rtcp.ssrc = transceiver.recvEncodingParameters[0].ssrc;
2174
      }
2175
      transceiver.rtpSender.send(params);
2176
    }
2177
    if (recv && transceiver.rtpReceiver) {
2178
      // remove RTX field in Edge 14942
2179
      if (transceiver.kind === 'video'
2180
          && transceiver.recvEncodingParameters
2181
          && edgeVersion < 15019) {
2182
        transceiver.recvEncodingParameters.forEach(function(p) {
2183
          delete p.rtx;
2184
        });
2185
      }
2186
      params.encodings = transceiver.recvEncodingParameters;
2187
      params.rtcp = {
2188
        cname: transceiver.rtcpParameters.cname,
2189
        compound: transceiver.rtcpParameters.compound
2190
      };
2191
      if (transceiver.sendEncodingParameters.length) {
2192
        params.rtcp.ssrc = transceiver.sendEncodingParameters[0].ssrc;
2193
      }
2194
      transceiver.rtpReceiver.receive(params);
2195
    }
2196
  };
2197
2198
  RTCPeerConnection.prototype.setLocalDescription = function(description) {
2199
    var self = this;
2200
2201
    if (!isActionAllowedInSignalingState('setLocalDescription',
2202
        description.type, this.signalingState)) {
2203
      var e = new Error('Can not set local ' + description.type +
2204
          ' in state ' + this.signalingState);
2205
      e.name = 'InvalidStateError';
2206
      if (arguments.length > 2 && typeof arguments[2] === 'function') {
2207
        window.setTimeout(arguments[2], 0, e);
2208
      }
2209
      return Promise.reject(e);
2210
    }
2211
2212
    var sections;
2213
    var sessionpart;
2214
    if (description.type === 'offer') {
2215
      // FIXME: What was the purpose of this empty if statement?
2216
      // if (!this._pendingOffer) {
2217
      // } else {
2218
      if (this._pendingOffer) {
2219
        // VERY limited support for SDP munging. Limited to:
2220
        // * changing the order of codecs
2221
        sections = SDPUtils.splitSections(description.sdp);
2222
        sessionpart = sections.shift();
0 ignored issues
show
Unused Code introduced by
The assignment to variable sessionpart seems to be never used. Consider removing it.
Loading history...
2223
        sections.forEach(function(mediaSection, sdpMLineIndex) {
2224
          var caps = SDPUtils.parseRtpParameters(mediaSection);
2225
          self._pendingOffer[sdpMLineIndex].localCapabilities = caps;
2226
        });
2227
        this.transceivers = this._pendingOffer;
2228
        delete this._pendingOffer;
2229
      }
2230
    } else if (description.type === 'answer') {
2231
      sections = SDPUtils.splitSections(self.remoteDescription.sdp);
2232
      sessionpart = sections.shift();
2233
      var isIceLite = SDPUtils.matchPrefix(sessionpart,
2234
          'a=ice-lite').length > 0;
2235
      sections.forEach(function(mediaSection, sdpMLineIndex) {
2236
        var transceiver = self.transceivers[sdpMLineIndex];
2237
        var iceGatherer = transceiver.iceGatherer;
2238
        var iceTransport = transceiver.iceTransport;
2239
        var dtlsTransport = transceiver.dtlsTransport;
2240
        var localCapabilities = transceiver.localCapabilities;
2241
        var remoteCapabilities = transceiver.remoteCapabilities;
2242
2243
        var rejected = SDPUtils.isRejected(mediaSection);
2244
2245
        if (!rejected && !transceiver.isDatachannel) {
2246
          var remoteIceParameters = SDPUtils.getIceParameters(
2247
              mediaSection, sessionpart);
2248
          var remoteDtlsParameters = SDPUtils.getDtlsParameters(
2249
              mediaSection, sessionpart);
2250
          if (isIceLite) {
2251
            remoteDtlsParameters.role = 'server';
2252
          }
2253
2254
          if (!self.usingBundle || sdpMLineIndex === 0) {
2255
            iceTransport.start(iceGatherer, remoteIceParameters,
2256
                isIceLite ? 'controlling' : 'controlled');
2257
            dtlsTransport.start(remoteDtlsParameters);
2258
          }
2259
2260
          // Calculate intersection of capabilities.
2261
          var params = getCommonCapabilities(localCapabilities,
2262
              remoteCapabilities);
2263
2264
          // Start the RTCRtpSender. The RTCRtpReceiver for this
2265
          // transceiver has already been started in setRemoteDescription.
2266
          self._transceive(transceiver,
2267
              params.codecs.length > 0,
2268
              false);
2269
        }
2270
      });
2271
    }
2272
2273
    this.localDescription = {
2274
      type: description.type,
2275
      sdp: description.sdp
2276
    };
2277
    switch (description.type) {
2278
      case 'offer':
2279
        this._updateSignalingState('have-local-offer');
2280
        break;
2281
      case 'answer':
2282
        this._updateSignalingState('stable');
2283
        break;
2284
      default:
2285
        throw new TypeError('unsupported type "' + description.type +
2286
            '"');
2287
    }
2288
2289
    // If a success callback was provided, emit ICE candidates after it
2290
    // has been executed. Otherwise, emit callback after the Promise is
2291
    // resolved.
2292
    var hasCallback = arguments.length > 1 &&
2293
      typeof arguments[1] === 'function';
2294
    if (hasCallback) {
2295
      var cb = arguments[1];
2296
      window.setTimeout(function() {
2297
        cb();
2298
        if (self.iceGatheringState === 'new') {
2299
          self.iceGatheringState = 'gathering';
2300
          self._emitGatheringStateChange();
2301
        }
2302
        self._emitBufferedCandidates();
2303
      }, 0);
2304
    }
2305
    var p = Promise.resolve();
2306
    p.then(function() {
2307
      if (!hasCallback) {
2308
        if (self.iceGatheringState === 'new') {
2309
          self.iceGatheringState = 'gathering';
2310
          self._emitGatheringStateChange();
2311
        }
2312
        // Usually candidates will be emitted earlier.
2313
        window.setTimeout(self._emitBufferedCandidates.bind(self), 500);
2314
      }
2315
    });
2316
    return p;
2317
  };
2318
2319
  RTCPeerConnection.prototype.setRemoteDescription = function(description) {
2320
    var self = this;
2321
2322
    if (!isActionAllowedInSignalingState('setRemoteDescription',
2323
        description.type, this.signalingState)) {
2324
      var e = new Error('Can not set remote ' + description.type +
2325
          ' in state ' + this.signalingState);
2326
      e.name = 'InvalidStateError';
2327
      if (arguments.length > 2 && typeof arguments[2] === 'function') {
2328
        window.setTimeout(arguments[2], 0, e);
2329
      }
2330
      return Promise.reject(e);
2331
    }
2332
2333
    var streams = {};
2334
    var receiverList = [];
2335
    var sections = SDPUtils.splitSections(description.sdp);
2336
    var sessionpart = sections.shift();
2337
    var isIceLite = SDPUtils.matchPrefix(sessionpart,
2338
        'a=ice-lite').length > 0;
2339
    var usingBundle = SDPUtils.matchPrefix(sessionpart,
2340
        'a=group:BUNDLE ').length > 0;
2341
    this.usingBundle = usingBundle;
2342
    var iceOptions = SDPUtils.matchPrefix(sessionpart,
2343
        'a=ice-options:')[0];
2344
    if (iceOptions) {
2345
      this.canTrickleIceCandidates = iceOptions.substr(14).split(' ')
2346
          .indexOf('trickle') >= 0;
2347
    } else {
2348
      this.canTrickleIceCandidates = false;
2349
    }
2350
2351
    sections.forEach(function(mediaSection, sdpMLineIndex) {
2352
      var lines = SDPUtils.splitLines(mediaSection);
2353
      var kind = SDPUtils.getKind(mediaSection);
2354
      var rejected = SDPUtils.isRejected(mediaSection);
2355
      var protocol = lines[0].substr(2).split(' ')[2];
2356
2357
      var direction = SDPUtils.getDirection(mediaSection, sessionpart);
2358
      var remoteMsid = SDPUtils.parseMsid(mediaSection);
2359
2360
      var mid = SDPUtils.getMid(mediaSection) || SDPUtils.generateIdentifier();
2361
2362
      // Reject datachannels which are not implemented yet.
2363
      if (kind === 'application' && protocol === 'DTLS/SCTP') {
2364
        self.transceivers[sdpMLineIndex] = {
2365
          mid: mid,
2366
          isDatachannel: true
2367
        };
2368
        return;
2369
      }
2370
2371
      var transceiver;
2372
      var iceGatherer;
2373
      var iceTransport;
2374
      var dtlsTransport;
2375
      var rtpReceiver;
2376
      var sendEncodingParameters;
2377
      var recvEncodingParameters;
2378
      var localCapabilities;
2379
2380
      var track;
2381
      // FIXME: ensure the mediaSection has rtcp-mux set.
2382
      var remoteCapabilities = SDPUtils.parseRtpParameters(mediaSection);
2383
      var remoteIceParameters;
2384
      var remoteDtlsParameters;
2385
      if (!rejected) {
2386
        remoteIceParameters = SDPUtils.getIceParameters(mediaSection,
2387
            sessionpart);
2388
        remoteDtlsParameters = SDPUtils.getDtlsParameters(mediaSection,
2389
            sessionpart);
2390
        remoteDtlsParameters.role = 'client';
2391
      }
2392
      recvEncodingParameters =
2393
          SDPUtils.parseRtpEncodingParameters(mediaSection);
2394
2395
      var rtcpParameters = SDPUtils.parseRtcpParameters(mediaSection);
2396
2397
      var isComplete = SDPUtils.matchPrefix(mediaSection,
2398
          'a=end-of-candidates', sessionpart).length > 0;
2399
      var cands = SDPUtils.matchPrefix(mediaSection, 'a=candidate:')
2400
          .map(function(cand) {
2401
            return SDPUtils.parseCandidate(cand);
2402
          })
2403
          .filter(function(cand) {
2404
            return cand.component === '1' || cand.component === 1;
2405
          });
2406
2407
      // Check if we can use BUNDLE and dispose transports.
2408
      if ((description.type === 'offer' || description.type === 'answer') &&
2409
          !rejected && usingBundle && sdpMLineIndex > 0 &&
2410
          self.transceivers[sdpMLineIndex]) {
2411
        self._disposeIceAndDtlsTransports(sdpMLineIndex);
2412
        self.transceivers[sdpMLineIndex].iceGatherer =
2413
            self.transceivers[0].iceGatherer;
2414
        self.transceivers[sdpMLineIndex].iceTransport =
2415
            self.transceivers[0].iceTransport;
2416
        self.transceivers[sdpMLineIndex].dtlsTransport =
2417
            self.transceivers[0].dtlsTransport;
2418
        if (self.transceivers[sdpMLineIndex].rtpSender) {
2419
          self.transceivers[sdpMLineIndex].rtpSender.setTransport(
2420
              self.transceivers[0].dtlsTransport);
2421
        }
2422
        if (self.transceivers[sdpMLineIndex].rtpReceiver) {
2423
          self.transceivers[sdpMLineIndex].rtpReceiver.setTransport(
2424
              self.transceivers[0].dtlsTransport);
2425
        }
2426
      }
2427
      if (description.type === 'offer' && !rejected) {
2428
        transceiver = self.transceivers[sdpMLineIndex] ||
2429
            self._createTransceiver(kind);
2430
        transceiver.mid = mid;
2431
2432
        if (!transceiver.iceGatherer) {
2433
          transceiver.iceGatherer = usingBundle && sdpMLineIndex > 0 ?
2434
              self.transceivers[0].iceGatherer :
2435
              self._createIceGatherer(mid, sdpMLineIndex);
2436
        }
2437
2438
        if (isComplete && cands.length &&
2439
            (!usingBundle || sdpMLineIndex === 0)) {
2440
          transceiver.iceTransport.setRemoteCandidates(cands);
2441
        }
2442
2443
        localCapabilities = window.RTCRtpReceiver.getCapabilities(kind);
2444
2445
        // filter RTX until additional stuff needed for RTX is implemented
2446
        // in adapter.js
2447
        if (edgeVersion < 15019) {
2448
          localCapabilities.codecs = localCapabilities.codecs.filter(
2449
              function(codec) {
2450
                return codec.name !== 'rtx';
2451
              });
2452
        }
2453
2454
        sendEncodingParameters = [{
2455
          ssrc: (2 * sdpMLineIndex + 2) * 1001
2456
        }];
2457
2458
        if (direction === 'sendrecv' || direction === 'sendonly') {
2459
          rtpReceiver = new window.RTCRtpReceiver(transceiver.dtlsTransport,
2460
              kind);
2461
2462
          track = rtpReceiver.track;
2463
          // FIXME: does not work with Plan B.
2464
          if (remoteMsid) {
2465
            if (!streams[remoteMsid.stream]) {
2466
              streams[remoteMsid.stream] = new window.MediaStream();
2467
              Object.defineProperty(streams[remoteMsid.stream], 'id', {
2468
                get: function() {
2469
                  return remoteMsid.stream;
2470
                }
2471
              });
2472
            }
2473
            Object.defineProperty(track, 'id', {
2474
              get: function() {
2475
                return remoteMsid.track;
2476
              }
2477
            });
2478
            streams[remoteMsid.stream].addTrack(track);
2479
            receiverList.push([track, rtpReceiver,
2480
              streams[remoteMsid.stream]]);
2481
          } else {
2482
            if (!streams.default) {
2483
              streams.default = new window.MediaStream();
2484
            }
2485
            streams.default.addTrack(track);
2486
            receiverList.push([track, rtpReceiver, streams.default]);
2487
          }
2488
        }
2489
2490
        transceiver.localCapabilities = localCapabilities;
2491
        transceiver.remoteCapabilities = remoteCapabilities;
2492
        transceiver.rtpReceiver = rtpReceiver;
0 ignored issues
show
Bug introduced by
The variable rtpReceiver does not seem to be initialized in case direction === "sendrecv"...irection === "sendonly" on line 2458 is false. Are you sure this can never be the case?
Loading history...
2493
        transceiver.rtcpParameters = rtcpParameters;
2494
        transceiver.sendEncodingParameters = sendEncodingParameters;
2495
        transceiver.recvEncodingParameters = recvEncodingParameters;
2496
2497
        // Start the RTCRtpReceiver now. The RTPSender is started in
2498
        // setLocalDescription.
2499
        self._transceive(self.transceivers[sdpMLineIndex],
2500
            false,
2501
            direction === 'sendrecv' || direction === 'sendonly');
2502
      } else if (description.type === 'answer' && !rejected) {
2503
        transceiver = self.transceivers[sdpMLineIndex];
2504
        iceGatherer = transceiver.iceGatherer;
2505
        iceTransport = transceiver.iceTransport;
2506
        dtlsTransport = transceiver.dtlsTransport;
2507
        rtpReceiver = transceiver.rtpReceiver;
2508
        sendEncodingParameters = transceiver.sendEncodingParameters;
0 ignored issues
show
Unused Code introduced by
The assignment to variable sendEncodingParameters seems to be never used. Consider removing it.
Loading history...
2509
        localCapabilities = transceiver.localCapabilities;
0 ignored issues
show
Unused Code introduced by
The assignment to variable localCapabilities seems to be never used. Consider removing it.
Loading history...
2510
2511
        self.transceivers[sdpMLineIndex].recvEncodingParameters =
2512
            recvEncodingParameters;
2513
        self.transceivers[sdpMLineIndex].remoteCapabilities =
2514
            remoteCapabilities;
2515
        self.transceivers[sdpMLineIndex].rtcpParameters = rtcpParameters;
2516
2517
        if (!usingBundle || sdpMLineIndex === 0) {
2518
          if ((isIceLite || isComplete) && cands.length) {
2519
            iceTransport.setRemoteCandidates(cands);
2520
          }
2521
          iceTransport.start(iceGatherer, remoteIceParameters,
0 ignored issues
show
Bug introduced by
The variable remoteIceParameters does not seem to be initialized in case !rejected on line 2385 is false. Are you sure the function start handles undefined variables?
Loading history...
2522
              'controlling');
2523
          dtlsTransport.start(remoteDtlsParameters);
0 ignored issues
show
Bug introduced by
The variable remoteDtlsParameters does not seem to be initialized in case !rejected on line 2385 is false. Are you sure the function start handles undefined variables?
Loading history...
2524
        }
2525
2526
        self._transceive(transceiver,
2527
            direction === 'sendrecv' || direction === 'recvonly',
2528
            direction === 'sendrecv' || direction === 'sendonly');
2529
2530
        if (rtpReceiver &&
2531
            (direction === 'sendrecv' || direction === 'sendonly')) {
2532
          track = rtpReceiver.track;
2533
          if (remoteMsid) {
2534
            if (!streams[remoteMsid.stream]) {
2535
              streams[remoteMsid.stream] = new window.MediaStream();
2536
            }
2537
            streams[remoteMsid.stream].addTrack(track);
2538
            receiverList.push([track, rtpReceiver, streams[remoteMsid.stream]]);
2539
          } else {
2540
            if (!streams.default) {
2541
              streams.default = new window.MediaStream();
2542
            }
2543
            streams.default.addTrack(track);
2544
            receiverList.push([track, rtpReceiver, streams.default]);
2545
          }
2546
        } else {
2547
          // FIXME: actually the receiver should be created later.
2548
          delete transceiver.rtpReceiver;
2549
        }
2550
      }
2551
    });
2552
2553
    this.remoteDescription = {
2554
      type: description.type,
2555
      sdp: description.sdp
2556
    };
2557
    switch (description.type) {
2558
      case 'offer':
2559
        this._updateSignalingState('have-remote-offer');
2560
        break;
2561
      case 'answer':
2562
        this._updateSignalingState('stable');
2563
        break;
2564
      default:
2565
        throw new TypeError('unsupported type "' + description.type +
2566
            '"');
2567
    }
2568
    Object.keys(streams).forEach(function(sid) {
2569
      var stream = streams[sid];
2570
      if (stream.getTracks().length) {
2571
        self.remoteStreams.push(stream);
2572
        var event = new Event('addstream');
0 ignored issues
show
Bug introduced by
The variable Event seems to be never declared. If this is a global, consider adding a /** global: Event */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
2573
        event.stream = stream;
2574
        self.dispatchEvent(event);
2575
        if (self.onaddstream !== null) {
2576
          window.setTimeout(function() {
2577
            self.onaddstream(event);
2578
          }, 0);
2579
        }
2580
2581
        receiverList.forEach(function(item) {
2582
          var track = item[0];
2583
          var receiver = item[1];
2584
          if (stream.id !== item[2].id) {
2585
            return;
2586
          }
2587
          var trackEvent = new Event('track');
0 ignored issues
show
Bug introduced by
The variable Event seems to be never declared. If this is a global, consider adding a /** global: Event */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
2588
          trackEvent.track = track;
2589
          trackEvent.receiver = receiver;
2590
          trackEvent.streams = [stream];
2591
          self.dispatchEvent(trackEvent);
2592
          if (self.ontrack !== null) {
2593
            window.setTimeout(function() {
2594
              self.ontrack(trackEvent);
2595
            }, 0);
2596
          }
2597
        });
2598
      }
2599
    });
2600
2601
    // check whether addIceCandidate({}) was called within four seconds after
2602
    // setRemoteDescription.
2603
    window.setTimeout(function() {
2604
      if (!(self && self.transceivers)) {
2605
        return;
2606
      }
2607
      self.transceivers.forEach(function(transceiver) {
2608
        if (transceiver.iceTransport &&
2609
            transceiver.iceTransport.state === 'new' &&
2610
            transceiver.iceTransport.getRemoteCandidates().length > 0) {
2611
          console.warn('Timeout for addRemoteCandidate. Consider sending ' +
2612
              'an end-of-candidates notification');
2613
          transceiver.iceTransport.addRemoteCandidate({});
2614
        }
2615
      });
2616
    }, 4000);
2617
2618
    if (arguments.length > 1 && typeof arguments[1] === 'function') {
2619
      window.setTimeout(arguments[1], 0);
2620
    }
2621
    return Promise.resolve();
2622
  };
2623
2624
  RTCPeerConnection.prototype.close = function() {
2625
    this.transceivers.forEach(function(transceiver) {
2626
      /* not yet
2627
      if (transceiver.iceGatherer) {
2628
        transceiver.iceGatherer.close();
2629
      }
2630
      */
2631
      if (transceiver.iceTransport) {
2632
        transceiver.iceTransport.stop();
2633
      }
2634
      if (transceiver.dtlsTransport) {
2635
        transceiver.dtlsTransport.stop();
2636
      }
2637
      if (transceiver.rtpSender) {
2638
        transceiver.rtpSender.stop();
2639
      }
2640
      if (transceiver.rtpReceiver) {
2641
        transceiver.rtpReceiver.stop();
2642
      }
2643
    });
2644
    // FIXME: clean up tracks, local streams, remote streams, etc
2645
    this._updateSignalingState('closed');
2646
  };
2647
2648
  // Update the signaling state.
2649
  RTCPeerConnection.prototype._updateSignalingState = function(newState) {
2650
    this.signalingState = newState;
2651
    var event = new Event('signalingstatechange');
0 ignored issues
show
Bug introduced by
The variable Event seems to be never declared. If this is a global, consider adding a /** global: Event */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
2652
    this.dispatchEvent(event);
2653
    if (this.onsignalingstatechange !== null) {
2654
      this.onsignalingstatechange(event);
2655
    }
2656
  };
2657
2658
  // Determine whether to fire the negotiationneeded event.
2659
  RTCPeerConnection.prototype._maybeFireNegotiationNeeded = function() {
2660
    var self = this;
2661
    if (this.signalingState !== 'stable' || this.needNegotiation === true) {
2662
      return;
2663
    }
2664
    this.needNegotiation = true;
2665
    window.setTimeout(function() {
2666
      if (self.needNegotiation === false) {
2667
        return;
2668
      }
2669
      self.needNegotiation = false;
2670
      var event = new Event('negotiationneeded');
0 ignored issues
show
Bug introduced by
The variable Event seems to be never declared. If this is a global, consider adding a /** global: Event */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
2671
      self.dispatchEvent(event);
2672
      if (self.onnegotiationneeded !== null) {
2673
        self.onnegotiationneeded(event);
2674
      }
2675
    }, 0);
2676
  };
2677
2678
  // Update the connection state.
2679
  RTCPeerConnection.prototype._updateConnectionState = function() {
2680
    var self = this;
2681
    var newState;
2682
    var states = {
2683
      'new': 0,
2684
      closed: 0,
2685
      connecting: 0,
2686
      checking: 0,
2687
      connected: 0,
2688
      completed: 0,
2689
      disconnected: 0,
2690
      failed: 0
2691
    };
2692
    this.transceivers.forEach(function(transceiver) {
2693
      states[transceiver.iceTransport.state]++;
2694
      states[transceiver.dtlsTransport.state]++;
2695
    });
2696
    // ICETransport.completed and connected are the same for this purpose.
2697
    states.connected += states.completed;
2698
2699
    newState = 'new';
2700
    if (states.failed > 0) {
2701
      newState = 'failed';
2702
    } else if (states.connecting > 0 || states.checking > 0) {
2703
      newState = 'connecting';
2704
    } else if (states.disconnected > 0) {
2705
      newState = 'disconnected';
2706
    } else if (states.new > 0) {
2707
      newState = 'new';
2708
    } else if (states.connected > 0 || states.completed > 0) {
2709
      newState = 'connected';
2710
    }
2711
2712
    if (newState !== self.iceConnectionState) {
2713
      self.iceConnectionState = newState;
2714
      var event = new Event('iceconnectionstatechange');
0 ignored issues
show
Bug introduced by
The variable Event seems to be never declared. If this is a global, consider adding a /** global: Event */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
2715
      this.dispatchEvent(event);
2716
      if (this.oniceconnectionstatechange !== null) {
2717
        this.oniceconnectionstatechange(event);
2718
      }
2719
    }
2720
  };
2721
2722
  RTCPeerConnection.prototype.createOffer = function() {
2723
    var self = this;
2724
    if (this._pendingOffer) {
2725
      throw new Error('createOffer called while there is a pending offer.');
2726
    }
2727
    var offerOptions;
2728
    if (arguments.length === 1 && typeof arguments[0] !== 'function') {
2729
      offerOptions = arguments[0];
2730
    } else if (arguments.length === 3) {
2731
      offerOptions = arguments[2];
2732
    }
2733
2734
    var numAudioTracks = this.transceivers.filter(function(t) {
2735
      return t.kind === 'audio';
2736
    }).length;
2737
    var numVideoTracks = this.transceivers.filter(function(t) {
2738
      return t.kind === 'video';
2739
    }).length;
2740
2741
    // Determine number of audio and video tracks we need to send/recv.
2742
    if (offerOptions) {
2743
      // Reject Chrome legacy constraints.
2744
      if (offerOptions.mandatory || offerOptions.optional) {
2745
        throw new TypeError(
2746
            'Legacy mandatory/optional constraints not supported.');
2747
      }
2748
      if (offerOptions.offerToReceiveAudio !== undefined) {
2749
        if (offerOptions.offerToReceiveAudio === true) {
2750
          numAudioTracks = 1;
2751
        } else if (offerOptions.offerToReceiveAudio === false) {
2752
          numAudioTracks = 0;
2753
        } else {
2754
          numAudioTracks = offerOptions.offerToReceiveAudio;
2755
        }
2756
      }
2757
      if (offerOptions.offerToReceiveVideo !== undefined) {
2758
        if (offerOptions.offerToReceiveVideo === true) {
2759
          numVideoTracks = 1;
2760
        } else if (offerOptions.offerToReceiveVideo === false) {
2761
          numVideoTracks = 0;
2762
        } else {
2763
          numVideoTracks = offerOptions.offerToReceiveVideo;
2764
        }
2765
      }
2766
    }
2767
2768
    this.transceivers.forEach(function(transceiver) {
2769
      if (transceiver.kind === 'audio') {
2770
        numAudioTracks--;
2771
        if (numAudioTracks < 0) {
2772
          transceiver.wantReceive = false;
2773
        }
2774
      } else if (transceiver.kind === 'video') {
2775
        numVideoTracks--;
2776
        if (numVideoTracks < 0) {
2777
          transceiver.wantReceive = false;
2778
        }
2779
      }
2780
    });
2781
2782
    // Create M-lines for recvonly streams.
2783
    while (numAudioTracks > 0 || numVideoTracks > 0) {
2784
      if (numAudioTracks > 0) {
2785
        this._createTransceiver('audio');
2786
        numAudioTracks--;
2787
      }
2788
      if (numVideoTracks > 0) {
2789
        this._createTransceiver('video');
2790
        numVideoTracks--;
2791
      }
2792
    }
2793
    // reorder tracks
2794
    var transceivers = sortTracks(this.transceivers);
2795
2796
    var sdp = SDPUtils.writeSessionBoilerplate(this._sdpSessionId);
2797
    transceivers.forEach(function(transceiver, sdpMLineIndex) {
2798
      // For each track, create an ice gatherer, ice transport,
2799
      // dtls transport, potentially rtpsender and rtpreceiver.
2800
      var track = transceiver.track;
2801
      var kind = transceiver.kind;
2802
      var mid = SDPUtils.generateIdentifier();
2803
      transceiver.mid = mid;
2804
2805
      if (!transceiver.iceGatherer) {
2806
        transceiver.iceGatherer = self.usingBundle && sdpMLineIndex > 0 ?
2807
            transceivers[0].iceGatherer :
2808
            self._createIceGatherer(mid, sdpMLineIndex);
2809
      }
2810
2811
      var localCapabilities = window.RTCRtpSender.getCapabilities(kind);
2812
      // filter RTX until additional stuff needed for RTX is implemented
2813
      // in adapter.js
2814
      if (edgeVersion < 15019) {
2815
        localCapabilities.codecs = localCapabilities.codecs.filter(
2816
            function(codec) {
2817
              return codec.name !== 'rtx';
2818
            });
2819
      }
2820
      localCapabilities.codecs.forEach(function(codec) {
2821
        // work around https://bugs.chromium.org/p/webrtc/issues/detail?id=6552
2822
        // by adding level-asymmetry-allowed=1
2823
        if (codec.name === 'H264' &&
2824
            codec.parameters['level-asymmetry-allowed'] === undefined) {
2825
          codec.parameters['level-asymmetry-allowed'] = '1';
2826
        }
2827
      });
2828
2829
      // generate an ssrc now, to be used later in rtpSender.send
2830
      var sendEncodingParameters = [{
2831
        ssrc: (2 * sdpMLineIndex + 1) * 1001
2832
      }];
2833
      if (track) {
2834
        // add RTX
2835
        if (edgeVersion >= 15019 && kind === 'video') {
2836
          sendEncodingParameters[0].rtx = {
2837
            ssrc: (2 * sdpMLineIndex + 1) * 1001 + 1
2838
          };
2839
        }
2840
      }
2841
2842
      if (transceiver.wantReceive) {
2843
        transceiver.rtpReceiver = new window.RTCRtpReceiver(
2844
          transceiver.dtlsTransport,
2845
          kind
2846
        );
2847
      }
2848
2849
      transceiver.localCapabilities = localCapabilities;
2850
      transceiver.sendEncodingParameters = sendEncodingParameters;
2851
    });
2852
2853
    // always offer BUNDLE and dispose on return if not supported.
2854
    if (this._config.bundlePolicy !== 'max-compat') {
2855
      sdp += 'a=group:BUNDLE ' + transceivers.map(function(t) {
2856
        return t.mid;
2857
      }).join(' ') + '\r\n';
2858
    }
2859
    sdp += 'a=ice-options:trickle\r\n';
2860
2861
    transceivers.forEach(function(transceiver, sdpMLineIndex) {
0 ignored issues
show
Unused Code introduced by
The parameter sdpMLineIndex is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
2862
      sdp += SDPUtils.writeMediaSection(transceiver,
2863
          transceiver.localCapabilities, 'offer', transceiver.stream);
2864
      sdp += 'a=rtcp-rsize\r\n';
2865
    });
2866
2867
    this._pendingOffer = transceivers;
2868
    var desc = new window.RTCSessionDescription({
2869
      type: 'offer',
2870
      sdp: sdp
2871
    });
2872
    if (arguments.length && typeof arguments[0] === 'function') {
2873
      window.setTimeout(arguments[0], 0, desc);
2874
    }
2875
    return Promise.resolve(desc);
2876
  };
2877
2878
  RTCPeerConnection.prototype.createAnswer = function() {
2879
    var sdp = SDPUtils.writeSessionBoilerplate(this._sdpSessionId);
2880
    if (this.usingBundle) {
2881
      sdp += 'a=group:BUNDLE ' + this.transceivers.map(function(t) {
2882
        return t.mid;
2883
      }).join(' ') + '\r\n';
2884
    }
2885
    this.transceivers.forEach(function(transceiver, sdpMLineIndex) {
2886
      if (transceiver.isDatachannel) {
2887
        sdp += 'm=application 0 DTLS/SCTP 5000\r\n' +
2888
            'c=IN IP4 0.0.0.0\r\n' +
2889
            'a=mid:' + transceiver.mid + '\r\n';
2890
        return;
2891
      }
2892
2893
      // FIXME: look at direction.
2894
      if (transceiver.stream) {
2895
        var localTrack;
2896
        if (transceiver.kind === 'audio') {
2897
          localTrack = transceiver.stream.getAudioTracks()[0];
2898
        } else if (transceiver.kind === 'video') {
2899
          localTrack = transceiver.stream.getVideoTracks()[0];
2900
        }
2901
        if (localTrack) {
2902
          // add RTX
2903
          if (edgeVersion >= 15019 && transceiver.kind === 'video') {
2904
            transceiver.sendEncodingParameters[0].rtx = {
2905
              ssrc: (2 * sdpMLineIndex + 2) * 1001 + 1
2906
            };
2907
          }
2908
        }
2909
      }
2910
2911
      // Calculate intersection of capabilities.
2912
      var commonCapabilities = getCommonCapabilities(
2913
          transceiver.localCapabilities,
2914
          transceiver.remoteCapabilities);
2915
2916
      var hasRtx = commonCapabilities.codecs.filter(function(c) {
2917
        return c.name.toLowerCase() === 'rtx';
2918
      }).length;
2919
      if (!hasRtx && transceiver.sendEncodingParameters[0].rtx) {
2920
        delete transceiver.sendEncodingParameters[0].rtx;
2921
      }
2922
2923
      sdp += SDPUtils.writeMediaSection(transceiver, commonCapabilities,
2924
          'answer', transceiver.stream);
2925
      if (transceiver.rtcpParameters &&
2926
          transceiver.rtcpParameters.reducedSize) {
2927
        sdp += 'a=rtcp-rsize\r\n';
2928
      }
2929
    });
2930
2931
    var desc = new window.RTCSessionDescription({
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
2932
      type: 'answer',
2933
      sdp: sdp
2934
    });
2935
    if (arguments.length && typeof arguments[0] === 'function') {
2936
      window.setTimeout(arguments[0], 0, desc);
2937
    }
2938
    return Promise.resolve(desc);
2939
  };
2940
2941
  RTCPeerConnection.prototype.addIceCandidate = function(candidate) {
2942
    if (!candidate) {
2943
      for (var j = 0; j < this.transceivers.length; j++) {
2944
        this.transceivers[j].iceTransport.addRemoteCandidate({});
2945
        if (this.usingBundle) {
2946
          return Promise.resolve();
2947
        }
2948
      }
2949
    } else {
2950
      var mLineIndex = candidate.sdpMLineIndex;
2951
      if (candidate.sdpMid) {
2952
        for (var i = 0; i < this.transceivers.length; i++) {
2953
          if (this.transceivers[i].mid === candidate.sdpMid) {
2954
            mLineIndex = i;
2955
            break;
2956
          }
2957
        }
2958
      }
2959
      var transceiver = this.transceivers[mLineIndex];
2960
      if (transceiver) {
2961
        var cand = Object.keys(candidate.candidate).length > 0 ?
2962
            SDPUtils.parseCandidate(candidate.candidate) : {};
2963
        // Ignore Chrome's invalid candidates since Edge does not like them.
2964
        if (cand.protocol === 'tcp' && (cand.port === 0 || cand.port === 9)) {
2965
          return Promise.resolve();
2966
        }
2967
        // Ignore RTCP candidates, we assume RTCP-MUX.
2968
        if (cand.component &&
2969
            !(cand.component === '1' || cand.component === 1)) {
2970
          return Promise.resolve();
2971
        }
2972
        transceiver.iceTransport.addRemoteCandidate(cand);
2973
2974
        // update the remoteDescription.
2975
        var sections = SDPUtils.splitSections(this.remoteDescription.sdp);
2976
        sections[mLineIndex + 1] += (cand.type ? candidate.candidate.trim()
2977
            : 'a=end-of-candidates') + '\r\n';
2978
        this.remoteDescription.sdp = sections.join('');
2979
      }
2980
    }
2981
    if (arguments.length > 1 && typeof arguments[1] === 'function') {
2982
      window.setTimeout(arguments[1], 0);
2983
    }
2984
    return Promise.resolve();
2985
  };
2986
2987
  RTCPeerConnection.prototype.getStats = function() {
2988
    var promises = [];
2989
    this.transceivers.forEach(function(transceiver) {
2990
      ['rtpSender', 'rtpReceiver', 'iceGatherer', 'iceTransport',
2991
        'dtlsTransport'].forEach(function(method) {
2992
          if (transceiver[method]) {
2993
            promises.push(transceiver[method].getStats());
2994
          }
2995
        });
2996
    });
2997
    var cb = arguments.length > 1 && typeof arguments[1] === 'function' &&
2998
        arguments[1];
2999
    var fixStatsType = function(stat) {
3000
      return {
3001
        inboundrtp: 'inbound-rtp',
3002
        outboundrtp: 'outbound-rtp',
3003
        candidatepair: 'candidate-pair',
3004
        localcandidate: 'local-candidate',
3005
        remotecandidate: 'remote-candidate'
3006
      }[stat.type] || stat.type;
3007
    };
3008
    return new Promise(function(resolve) {
3009
      // shim getStats with maplike support
3010
      var results = new Map();
3011
      Promise.all(promises).then(function(res) {
3012
        res.forEach(function(result) {
3013
          Object.keys(result).forEach(function(id) {
3014
            result[id].type = fixStatsType(result[id]);
3015
            results.set(id, result[id]);
3016
          });
3017
        });
3018
        if (cb) {
3019
          window.setTimeout(cb, 0, results);
3020
        }
3021
        resolve(results);
3022
      });
3023
    });
3024
  };
3025
  return RTCPeerConnection;
3026
};
3027
3028
},{"sdp":1}],9:[function(require,module,exports){
3029
/*
3030
 *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
3031
 *
3032
 *  Use of this source code is governed by a BSD-style license
3033
 *  that can be found in the LICENSE file in the root of the source
3034
 *  tree.
3035
 */
3036
 /* eslint-env node */
3037
'use strict';
3038
3039
var utils = require('../utils');
3040
3041
var firefoxShim = {
3042
  shimOnTrack: function(window) {
3043
    if (typeof window === 'object' && window.RTCPeerConnection && !('ontrack' in
3044
        window.RTCPeerConnection.prototype)) {
3045
      Object.defineProperty(window.RTCPeerConnection.prototype, 'ontrack', {
3046
        get: function() {
3047
          return this._ontrack;
3048
        },
3049
        set: function(f) {
3050
          if (this._ontrack) {
3051
            this.removeEventListener('track', this._ontrack);
3052
            this.removeEventListener('addstream', this._ontrackpoly);
3053
          }
3054
          this.addEventListener('track', this._ontrack = f);
3055
          this.addEventListener('addstream', this._ontrackpoly = function(e) {
3056
            e.stream.getTracks().forEach(function(track) {
3057
              var event = new Event('track');
0 ignored issues
show
Bug introduced by
The variable Event seems to be never declared. If this is a global, consider adding a /** global: Event */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
3058
              event.track = track;
3059
              event.receiver = {track: track};
3060
              event.streams = [e.stream];
3061
              this.dispatchEvent(event);
3062
            }.bind(this));
3063
          }.bind(this));
3064
        }
3065
      });
3066
    }
3067
  },
3068
3069
  shimSourceObject: function(window) {
3070
    // Firefox has supported mozSrcObject since FF22, unprefixed in 42.
3071
    if (typeof window === 'object') {
3072
      if (window.HTMLMediaElement &&
3073
        !('srcObject' in window.HTMLMediaElement.prototype)) {
3074
        // Shim the srcObject property, once, when HTMLMediaElement is found.
3075
        Object.defineProperty(window.HTMLMediaElement.prototype, 'srcObject', {
3076
          get: function() {
3077
            return this.mozSrcObject;
3078
          },
3079
          set: function(stream) {
3080
            this.mozSrcObject = stream;
3081
          }
3082
        });
3083
      }
3084
    }
3085
  },
3086
3087
  shimPeerConnection: function(window) {
3088
    var browserDetails = utils.detectBrowser(window);
3089
3090
    if (typeof window !== 'object' || !(window.RTCPeerConnection ||
3091
        window.mozRTCPeerConnection)) {
3092
      return; // probably media.peerconnection.enabled=false in about:config
3093
    }
3094
    // The RTCPeerConnection object.
3095
    if (!window.RTCPeerConnection) {
3096
      window.RTCPeerConnection = function(pcConfig, pcConstraints) {
3097
        if (browserDetails.version < 38) {
3098
          // .urls is not supported in FF < 38.
3099
          // create RTCIceServers with a single url.
3100
          if (pcConfig && pcConfig.iceServers) {
3101
            var newIceServers = [];
3102
            for (var i = 0; i < pcConfig.iceServers.length; i++) {
3103
              var server = pcConfig.iceServers[i];
3104
              if (server.hasOwnProperty('urls')) {
3105
                for (var j = 0; j < server.urls.length; j++) {
3106
                  var newServer = {
3107
                    url: server.urls[j]
3108
                  };
3109
                  if (server.urls[j].indexOf('turn') === 0) {
3110
                    newServer.username = server.username;
3111
                    newServer.credential = server.credential;
3112
                  }
3113
                  newIceServers.push(newServer);
3114
                }
3115
              } else {
3116
                newIceServers.push(pcConfig.iceServers[i]);
3117
              }
3118
            }
3119
            pcConfig.iceServers = newIceServers;
3120
          }
3121
        }
3122
        return new window.mozRTCPeerConnection(pcConfig, pcConstraints);
3123
      };
3124
      window.RTCPeerConnection.prototype =
3125
          window.mozRTCPeerConnection.prototype;
3126
3127
      // wrap static methods. Currently just generateCertificate.
3128
      if (window.mozRTCPeerConnection.generateCertificate) {
3129
        Object.defineProperty(window.RTCPeerConnection, 'generateCertificate', {
3130
          get: function() {
3131
            return window.mozRTCPeerConnection.generateCertificate;
3132
          }
3133
        });
3134
      }
3135
3136
      window.RTCSessionDescription = window.mozRTCSessionDescription;
3137
      window.RTCIceCandidate = window.mozRTCIceCandidate;
3138
    }
3139
3140
    // shim away need for obsolete RTCIceCandidate/RTCSessionDescription.
3141
    ['setLocalDescription', 'setRemoteDescription', 'addIceCandidate']
3142
        .forEach(function(method) {
3143
          var nativeMethod = window.RTCPeerConnection.prototype[method];
3144
          window.RTCPeerConnection.prototype[method] = function() {
3145
            arguments[0] = new ((method === 'addIceCandidate') ?
3146
                window.RTCIceCandidate :
3147
                window.RTCSessionDescription)(arguments[0]);
3148
            return nativeMethod.apply(this, arguments);
3149
          };
3150
        });
3151
3152
    // support for addIceCandidate(null or undefined)
3153
    var nativeAddIceCandidate =
3154
        window.RTCPeerConnection.prototype.addIceCandidate;
3155
    window.RTCPeerConnection.prototype.addIceCandidate = function() {
3156
      if (!arguments[0]) {
3157
        if (arguments[1]) {
3158
          arguments[1].apply(null);
3159
        }
3160
        return Promise.resolve();
3161
      }
3162
      return nativeAddIceCandidate.apply(this, arguments);
3163
    };
3164
3165
    // shim getStats with maplike support
3166
    var makeMapStats = function(stats) {
3167
      var map = new Map();
3168
      Object.keys(stats).forEach(function(key) {
3169
        map.set(key, stats[key]);
3170
        map[key] = stats[key];
3171
      });
3172
      return map;
3173
    };
3174
3175
    var modernStatsTypes = {
3176
      inboundrtp: 'inbound-rtp',
3177
      outboundrtp: 'outbound-rtp',
3178
      candidatepair: 'candidate-pair',
3179
      localcandidate: 'local-candidate',
3180
      remotecandidate: 'remote-candidate'
3181
    };
3182
3183
    var nativeGetStats = window.RTCPeerConnection.prototype.getStats;
3184
    window.RTCPeerConnection.prototype.getStats = function(
3185
      selector,
3186
      onSucc,
3187
      onErr
3188
    ) {
3189
      return nativeGetStats.apply(this, [selector || null])
3190
        .then(function(stats) {
3191
          if (browserDetails.version < 48) {
3192
            stats = makeMapStats(stats);
3193
          }
3194
          if (browserDetails.version < 53 && !onSucc) {
3195
            // Shim only promise getStats with spec-hyphens in type names
3196
            // Leave callback version alone; misc old uses of forEach before Map
3197
            try {
3198
              stats.forEach(function(stat) {
3199
                stat.type = modernStatsTypes[stat.type] || stat.type;
3200
              });
3201
            } catch (e) {
3202
              if (e.name !== 'TypeError') {
3203
                throw e;
3204
              }
3205
              // Avoid TypeError: "type" is read-only, in old versions. 34-43ish
3206
              stats.forEach(function(stat, i) {
3207
                stats.set(i, Object.assign({}, stat, {
3208
                  type: modernStatsTypes[stat.type] || stat.type
3209
                }));
3210
              });
3211
            }
3212
          }
3213
          return stats;
3214
        })
3215
        .then(onSucc, onErr);
3216
    };
3217
  }
3218
};
3219
3220
// Expose public methods.
3221
module.exports = {
3222
  shimOnTrack: firefoxShim.shimOnTrack,
3223
  shimSourceObject: firefoxShim.shimSourceObject,
3224
  shimPeerConnection: firefoxShim.shimPeerConnection,
3225
  shimGetUserMedia: require('./getusermedia')
3226
};
3227
3228
},{"../utils":12,"./getusermedia":10}],10:[function(require,module,exports){
3229
/*
3230
 *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
3231
 *
3232
 *  Use of this source code is governed by a BSD-style license
3233
 *  that can be found in the LICENSE file in the root of the source
3234
 *  tree.
3235
 */
3236
 /* eslint-env node */
3237
'use strict';
3238
3239
var utils = require('../utils');
3240
var logging = utils.log;
3241
3242
// Expose public methods.
3243
module.exports = function(window) {
3244
  var browserDetails = utils.detectBrowser(window);
3245
  var navigator = window && window.navigator;
3246
  var MediaStreamTrack = window && window.MediaStreamTrack;
3247
3248
  var shimError_ = function(e) {
3249
    return {
3250
      name: {
3251
        InternalError: 'NotReadableError',
3252
        NotSupportedError: 'TypeError',
3253
        PermissionDeniedError: 'NotAllowedError',
3254
        SecurityError: 'NotAllowedError'
3255
      }[e.name] || e.name,
3256
      message: {
3257
        'The operation is insecure.': 'The request is not allowed by the ' +
3258
        'user agent or the platform in the current context.'
3259
      }[e.message] || e.message,
3260
      constraint: e.constraint,
3261
      toString: function() {
3262
        return this.name + (this.message && ': ') + this.message;
3263
      }
3264
    };
3265
  };
3266
3267
  // getUserMedia constraints shim.
3268
  var getUserMedia_ = function(constraints, onSuccess, onError) {
3269
    var constraintsToFF37_ = function(c) {
3270
      if (typeof c !== 'object' || c.require) {
3271
        return c;
3272
      }
3273
      var require = [];
3274
      Object.keys(c).forEach(function(key) {
3275
        if (key === 'require' || key === 'advanced' || key === 'mediaSource') {
3276
          return;
3277
        }
3278
        var r = c[key] = (typeof c[key] === 'object') ?
3279
            c[key] : {ideal: c[key]};
3280
        if (r.min !== undefined ||
3281
            r.max !== undefined || r.exact !== undefined) {
3282
          require.push(key);
3283
        }
3284
        if (r.exact !== undefined) {
3285
          if (typeof r.exact === 'number') {
3286
            r. min = r.max = r.exact;
3287
          } else {
3288
            c[key] = r.exact;
3289
          }
3290
          delete r.exact;
3291
        }
3292
        if (r.ideal !== undefined) {
3293
          c.advanced = c.advanced || [];
3294
          var oc = {};
3295
          if (typeof r.ideal === 'number') {
3296
            oc[key] = {min: r.ideal, max: r.ideal};
3297
          } else {
3298
            oc[key] = r.ideal;
3299
          }
3300
          c.advanced.push(oc);
3301
          delete r.ideal;
3302
          if (!Object.keys(r).length) {
3303
            delete c[key];
3304
          }
3305
        }
3306
      });
3307
      if (require.length) {
3308
        c.require = require;
3309
      }
3310
      return c;
3311
    };
3312
    constraints = JSON.parse(JSON.stringify(constraints));
3313
    if (browserDetails.version < 38) {
3314
      logging('spec: ' + JSON.stringify(constraints));
3315
      if (constraints.audio) {
3316
        constraints.audio = constraintsToFF37_(constraints.audio);
3317
      }
3318
      if (constraints.video) {
3319
        constraints.video = constraintsToFF37_(constraints.video);
3320
      }
3321
      logging('ff37: ' + JSON.stringify(constraints));
3322
    }
3323
    return navigator.mozGetUserMedia(constraints, onSuccess, function(e) {
3324
      onError(shimError_(e));
3325
    });
3326
  };
3327
3328
  // Returns the result of getUserMedia as a Promise.
3329
  var getUserMediaPromise_ = function(constraints) {
3330
    return new Promise(function(resolve, reject) {
3331
      getUserMedia_(constraints, resolve, reject);
3332
    });
3333
  };
3334
3335
  // Shim for mediaDevices on older versions.
3336
  if (!navigator.mediaDevices) {
3337
    navigator.mediaDevices = {getUserMedia: getUserMediaPromise_,
3338
      addEventListener: function() { },
3339
      removeEventListener: function() { }
3340
    };
3341
  }
3342
  navigator.mediaDevices.enumerateDevices =
3343
      navigator.mediaDevices.enumerateDevices || function() {
3344
        return new Promise(function(resolve) {
3345
          var infos = [
3346
            {kind: 'audioinput', deviceId: 'default', label: '', groupId: ''},
3347
            {kind: 'videoinput', deviceId: 'default', label: '', groupId: ''}
3348
          ];
3349
          resolve(infos);
3350
        });
3351
      };
3352
3353
  if (browserDetails.version < 41) {
3354
    // Work around http://bugzil.la/1169665
3355
    var orgEnumerateDevices =
3356
        navigator.mediaDevices.enumerateDevices.bind(navigator.mediaDevices);
3357
    navigator.mediaDevices.enumerateDevices = function() {
3358
      return orgEnumerateDevices().then(undefined, function(e) {
3359
        if (e.name === 'NotFoundError') {
3360
          return [];
3361
        }
3362
        throw e;
3363
      });
3364
    };
3365
  }
3366
  if (browserDetails.version < 49) {
3367
    var origGetUserMedia = navigator.mediaDevices.getUserMedia.
3368
        bind(navigator.mediaDevices);
3369
    navigator.mediaDevices.getUserMedia = function(c) {
3370
      return origGetUserMedia(c).then(function(stream) {
3371
        // Work around https://bugzil.la/802326
3372
        if (c.audio && !stream.getAudioTracks().length ||
3373
            c.video && !stream.getVideoTracks().length) {
3374
          stream.getTracks().forEach(function(track) {
3375
            track.stop();
3376
          });
3377
          throw new DOMException('The object can not be found here.',
0 ignored issues
show
Bug introduced by
The variable DOMException seems to be never declared. If this is a global, consider adding a /** global: DOMException */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
3378
                                 'NotFoundError');
3379
        }
3380
        return stream;
3381
      }, function(e) {
3382
        return Promise.reject(shimError_(e));
3383
      });
3384
    };
3385
  }
3386
  if (!(browserDetails.version > 55 &&
3387
      'autoGainControl' in navigator.mediaDevices.getSupportedConstraints())) {
3388
    var remap = function(obj, a, b) {
3389
      if (a in obj && !(b in obj)) {
3390
        obj[b] = obj[a];
3391
        delete obj[a];
3392
      }
3393
    };
3394
3395
    var nativeGetUserMedia = navigator.mediaDevices.getUserMedia.
3396
        bind(navigator.mediaDevices);
3397
    navigator.mediaDevices.getUserMedia = function(c) {
3398
      if (typeof c === 'object' && typeof c.audio === 'object') {
3399
        c = JSON.parse(JSON.stringify(c));
3400
        remap(c.audio, 'autoGainControl', 'mozAutoGainControl');
3401
        remap(c.audio, 'noiseSuppression', 'mozNoiseSuppression');
3402
      }
3403
      return nativeGetUserMedia(c);
3404
    };
3405
3406
    if (MediaStreamTrack && MediaStreamTrack.prototype.getSettings) {
3407
      var nativeGetSettings = MediaStreamTrack.prototype.getSettings;
3408
      MediaStreamTrack.prototype.getSettings = function() {
3409
        var obj = nativeGetSettings.apply(this, arguments);
3410
        remap(obj, 'mozAutoGainControl', 'autoGainControl');
3411
        remap(obj, 'mozNoiseSuppression', 'noiseSuppression');
3412
        return obj;
3413
      };
3414
    }
3415
3416
    if (MediaStreamTrack && MediaStreamTrack.prototype.applyConstraints) {
3417
      var nativeApplyConstraints = MediaStreamTrack.prototype.applyConstraints;
3418
      MediaStreamTrack.prototype.applyConstraints = function(c) {
3419
        if (this.kind === 'audio' && typeof c === 'object') {
3420
          c = JSON.parse(JSON.stringify(c));
3421
          remap(c, 'autoGainControl', 'mozAutoGainControl');
3422
          remap(c, 'noiseSuppression', 'mozNoiseSuppression');
3423
        }
3424
        return nativeApplyConstraints.apply(this, [c]);
3425
      };
3426
    }
3427
  }
3428
  navigator.getUserMedia = function(constraints, onSuccess, onError) {
3429
    if (browserDetails.version < 44) {
3430
      return getUserMedia_(constraints, onSuccess, onError);
3431
    }
3432
    // Replace Firefox 44+'s deprecation warning with unprefixed version.
3433
    console.warn('navigator.getUserMedia has been replaced by ' +
3434
                 'navigator.mediaDevices.getUserMedia');
3435
    navigator.mediaDevices.getUserMedia(constraints).then(onSuccess, onError);
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
3436
  };
3437
};
3438
3439
},{"../utils":12}],11:[function(require,module,exports){
3440
/*
3441
 *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
3442
 *
3443
 *  Use of this source code is governed by a BSD-style license
3444
 *  that can be found in the LICENSE file in the root of the source
3445
 *  tree.
3446
 */
3447
'use strict';
3448
var utils = require('../utils');
3449
3450
var safariShim = {
3451
  // TODO: DrAlex, should be here, double check against LayoutTests
3452
3453
  // TODO: once the back-end for the mac port is done, add.
3454
  // TODO: check for webkitGTK+
3455
  // shimPeerConnection: function() { },
3456
3457
  shimLocalStreamsAPI: function(window) {
3458
    if (typeof window !== 'object' || !window.RTCPeerConnection) {
3459
      return;
3460
    }
3461
    if (!('getLocalStreams' in window.RTCPeerConnection.prototype)) {
3462
      window.RTCPeerConnection.prototype.getLocalStreams = function() {
3463
        if (!this._localStreams) {
3464
          this._localStreams = [];
3465
        }
3466
        return this._localStreams;
3467
      };
3468
    }
3469
    if (!('getStreamById' in window.RTCPeerConnection.prototype)) {
3470
      window.RTCPeerConnection.prototype.getStreamById = function(id) {
3471
        var result = null;
3472
        if (this._localStreams) {
3473
          this._localStreams.forEach(function(stream) {
3474
            if (stream.id === id) {
3475
              result = stream;
3476
            }
3477
          });
3478
        }
3479
        if (this._remoteStreams) {
3480
          this._remoteStreams.forEach(function(stream) {
3481
            if (stream.id === id) {
3482
              result = stream;
3483
            }
3484
          });
3485
        }
3486
        return result;
3487
      };
3488
    }
3489
    if (!('addStream' in window.RTCPeerConnection.prototype)) {
3490
      var _addTrack = window.RTCPeerConnection.prototype.addTrack;
3491
      window.RTCPeerConnection.prototype.addStream = function(stream) {
3492
        if (!this._localStreams) {
3493
          this._localStreams = [];
3494
        }
3495
        if (this._localStreams.indexOf(stream) === -1) {
3496
          this._localStreams.push(stream);
3497
        }
3498
        var self = this;
3499
        stream.getTracks().forEach(function(track) {
3500
          _addTrack.call(self, track, stream);
3501
        });
3502
      };
3503
3504
      window.RTCPeerConnection.prototype.addTrack = function(track, stream) {
3505
        if (stream) {
3506
          if (!this._localStreams) {
3507
            this._localStreams = [stream];
3508
          } else if (this._localStreams.indexOf(stream) === -1) {
3509
            this._localStreams.push(stream);
3510
          }
3511
        }
3512
        _addTrack.call(this, track, stream);
3513
      };
3514
    }
3515
    if (!('removeStream' in window.RTCPeerConnection.prototype)) {
3516
      window.RTCPeerConnection.prototype.removeStream = function(stream) {
3517
        if (!this._localStreams) {
3518
          this._localStreams = [];
3519
        }
3520
        var index = this._localStreams.indexOf(stream);
3521
        if (index === -1) {
3522
          return;
3523
        }
3524
        this._localStreams.splice(index, 1);
3525
        var self = this;
3526
        var tracks = stream.getTracks();
3527
        this.getSenders().forEach(function(sender) {
3528
          if (tracks.indexOf(sender.track) !== -1) {
3529
            self.removeTrack(sender);
3530
          }
3531
        });
3532
      };
3533
    }
3534
  },
3535
  shimRemoteStreamsAPI: function(window) {
3536
    if (typeof window !== 'object' || !window.RTCPeerConnection) {
3537
      return;
3538
    }
3539
    if (!('getRemoteStreams' in window.RTCPeerConnection.prototype)) {
3540
      window.RTCPeerConnection.prototype.getRemoteStreams = function() {
3541
        return this._remoteStreams ? this._remoteStreams : [];
3542
      };
3543
    }
3544
    if (!('onaddstream' in window.RTCPeerConnection.prototype)) {
3545
      Object.defineProperty(window.RTCPeerConnection.prototype, 'onaddstream', {
3546
        get: function() {
3547
          return this._onaddstream;
3548
        },
3549
        set: function(f) {
3550
          if (this._onaddstream) {
3551
            this.removeEventListener('addstream', this._onaddstream);
3552
            this.removeEventListener('track', this._onaddstreampoly);
3553
          }
3554
          this.addEventListener('addstream', this._onaddstream = f);
3555
          this.addEventListener('track', this._onaddstreampoly = function(e) {
3556
            var stream = e.streams[0];
3557
            if (!this._remoteStreams) {
3558
              this._remoteStreams = [];
3559
            }
3560
            if (this._remoteStreams.indexOf(stream) >= 0) {
3561
              return;
3562
            }
3563
            this._remoteStreams.push(stream);
3564
            var event = new Event('addstream');
0 ignored issues
show
Bug introduced by
The variable Event seems to be never declared. If this is a global, consider adding a /** global: Event */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
3565
            event.stream = e.streams[0];
3566
            this.dispatchEvent(event);
3567
          }.bind(this));
3568
        }
3569
      });
3570
    }
3571
  },
3572
  shimCallbacksAPI: function(window) {
3573
    if (typeof window !== 'object' || !window.RTCPeerConnection) {
3574
      return;
3575
    }
3576
    var prototype = window.RTCPeerConnection.prototype;
3577
    var createOffer = prototype.createOffer;
3578
    var createAnswer = prototype.createAnswer;
3579
    var setLocalDescription = prototype.setLocalDescription;
3580
    var setRemoteDescription = prototype.setRemoteDescription;
3581
    var addIceCandidate = prototype.addIceCandidate;
3582
3583
    prototype.createOffer = function(successCallback, failureCallback) {
3584
      var options = (arguments.length >= 2) ? arguments[2] : arguments[0];
3585
      var promise = createOffer.apply(this, [options]);
3586
      if (!failureCallback) {
3587
        return promise;
3588
      }
3589
      promise.then(successCallback, failureCallback);
3590
      return Promise.resolve();
3591
    };
3592
3593
    prototype.createAnswer = function(successCallback, failureCallback) {
3594
      var options = (arguments.length >= 2) ? arguments[2] : arguments[0];
3595
      var promise = createAnswer.apply(this, [options]);
3596
      if (!failureCallback) {
3597
        return promise;
3598
      }
3599
      promise.then(successCallback, failureCallback);
3600
      return Promise.resolve();
3601
    };
3602
3603
    var withCallback = function(description, successCallback, failureCallback) {
3604
      var promise = setLocalDescription.apply(this, [description]);
3605
      if (!failureCallback) {
3606
        return promise;
3607
      }
3608
      promise.then(successCallback, failureCallback);
3609
      return Promise.resolve();
3610
    };
3611
    prototype.setLocalDescription = withCallback;
3612
3613
    withCallback = function(description, successCallback, failureCallback) {
3614
      var promise = setRemoteDescription.apply(this, [description]);
3615
      if (!failureCallback) {
3616
        return promise;
3617
      }
3618
      promise.then(successCallback, failureCallback);
3619
      return Promise.resolve();
3620
    };
3621
    prototype.setRemoteDescription = withCallback;
3622
3623
    withCallback = function(candidate, successCallback, failureCallback) {
3624
      var promise = addIceCandidate.apply(this, [candidate]);
3625
      if (!failureCallback) {
3626
        return promise;
3627
      }
3628
      promise.then(successCallback, failureCallback);
3629
      return Promise.resolve();
3630
    };
3631
    prototype.addIceCandidate = withCallback;
3632
  },
3633
  shimGetUserMedia: function(window) {
3634
    var navigator = window && window.navigator;
3635
3636
    if (!navigator.getUserMedia) {
3637
      if (navigator.webkitGetUserMedia) {
3638
        navigator.getUserMedia = navigator.webkitGetUserMedia.bind(navigator);
3639
      } else if (navigator.mediaDevices &&
3640
          navigator.mediaDevices.getUserMedia) {
3641
        navigator.getUserMedia = function(constraints, cb, errcb) {
3642
          navigator.mediaDevices.getUserMedia(constraints)
3643
          .then(cb, errcb);
3644
        }.bind(navigator);
0 ignored issues
show
unused-code introduced by
The call to bind does not seem necessary since the function does not use this. Consider calling it directly.
Loading history...
3645
      }
3646
    }
3647
  },
3648
  shimRTCIceServerUrls: function(window) {
3649
    // migrate from non-spec RTCIceServer.url to RTCIceServer.urls
3650
    var OrigPeerConnection = window.RTCPeerConnection;
3651
    window.RTCPeerConnection = function(pcConfig, pcConstraints) {
3652
      if (pcConfig && pcConfig.iceServers) {
3653
        var newIceServers = [];
3654
        for (var i = 0; i < pcConfig.iceServers.length; i++) {
3655
          var server = pcConfig.iceServers[i];
3656
          if (!server.hasOwnProperty('urls') &&
3657
              server.hasOwnProperty('url')) {
3658
            utils.deprecated('RTCIceServer.url', 'RTCIceServer.urls');
3659
            server = JSON.parse(JSON.stringify(server));
3660
            server.urls = server.url;
3661
            delete server.url;
3662
            newIceServers.push(server);
3663
          } else {
3664
            newIceServers.push(pcConfig.iceServers[i]);
3665
          }
3666
        }
3667
        pcConfig.iceServers = newIceServers;
3668
      }
3669
      return new OrigPeerConnection(pcConfig, pcConstraints);
3670
    };
3671
    window.RTCPeerConnection.prototype = OrigPeerConnection.prototype;
3672
    // wrap static methods. Currently just generateCertificate.
3673
    Object.defineProperty(window.RTCPeerConnection, 'generateCertificate', {
3674
      get: function() {
3675
        return OrigPeerConnection.generateCertificate;
3676
      }
3677
    });
3678
  }
3679
};
3680
3681
// Expose public methods.
3682
module.exports = {
3683
  shimCallbacksAPI: safariShim.shimCallbacksAPI,
3684
  shimLocalStreamsAPI: safariShim.shimLocalStreamsAPI,
3685
  shimRemoteStreamsAPI: safariShim.shimRemoteStreamsAPI,
3686
  shimGetUserMedia: safariShim.shimGetUserMedia,
3687
  shimRTCIceServerUrls: safariShim.shimRTCIceServerUrls
3688
  // TODO
3689
  // shimPeerConnection: safariShim.shimPeerConnection
3690
};
3691
3692
},{"../utils":12}],12:[function(require,module,exports){
3693
/*
3694
 *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
3695
 *
3696
 *  Use of this source code is governed by a BSD-style license
3697
 *  that can be found in the LICENSE file in the root of the source
3698
 *  tree.
3699
 */
3700
 /* eslint-env node */
3701
'use strict';
3702
3703
var logDisabled_ = true;
3704
var deprecationWarnings_ = true;
3705
3706
// Utility methods.
3707
var utils = {
3708
  disableLog: function(bool) {
3709
    if (typeof bool !== 'boolean') {
3710
      return new Error('Argument type: ' + typeof bool +
3711
          '. Please use a boolean.');
3712
    }
3713
    logDisabled_ = bool;
3714
    return (bool) ? 'adapter.js logging disabled' :
3715
        'adapter.js logging enabled';
3716
  },
3717
3718
  /**
3719
   * Disable or enable deprecation warnings
3720
   * @param {!boolean} bool set to true to disable warnings.
3721
   */
3722
  disableWarnings: function(bool) {
3723
    if (typeof bool !== 'boolean') {
3724
      return new Error('Argument type: ' + typeof bool +
3725
          '. Please use a boolean.');
3726
    }
3727
    deprecationWarnings_ = !bool;
3728
    return 'adapter.js deprecation warnings ' + (bool ? 'disabled' : 'enabled');
3729
  },
3730
3731
  log: function() {
3732
    if (typeof window === 'object') {
3733
      if (logDisabled_) {
3734
        return;
3735
      }
3736
      if (typeof console !== 'undefined' && typeof console.log === 'function') {
3737
        console.log.apply(console, arguments);
3738
      }
3739
    }
3740
  },
3741
3742
  /**
3743
   * Shows a deprecation warning suggesting the modern and spec-compatible API.
3744
   */
3745
  deprecated: function(oldMethod, newMethod) {
3746
    if (!deprecationWarnings_) {
3747
      return;
3748
    }
3749
    console.warn(oldMethod + ' is deprecated, please use ' + newMethod +
3750
        ' instead.');
3751
  },
3752
3753
  /**
3754
   * Extract browser version out of the provided user agent string.
3755
   *
3756
   * @param {!string} uastring userAgent string.
3757
   * @param {!string} expr Regular expression used as match criteria.
3758
   * @param {!number} pos position in the version string to be returned.
3759
   * @return {!number} browser version.
3760
   */
3761
  extractVersion: function(uastring, expr, pos) {
3762
    var match = uastring.match(expr);
3763
    return match && match.length >= pos && parseInt(match[pos], 10);
3764
  },
3765
3766
  /**
3767
   * Browser detector.
3768
   *
3769
   * @return {object} result containing browser and version
3770
   *     properties.
3771
   */
3772
  detectBrowser: function(window) {
3773
    var navigator = window && window.navigator;
3774
3775
    // Returned result object.
3776
    var result = {};
3777
    result.browser = null;
3778
    result.version = null;
3779
3780
    // Fail early if it's not a browser
3781
    if (typeof window === 'undefined' || !window.navigator) {
3782
      result.browser = 'Not a browser.';
3783
      return result;
3784
    }
3785
3786
    // Firefox.
3787
    if (navigator.mozGetUserMedia) {
3788
      result.browser = 'firefox';
3789
      result.version = this.extractVersion(navigator.userAgent,
3790
          /Firefox\/(\d+)\./, 1);
3791
    } else if (navigator.webkitGetUserMedia) {
3792
      // Chrome, Chromium, Webview, Opera, all use the chrome shim for now
3793
      if (window.webkitRTCPeerConnection) {
3794
        result.browser = 'chrome';
3795
        result.version = this.extractVersion(navigator.userAgent,
3796
          /Chrom(e|ium)\/(\d+)\./, 2);
3797
      } else { // Safari (in an unpublished version) or unknown webkit-based.
3798
        if (navigator.userAgent.match(/Version\/(\d+).(\d+)/)) {
3799
          result.browser = 'safari';
3800
          result.version = this.extractVersion(navigator.userAgent,
3801
            /AppleWebKit\/(\d+)\./, 1);
3802
        } else { // unknown webkit-based browser.
3803
          result.browser = 'Unsupported webkit-based browser ' +
3804
              'with GUM support but no WebRTC support.';
3805
          return result;
3806
        }
3807
      }
3808
    } else if (navigator.mediaDevices &&
3809
        navigator.userAgent.match(/Edge\/(\d+).(\d+)$/)) { // Edge.
3810
      result.browser = 'edge';
3811
      result.version = this.extractVersion(navigator.userAgent,
3812
          /Edge\/(\d+).(\d+)$/, 2);
3813
    } else if (navigator.mediaDevices &&
3814
        navigator.userAgent.match(/AppleWebKit\/(\d+)\./)) {
3815
        // Safari, with webkitGetUserMedia removed.
3816
      result.browser = 'safari';
3817
      result.version = this.extractVersion(navigator.userAgent,
3818
          /AppleWebKit\/(\d+)\./, 1);
3819
    } else { // Default fallthrough: not supported.
3820
      result.browser = 'Not a supported browser.';
3821
      return result;
3822
    }
3823
3824
    return result;
3825
  },
3826
3827
  // shimCreateObjectURL must be called before shimSourceObject to avoid loop.
3828
3829
  shimCreateObjectURL: function(window) {
3830
    var URL = window && window.URL;
3831
3832
    if (!(typeof window === 'object' && window.HTMLMediaElement &&
3833
          'srcObject' in window.HTMLMediaElement.prototype)) {
3834
      // Only shim CreateObjectURL using srcObject if srcObject exists.
3835
      return undefined;
3836
    }
3837
3838
    var nativeCreateObjectURL = URL.createObjectURL.bind(URL);
3839
    var nativeRevokeObjectURL = URL.revokeObjectURL.bind(URL);
3840
    var streams = new Map(), newId = 0;
3841
3842
    URL.createObjectURL = function(stream) {
3843
      if ('getTracks' in stream) {
3844
        var url = 'polyblob:' + (++newId);
3845
        streams.set(url, stream);
3846
        utils.deprecated('URL.createObjectURL(stream)',
3847
            'elem.srcObject = stream');
3848
        return url;
3849
      }
3850
      return nativeCreateObjectURL(stream);
3851
    };
3852
    URL.revokeObjectURL = function(url) {
3853
      nativeRevokeObjectURL(url);
3854
      streams.delete(url);
3855
    };
3856
3857
    var dsc = Object.getOwnPropertyDescriptor(window.HTMLMediaElement.prototype,
3858
                                              'src');
3859
    Object.defineProperty(window.HTMLMediaElement.prototype, 'src', {
3860
      get: function() {
3861
        return dsc.get.apply(this);
3862
      },
3863
      set: function(url) {
3864
        this.srcObject = streams.get(url) || null;
3865
        return dsc.set.apply(this, [url]);
3866
      }
3867
    });
3868
3869
    var nativeSetAttribute = window.HTMLMediaElement.prototype.setAttribute;
3870
    window.HTMLMediaElement.prototype.setAttribute = function() {
3871
      if (arguments.length === 2 &&
3872
          ('' + arguments[0]).toLowerCase() === 'src') {
3873
        this.srcObject = streams.get(arguments[1]) || null;
3874
      }
3875
      return nativeSetAttribute.apply(this, arguments);
3876
    };
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
3877
  }
3878
};
3879
3880
// Export.
3881
module.exports = {
3882
  log: utils.log,
3883
  deprecated: utils.deprecated,
3884
  disableLog: utils.disableLog,
3885
  disableWarnings: utils.disableWarnings,
3886
  extractVersion: utils.extractVersion,
3887
  shimCreateObjectURL: utils.shimCreateObjectURL,
3888
  detectBrowser: utils.detectBrowser.bind(utils)
3889
};
3890
3891
},{}]},{},[2])(2)
3892
});
3893