Passed
Push — master ( 752189...d0149f )
by Jesus
02:09
created

amd/src/adapter.js   F

Complexity

Total Complexity 944
Complexity/F 2.67

Size

Lines of Code 3893
Function Count 353

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 0
nc 0
dl 0
loc 3893
rs 2.4
c 2
b 0
f 0
wmc 944
mnd 7
bc 754
fnc 353
bpm 2.1359
cpm 2.6742
noi 40

How to fix   Complexity   

Complexity

Complex classes like amd/src/adapter.js often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

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(){*/
2
define([], function() {
3
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
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...
Unused Code introduced by
The variable define seems to be never used. Consider removing it.
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...
Unused Code introduced by
The variable module seems to be never used. Consider removing it.
Loading history...
4
/* eslint-env node */
5
'use strict';
6
7
// SDP helpers.
8
var SDPUtils = {};
9
10
// Generate an alphanumeric identifier for cname or mids.
11
// TODO: use UUIDs instead? https://gist.github.com/jed/982883
12
SDPUtils.generateIdentifier = function() {
13
  return Math.random().toString(36).substr(2, 10);
14
};
15
16
// The RTCP CNAME used by all peerconnections from the same JS.
17
SDPUtils.localCName = SDPUtils.generateIdentifier();
18
19
// Splits SDP into lines, dealing with both CRLF and LF.
20
SDPUtils.splitLines = function(blob) {
21
  return blob.trim().split('\n').map(function(line) {
22
    return line.trim();
23
  });
24
};
25
// Splits SDP into sessionpart and mediasections. Ensures CRLF.
26
SDPUtils.splitSections = function(blob) {
27
  var parts = blob.split('\nm=');
28
  return parts.map(function(part, index) {
29
    return (index > 0 ? 'm=' + part : part).trim() + '\r\n';
30
  });
31
};
32
33
// Returns lines that start with a certain prefix.
34
SDPUtils.matchPrefix = function(blob, prefix) {
35
  return SDPUtils.splitLines(blob).filter(function(line) {
36
    return line.indexOf(prefix) === 0;
37
  });
38
};
39
40
// Parses an ICE candidate line. Sample input:
41
// candidate:702786350 2 udp 41819902 8.8.8.8 60769 typ relay raddr 8.8.8.8
42
// rport 55996"
43
SDPUtils.parseCandidate = function(line) {
44
  var parts;
45
  // Parse both variants.
46
  if (line.indexOf('a=candidate:') === 0) {
47
    parts = line.substring(12).split(' ');
48
  } else {
49
    parts = line.substring(10).split(' ');
50
  }
51
52
  var candidate = {
53
    foundation: parts[0],
54
    component: parseInt(parts[1], 10),
55
    protocol: parts[2].toLowerCase(),
56
    priority: parseInt(parts[3], 10),
57
    ip: parts[4],
58
    port: parseInt(parts[5], 10),
59
    // skip parts[6] == 'typ'
60
    type: parts[7]
61
  };
62
63
  for (var i = 8; i < parts.length; i += 2) {
64
    switch (parts[i]) {
65
      case 'raddr':
66
        candidate.relatedAddress = parts[i + 1];
67
        break;
68
      case 'rport':
69
        candidate.relatedPort = parseInt(parts[i + 1], 10);
70
        break;
71
      case 'tcptype':
72
        candidate.tcpType = parts[i + 1];
73
        break;
74
      default: // extension handling, in particular ufrag
75
        candidate[parts[i]] = parts[i + 1];
76
        break;
77
    }
78
  }
79
  return candidate;
80
};
81
82
// Translates a candidate object into SDP candidate attribute.
83
SDPUtils.writeCandidate = function(candidate) {
84
  var sdp = [];
85
  sdp.push(candidate.foundation);
86
  sdp.push(candidate.component);
87
  sdp.push(candidate.protocol.toUpperCase());
88
  sdp.push(candidate.priority);
89
  sdp.push(candidate.ip);
90
  sdp.push(candidate.port);
91
92
  var type = candidate.type;
93
  sdp.push('typ');
94
  sdp.push(type);
95
  if (type !== 'host' && candidate.relatedAddress &&
96
      candidate.relatedPort) {
97
    sdp.push('raddr');
98
    sdp.push(candidate.relatedAddress); // was: relAddr
99
    sdp.push('rport');
100
    sdp.push(candidate.relatedPort); // was: relPort
101
  }
102
  if (candidate.tcpType && candidate.protocol.toLowerCase() === 'tcp') {
103
    sdp.push('tcptype');
104
    sdp.push(candidate.tcpType);
105
  }
106
  if (candidate.ufrag) {
107
    sdp.push('ufrag');
108
    sdp.push(candidate.ufrag);
109
  }
110
  return 'candidate:' + sdp.join(' ');
111
};
112
113
// Parses an ice-options line, returns an array of option tags.
114
// a=ice-options:foo bar
115
SDPUtils.parseIceOptions = function(line) {
116
  return line.substr(14).split(' ');
117
}
118
119
// Parses an rtpmap line, returns RTCRtpCoddecParameters. Sample input:
120
// a=rtpmap:111 opus/48000/2
121
SDPUtils.parseRtpMap = function(line) {
122
  var parts = line.substr(9).split(' ');
123
  var parsed = {
124
    payloadType: parseInt(parts.shift(), 10) // was: id
125
  };
126
127
  parts = parts[0].split('/');
128
129
  parsed.name = parts[0];
130
  parsed.clockRate = parseInt(parts[1], 10); // was: clockrate
131
  // was: channels
132
  parsed.numChannels = parts.length === 3 ? parseInt(parts[2], 10) : 1;
133
  return parsed;
134
};
135
136
// Generate an a=rtpmap line from RTCRtpCodecCapability or
137
// RTCRtpCodecParameters.
138
SDPUtils.writeRtpMap = function(codec) {
139
  var pt = codec.payloadType;
140
  if (codec.preferredPayloadType !== undefined) {
141
    pt = codec.preferredPayloadType;
142
  }
143
  return 'a=rtpmap:' + pt + ' ' + codec.name + '/' + codec.clockRate +
144
      (codec.numChannels !== 1 ? '/' + codec.numChannels : '') + '\r\n';
145
};
146
147
// Parses an a=extmap line (headerextension from RFC 5285). Sample input:
148
// a=extmap:2 urn:ietf:params:rtp-hdrext:toffset
149
// a=extmap:2/sendonly urn:ietf:params:rtp-hdrext:toffset
150
SDPUtils.parseExtmap = function(line) {
151
  var parts = line.substr(9).split(' ');
152
  return {
153
    id: parseInt(parts[0], 10),
154
    direction: parts[0].indexOf('/') > 0 ? parts[0].split('/')[1] : 'sendrecv',
155
    uri: parts[1]
156
  };
157
};
158
159
// Generates a=extmap line from RTCRtpHeaderExtensionParameters or
160
// RTCRtpHeaderExtension.
161
SDPUtils.writeExtmap = function(headerExtension) {
162
  return 'a=extmap:' + (headerExtension.id || headerExtension.preferredId) +
163
      (headerExtension.direction && headerExtension.direction !== 'sendrecv'
164
          ? '/' + headerExtension.direction
165
          : '') +
166
      ' ' + headerExtension.uri + '\r\n';
167
};
168
169
// Parses an ftmp line, returns dictionary. Sample input:
170
// a=fmtp:96 vbr=on;cng=on
171
// Also deals with vbr=on; cng=on
172
SDPUtils.parseFmtp = function(line) {
173
  var parsed = {};
174
  var kv;
175
  var parts = line.substr(line.indexOf(' ') + 1).split(';');
176
  for (var j = 0; j < parts.length; j++) {
177
    kv = parts[j].trim().split('=');
178
    parsed[kv[0].trim()] = kv[1];
179
  }
180
  return parsed;
181
};
182
183
// Generates an a=ftmp line from RTCRtpCodecCapability or RTCRtpCodecParameters.
184
SDPUtils.writeFmtp = function(codec) {
185
  var line = '';
186
  var pt = codec.payloadType;
187
  if (codec.preferredPayloadType !== undefined) {
188
    pt = codec.preferredPayloadType;
189
  }
190
  if (codec.parameters && Object.keys(codec.parameters).length) {
191
    var params = [];
192
    Object.keys(codec.parameters).forEach(function(param) {
193
      params.push(param + '=' + codec.parameters[param]);
194
    });
195
    line += 'a=fmtp:' + pt + ' ' + params.join(';') + '\r\n';
196
  }
197
  return line;
198
};
199
200
// Parses an rtcp-fb line, returns RTCPRtcpFeedback object. Sample input:
201
// a=rtcp-fb:98 nack rpsi
202
SDPUtils.parseRtcpFb = function(line) {
203
  var parts = line.substr(line.indexOf(' ') + 1).split(' ');
204
  return {
205
    type: parts.shift(),
206
    parameter: parts.join(' ')
207
  };
208
};
209
// Generate a=rtcp-fb lines from RTCRtpCodecCapability or RTCRtpCodecParameters.
210
SDPUtils.writeRtcpFb = function(codec) {
211
  var lines = '';
212
  var pt = codec.payloadType;
213
  if (codec.preferredPayloadType !== undefined) {
214
    pt = codec.preferredPayloadType;
215
  }
216
  if (codec.rtcpFeedback && codec.rtcpFeedback.length) {
217
    // FIXME: special handling for trr-int?
218
    codec.rtcpFeedback.forEach(function(fb) {
219
      lines += 'a=rtcp-fb:' + pt + ' ' + fb.type +
220
      (fb.parameter && fb.parameter.length ? ' ' + fb.parameter : '') +
221
          '\r\n';
222
    });
223
  }
224
  return lines;
225
};
226
227
// Parses an RFC 5576 ssrc media attribute. Sample input:
228
// a=ssrc:3735928559 cname:something
229
SDPUtils.parseSsrcMedia = function(line) {
230
  var sp = line.indexOf(' ');
231
  var parts = {
232
    ssrc: parseInt(line.substr(7, sp - 7), 10)
233
  };
234
  var colon = line.indexOf(':', sp);
235
  if (colon > -1) {
236
    parts.attribute = line.substr(sp + 1, colon - sp - 1);
237
    parts.value = line.substr(colon + 1);
238
  } else {
239
    parts.attribute = line.substr(sp + 1);
240
  }
241
  return parts;
242
};
243
244
// Extracts the MID (RFC 5888) from a media section.
245
// returns the MID or undefined if no mid line was found.
246
SDPUtils.getMid = function(mediaSection) {
247
  var mid = SDPUtils.matchPrefix(mediaSection, 'a=mid:')[0];
248
  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...
249
    return mid.substr(6);
250
  }
251
}
252
253
SDPUtils.parseFingerprint = function(line) {
254
  var parts = line.substr(14).split(' ');
255
  return {
256
    algorithm: parts[0].toLowerCase(), // algorithm is case-sensitive in Edge.
257
    value: parts[1]
258
  };
259
};
260
261
// Extracts DTLS parameters from SDP media section or sessionpart.
262
// FIXME: for consistency with other functions this should only
263
//   get the fingerprint line as input. See also getIceParameters.
264
SDPUtils.getDtlsParameters = function(mediaSection, sessionpart) {
265
  var lines = SDPUtils.matchPrefix(mediaSection + sessionpart,
266
      'a=fingerprint:');
267
  // Note: a=setup line is ignored since we use the 'auto' role.
268
  // Note2: 'algorithm' is not case sensitive except in Edge.
269
  return {
270
    role: 'auto',
271
    fingerprints: lines.map(SDPUtils.parseFingerprint)
272
  };
273
};
274
275
// Serializes DTLS parameters to SDP.
276
SDPUtils.writeDtlsParameters = function(params, setupType) {
277
  var sdp = 'a=setup:' + setupType + '\r\n';
278
  params.fingerprints.forEach(function(fp) {
279
    sdp += 'a=fingerprint:' + fp.algorithm + ' ' + fp.value + '\r\n';
280
  });
281
  return sdp;
282
};
283
// Parses ICE information from SDP media section or sessionpart.
284
// FIXME: for consistency with other functions this should only
285
//   get the ice-ufrag and ice-pwd lines as input.
286
SDPUtils.getIceParameters = function(mediaSection, sessionpart) {
287
  var lines = SDPUtils.splitLines(mediaSection);
288
  // Search in session part, too.
289
  lines = lines.concat(SDPUtils.splitLines(sessionpart));
290
  var iceParameters = {
291
    usernameFragment: lines.filter(function(line) {
292
      return line.indexOf('a=ice-ufrag:') === 0;
293
    })[0].substr(12),
294
    password: lines.filter(function(line) {
295
      return line.indexOf('a=ice-pwd:') === 0;
296
    })[0].substr(10)
297
  };
298
  return iceParameters;
299
};
300
301
// Serializes ICE parameters to SDP.
302
SDPUtils.writeIceParameters = function(params) {
303
  return 'a=ice-ufrag:' + params.usernameFragment + '\r\n' +
304
      'a=ice-pwd:' + params.password + '\r\n';
305
};
306
307
// Parses the SDP media section and returns RTCRtpParameters.
308
SDPUtils.parseRtpParameters = function(mediaSection) {
309
  var description = {
310
    codecs: [],
311
    headerExtensions: [],
312
    fecMechanisms: [],
313
    rtcp: []
314
  };
315
  var lines = SDPUtils.splitLines(mediaSection);
316
  var mline = lines[0].split(' ');
317
  for (var i = 3; i < mline.length; i++) { // find all codecs from mline[3..]
318
    var pt = mline[i];
319
    var rtpmapline = SDPUtils.matchPrefix(
320
        mediaSection, 'a=rtpmap:' + pt + ' ')[0];
321
    if (rtpmapline) {
322
      var codec = SDPUtils.parseRtpMap(rtpmapline);
323
      var fmtps = SDPUtils.matchPrefix(
324
          mediaSection, 'a=fmtp:' + pt + ' ');
325
      // Only the first a=fmtp:<pt> is considered.
326
      codec.parameters = fmtps.length ? SDPUtils.parseFmtp(fmtps[0]) : {};
327
      codec.rtcpFeedback = SDPUtils.matchPrefix(
328
          mediaSection, 'a=rtcp-fb:' + pt + ' ')
329
        .map(SDPUtils.parseRtcpFb);
330
      description.codecs.push(codec);
331
      // parse FEC mechanisms from rtpmap lines.
332
      switch (codec.name.toUpperCase()) {
333
        case 'RED':
334
        case 'ULPFEC':
335
          description.fecMechanisms.push(codec.name.toUpperCase());
336
          break;
337
        default: // only RED and ULPFEC are recognized as FEC mechanisms.
338
          break;
339
      }
340
    }
341
  }
342
  SDPUtils.matchPrefix(mediaSection, 'a=extmap:').forEach(function(line) {
343
    description.headerExtensions.push(SDPUtils.parseExtmap(line));
344
  });
345
  // FIXME: parse rtcp.
346
  return description;
347
};
348
349
// Generates parts of the SDP media section describing the capabilities /
350
// parameters.
351
SDPUtils.writeRtpDescription = function(kind, caps) {
352
  var sdp = '';
353
354
  // Build the mline.
355
  sdp += 'm=' + kind + ' ';
356
  sdp += caps.codecs.length > 0 ? '9' : '0'; // reject if no codecs.
357
  sdp += ' UDP/TLS/RTP/SAVPF ';
358
  sdp += caps.codecs.map(function(codec) {
359
    if (codec.preferredPayloadType !== undefined) {
360
      return codec.preferredPayloadType;
361
    }
362
    return codec.payloadType;
363
  }).join(' ') + '\r\n';
364
365
  sdp += 'c=IN IP4 0.0.0.0\r\n';
366
  sdp += 'a=rtcp:9 IN IP4 0.0.0.0\r\n';
367
368
  // Add a=rtpmap lines for each codec. Also fmtp and rtcp-fb.
369
  caps.codecs.forEach(function(codec) {
370
    sdp += SDPUtils.writeRtpMap(codec);
371
    sdp += SDPUtils.writeFmtp(codec);
372
    sdp += SDPUtils.writeRtcpFb(codec);
373
  });
374
  var maxptime = 0;
375
  caps.codecs.forEach(function(codec) {
376
    if (codec.maxptime > maxptime) {
377
      maxptime = codec.maxptime;
378
    }
379
  });
380
  if (maxptime > 0) {
381
    sdp += 'a=maxptime:' + maxptime + '\r\n';
382
  }
383
  sdp += 'a=rtcp-mux\r\n';
384
385
  caps.headerExtensions.forEach(function(extension) {
386
    sdp += SDPUtils.writeExtmap(extension);
387
  });
388
  // FIXME: write fecMechanisms.
389
  return sdp;
390
};
391
392
// Parses the SDP media section and returns an array of
393
// RTCRtpEncodingParameters.
394
SDPUtils.parseRtpEncodingParameters = function(mediaSection) {
395
  var encodingParameters = [];
396
  var description = SDPUtils.parseRtpParameters(mediaSection);
397
  var hasRed = description.fecMechanisms.indexOf('RED') !== -1;
398
  var hasUlpfec = description.fecMechanisms.indexOf('ULPFEC') !== -1;
399
400
  // filter a=ssrc:... cname:, ignore PlanB-msid
401
  var ssrcs = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:')
402
  .map(function(line) {
403
    return SDPUtils.parseSsrcMedia(line);
404
  })
405
  .filter(function(parts) {
406
    return parts.attribute === 'cname';
407
  });
408
  var primarySsrc = ssrcs.length > 0 && ssrcs[0].ssrc;
409
  var secondarySsrc;
410
411
  var flows = SDPUtils.matchPrefix(mediaSection, 'a=ssrc-group:FID')
412
  .map(function(line) {
413
    var parts = line.split(' ');
414
    parts.shift();
415
    return parts.map(function(part) {
416
      return parseInt(part, 10);
417
    });
418
  });
419
  if (flows.length > 0 && flows[0].length > 1 && flows[0][0] === primarySsrc) {
420
    secondarySsrc = flows[0][1];
421
  }
422
423
  description.codecs.forEach(function(codec) {
424
    if (codec.name.toUpperCase() === 'RTX' && codec.parameters.apt) {
425
      var encParam = {
426
        ssrc: primarySsrc,
427
        codecPayloadType: parseInt(codec.parameters.apt, 10),
428
        rtx: {
429
          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 419 is false. Are you sure this can never be the case?
Loading history...
430
        }
431
      };
432
      encodingParameters.push(encParam);
433
      if (hasRed) {
434
        encParam = JSON.parse(JSON.stringify(encParam));
435
        encParam.fec = {
436
          ssrc: secondarySsrc,
437
          mechanism: hasUlpfec ? 'red+ulpfec' : 'red'
438
        };
439
        encodingParameters.push(encParam);
440
      }
441
    }
442
  });
443
  if (encodingParameters.length === 0 && primarySsrc) {
444
    encodingParameters.push({
445
      ssrc: primarySsrc
446
    });
447
  }
448
449
  // we support both b=AS and b=TIAS but interpret AS as TIAS.
450
  var bandwidth = SDPUtils.matchPrefix(mediaSection, 'b=');
451
  if (bandwidth.length) {
452
    if (bandwidth[0].indexOf('b=TIAS:') === 0) {
453
      bandwidth = parseInt(bandwidth[0].substr(7), 10);
454
    } else if (bandwidth[0].indexOf('b=AS:') === 0) {
455
      // use formula from JSEP to convert b=AS to TIAS value.
456
      bandwidth = parseInt(bandwidth[0].substr(5), 10) * 1000 * 0.95
457
          - (50 * 40 * 8);
458
    } else {
459
      bandwidth = undefined;
460
    }
461
    encodingParameters.forEach(function(params) {
462
      params.maxBitrate = bandwidth;
463
    });
464
  }
465
  return encodingParameters;
466
};
467
468
// parses http://draft.ortc.org/#rtcrtcpparameters*
469
SDPUtils.parseRtcpParameters = function(mediaSection) {
470
  var rtcpParameters = {};
471
472
  var cname;
473
  // Gets the first SSRC. Note that with RTX there might be multiple
474
  // SSRCs.
475
  var remoteSsrc = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:')
476
      .map(function(line) {
477
        return SDPUtils.parseSsrcMedia(line);
478
      })
479
      .filter(function(obj) {
480
        return obj.attribute === 'cname';
481
      })[0];
482
  if (remoteSsrc) {
483
    rtcpParameters.cname = remoteSsrc.value;
484
    rtcpParameters.ssrc = remoteSsrc.ssrc;
485
  }
486
487
  // Edge uses the compound attribute instead of reducedSize
488
  // compound is !reducedSize
489
  var rsize = SDPUtils.matchPrefix(mediaSection, 'a=rtcp-rsize');
490
  rtcpParameters.reducedSize = rsize.length > 0;
491
  rtcpParameters.compound = rsize.length === 0;
492
493
  // parses the rtcp-mux attrіbute.
494
  // Note that Edge does not support unmuxed RTCP.
495
  var mux = SDPUtils.matchPrefix(mediaSection, 'a=rtcp-mux');
496
  rtcpParameters.mux = mux.length > 0;
497
498
  return rtcpParameters;
499
};
500
501
// parses either a=msid: or a=ssrc:... msid lines and returns
502
// the id of the MediaStream and MediaStreamTrack.
503
SDPUtils.parseMsid = function(mediaSection) {
504
  var parts;
505
  var spec = SDPUtils.matchPrefix(mediaSection, 'a=msid:');
506
  if (spec.length === 1) {
507
    parts = spec[0].substr(7).split(' ');
508
    return {stream: parts[0], track: parts[1]};
509
  }
510
  var planB = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:')
511
  .map(function(line) {
512
    return SDPUtils.parseSsrcMedia(line);
513
  })
514
  .filter(function(parts) {
515
    return parts.attribute === 'msid';
516
  });
517
  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...
518
    parts = planB[0].value.split(' ');
519
    return {stream: parts[0], track: parts[1]};
520
  }
521
};
522
523
// Generate a session ID for SDP.
524
// https://tools.ietf.org/html/draft-ietf-rtcweb-jsep-20#section-5.2.1
525
// recommends using a cryptographically random +ve 64-bit value
526
// but right now this should be acceptable and within the right range
527
SDPUtils.generateSessionId = function() {
528
  return Math.random().toString().substr(2, 21);
529
};
530
531
// Write boilder plate for start of SDP
532
// sessId argument is optional - if not supplied it will
533
// be generated randomly
534
SDPUtils.writeSessionBoilerplate = function(sessId) {
535
  var sessionId;
536
  if (sessId) {
537
    sessionId = sessId;
538
  } else {
539
    sessionId = SDPUtils.generateSessionId();
540
  }
541
  // FIXME: sess-id should be an NTP timestamp.
542
  return 'v=0\r\n' +
543
      'o=thisisadapterortc ' + sessionId + ' 2 IN IP4 127.0.0.1\r\n' +
544
      's=-\r\n' +
545
      't=0 0\r\n';
546
};
547
548
SDPUtils.writeMediaSection = function(transceiver, caps, type, stream) {
549
  var sdp = SDPUtils.writeRtpDescription(transceiver.kind, caps);
550
551
  // Map ICE parameters (ufrag, pwd) to SDP.
552
  sdp += SDPUtils.writeIceParameters(
553
      transceiver.iceGatherer.getLocalParameters());
554
555
  // Map DTLS parameters to SDP.
556
  sdp += SDPUtils.writeDtlsParameters(
557
      transceiver.dtlsTransport.getLocalParameters(),
558
      type === 'offer' ? 'actpass' : 'active');
559
560
  sdp += 'a=mid:' + transceiver.mid + '\r\n';
561
562
  if (transceiver.direction) {
563
    sdp += 'a=' + transceiver.direction + '\r\n';
564
  } else if (transceiver.rtpSender && transceiver.rtpReceiver) {
565
    sdp += 'a=sendrecv\r\n';
566
  } else if (transceiver.rtpSender) {
567
    sdp += 'a=sendonly\r\n';
568
  } else if (transceiver.rtpReceiver) {
569
    sdp += 'a=recvonly\r\n';
570
  } else {
571
    sdp += 'a=inactive\r\n';
572
  }
573
574
  if (transceiver.rtpSender) {
575
    // spec.
576
    var msid = 'msid:' + stream.id + ' ' +
577
        transceiver.rtpSender.track.id + '\r\n';
578
    sdp += 'a=' + msid;
579
580
    // for Chrome.
581
    sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].ssrc +
582
        ' ' + msid;
583
    if (transceiver.sendEncodingParameters[0].rtx) {
584
      sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].rtx.ssrc +
585
          ' ' + msid;
586
      sdp += 'a=ssrc-group:FID ' +
587
          transceiver.sendEncodingParameters[0].ssrc + ' ' +
588
          transceiver.sendEncodingParameters[0].rtx.ssrc +
589
          '\r\n';
590
    }
591
  }
592
  // FIXME: this should be written by writeRtpDescription.
593
  sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].ssrc +
594
      ' cname:' + SDPUtils.localCName + '\r\n';
595
  if (transceiver.rtpSender && transceiver.sendEncodingParameters[0].rtx) {
596
    sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].rtx.ssrc +
597
        ' cname:' + SDPUtils.localCName + '\r\n';
598
  }
599
  return sdp;
600
};
601
602
// Gets the direction from the mediaSection or the sessionpart.
603
SDPUtils.getDirection = function(mediaSection, sessionpart) {
604
  // Look for sendrecv, sendonly, recvonly, inactive, default to sendrecv.
605
  var lines = SDPUtils.splitLines(mediaSection);
606
  for (var i = 0; i < lines.length; i++) {
607
    switch (lines[i]) {
608
      case 'a=sendrecv':
609
      case 'a=sendonly':
610
      case 'a=recvonly':
611
      case 'a=inactive':
612
        return lines[i].substr(2);
613
      default:
614
        // FIXME: What should happen here?
615
    }
616
  }
617
  if (sessionpart) {
618
    return SDPUtils.getDirection(sessionpart);
619
  }
620
  return 'sendrecv';
621
};
622
623
SDPUtils.getKind = function(mediaSection) {
624
  var lines = SDPUtils.splitLines(mediaSection);
625
  var mline = lines[0].split(' ');
626
  return mline[0].substr(2);
627
};
628
629
SDPUtils.isRejected = function(mediaSection) {
630
  return mediaSection.split(' ', 2)[1] === '0';
631
};
632
633
// Expose public methods.
634
module.exports = SDPUtils;
635
636
},{}],2:[function(require,module,exports){
637
(function (global){
638
/*
639
 *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
640
 *
641
 *  Use of this source code is governed by a BSD-style license
642
 *  that can be found in the LICENSE file in the root of the source
643
 *  tree.
644
 */
645
 /* eslint-env node */
646
647
'use strict';
648
649
var adapterFactory = require('./adapter_factory.js');
650
module.exports = adapterFactory({window: global.window});
651
652
}).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...
653
},{"./adapter_factory.js":3}],3:[function(require,module,exports){
654
/*
655
 *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
656
 *
657
 *  Use of this source code is governed by a BSD-style license
658
 *  that can be found in the LICENSE file in the root of the source
659
 *  tree.
660
 */
661
 /* eslint-env node */
662
663
'use strict';
664
665
// Shimming starts here.
666
module.exports = function(dependencies, opts) {
667
  var window = dependencies && dependencies.window;
668
669
  var options = Object.assign({
670
    shimChrome: true,
671
    shimFirefox: true,
672
    shimEdge: true,
673
    shimSafari: true,
674
  }, opts);
675
676
  // Utils.
677
  var utils = require('./utils');
678
  var logging = utils.log;
679
  var browserDetails = utils.detectBrowser(window);
680
681
  // Export to the adapter global object visible in the browser.
682
  var adapter = {
683
    browserDetails: browserDetails,
684
    extractVersion: utils.extractVersion,
685
    disableLog: utils.disableLog,
686
    disableWarnings: utils.disableWarnings
687
  };
688
689
  // Uncomment the line below if you want logging to occur, including logging
690
  // for the switch statement below. Can also be turned on in the browser via
691
  // adapter.disableLog(false), but then logging from the switch statement below
692
  // will not appear.
693
  // require('./utils').disableLog(false);
694
695
  // Browser shims.
696
  var chromeShim = require('./chrome/chrome_shim') || null;
697
  var edgeShim = require('./edge/edge_shim') || null;
698
  var firefoxShim = require('./firefox/firefox_shim') || null;
699
  var safariShim = require('./safari/safari_shim') || null;
700
701
  // Shim browser if found.
702
  switch (browserDetails.browser) {
703
    case 'chrome':
704
      if (!chromeShim || !chromeShim.shimPeerConnection ||
705
          !options.shimChrome) {
706
        logging('Chrome shim is not included in this adapter release.');
707
        return adapter;
708
      }
709
      logging('adapter.js shimming chrome.');
710
      // Export to the adapter global object visible in the browser.
711
      adapter.browserShim = chromeShim;
712
713
      chromeShim.shimGetUserMedia(window);
714
      chromeShim.shimMediaStream(window);
715
      utils.shimCreateObjectURL(window);
716
      chromeShim.shimSourceObject(window);
717
      chromeShim.shimPeerConnection(window);
718
      chromeShim.shimOnTrack(window);
719
      chromeShim.shimAddTrack(window);
720
      chromeShim.shimGetSendersWithDtmf(window);
721
      break;
722
    case 'firefox':
723
      if (!firefoxShim || !firefoxShim.shimPeerConnection ||
724
          !options.shimFirefox) {
725
        logging('Firefox shim is not included in this adapter release.');
726
        return adapter;
727
      }
728
      logging('adapter.js shimming firefox.');
729
      // Export to the adapter global object visible in the browser.
730
      adapter.browserShim = firefoxShim;
731
732
      firefoxShim.shimGetUserMedia(window);
733
      utils.shimCreateObjectURL(window);
734
      firefoxShim.shimSourceObject(window);
735
      firefoxShim.shimPeerConnection(window);
736
      firefoxShim.shimOnTrack(window);
737
      break;
738
    case 'edge':
739
      if (!edgeShim || !edgeShim.shimPeerConnection || !options.shimEdge) {
740
        logging('MS edge shim is not included in this adapter release.');
741
        return adapter;
742
      }
743
      logging('adapter.js shimming edge.');
744
      // Export to the adapter global object visible in the browser.
745
      adapter.browserShim = edgeShim;
746
747
      edgeShim.shimGetUserMedia(window);
748
      utils.shimCreateObjectURL(window);
749
      edgeShim.shimPeerConnection(window);
750
      edgeShim.shimReplaceTrack(window);
751
      break;
752
    case 'safari':
753
      if (!safariShim || !options.shimSafari) {
754
        logging('Safari shim is not included in this adapter release.');
755
        return adapter;
756
      }
757
      logging('adapter.js shimming safari.');
758
      // Export to the adapter global object visible in the browser.
759
      adapter.browserShim = safariShim;
760
      // shim window.URL.createObjectURL Safari (technical preview)
761
      utils.shimCreateObjectURL(window);
762
      safariShim.shimRTCIceServerUrls(window);
763
      safariShim.shimCallbacksAPI(window);
764
      safariShim.shimLocalStreamsAPI(window);
765
      safariShim.shimRemoteStreamsAPI(window);
766
      safariShim.shimGetUserMedia(window);
767
      break;
768
    default:
769
      logging('Unsupported browser!');
770
      break;
771
  }
772
773
  return adapter;
774
};
775
776
},{"./chrome/chrome_shim":4,"./edge/edge_shim":6,"./firefox/firefox_shim":9,"./safari/safari_shim":11,"./utils":12}],4:[function(require,module,exports){
777
778
/*
779
 *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
780
 *
781
 *  Use of this source code is governed by a BSD-style license
782
 *  that can be found in the LICENSE file in the root of the source
783
 *  tree.
784
 */
785
 /* eslint-env node */
786
'use strict';
787
var utils = require('../utils.js');
788
var logging = utils.log;
789
790
var chromeShim = {
791
  shimMediaStream: function(window) {
792
    window.MediaStream = window.MediaStream || window.webkitMediaStream;
793
  },
794
795
  shimOnTrack: function(window) {
796
    if (typeof window === 'object' && window.RTCPeerConnection && !('ontrack' in
797
        window.RTCPeerConnection.prototype)) {
798
      Object.defineProperty(window.RTCPeerConnection.prototype, 'ontrack', {
799
        get: function() {
800
          return this._ontrack;
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
801
        },
802
        set: function(f) {
803
          var self = this;
804
          if (this._ontrack) {
805
            this.removeEventListener('track', this._ontrack);
806
            this.removeEventListener('addstream', this._ontrackpoly);
807
          }
808
          this.addEventListener('track', this._ontrack = f);
809
          this.addEventListener('addstream', this._ontrackpoly = function(e) {
810
            // onaddstream does not fire when a track is added to an existing
811
            // stream. But stream.onaddtrack is implemented so we use that.
812
            e.stream.addEventListener('addtrack', function(te) {
813
              var receiver;
814
              if (window.RTCPeerConnection.prototype.getReceivers) {
815
                receiver = self.getReceivers().find(function(r) {
816
                  return r.track.id === te.track.id;
817
                });
818
              } else {
819
                receiver = {track: te.track};
820
              }
821
822
              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...
823
              event.track = te.track;
824
              event.receiver = receiver;
825
              event.streams = [e.stream];
826
              self.dispatchEvent(event);
827
            });
828
            e.stream.getTracks().forEach(function(track) {
829
              var receiver;
830
              if (window.RTCPeerConnection.prototype.getReceivers) {
831
                receiver = self.getReceivers().find(function(r) {
832
                  return r.track.id === track.id;
833
                });
834
              } else {
835
                receiver = {track: track};
836
              }
837
              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...
838
              event.track = track;
839
              event.receiver = receiver;
840
              event.streams = [e.stream];
841
              this.dispatchEvent(event);
842
            }.bind(this));
843
          }.bind(this));
844
        }
845
      });
846
    }
847
  },
848
849
  shimGetSendersWithDtmf: function(window) {
850
    if (typeof window === 'object' && window.RTCPeerConnection &&
851
        !('getSenders' in window.RTCPeerConnection.prototype) &&
852
        'createDTMFSender' in window.RTCPeerConnection.prototype) {
853
      var shimSenderWithDtmf = function(pc, track) {
854
        return {
855
          track: track,
856
          get dtmf() {
857
            if (this._dtmf === undefined) {
858
              if (track.kind === 'audio') {
859
                this._dtmf = pc.createDTMFSender(track);
860
              } else {
861
                this._dtmf = null;
862
              }
863
            }
864
            return this._dtmf;
865
          }
866
        };
867
      };
868
869
      // shim addTrack when getSenders is not available.
870
      if (!window.RTCPeerConnection.prototype.getSenders) {
871
        window.RTCPeerConnection.prototype.getSenders = function() {
872
          return this._senders || [];
873
        };
874
        var origAddTrack = window.RTCPeerConnection.prototype.addTrack;
875
        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...
876
          var pc = this;
877
          var sender = origAddTrack.apply(pc, arguments);
878
          if (!sender) {
879
            sender = shimSenderWithDtmf(pc, track);
880
            pc._senders.push(sender);
881
          }
882
          return sender;
883
        };
884
      }
885
      var origAddStream = window.RTCPeerConnection.prototype.addStream;
886
      window.RTCPeerConnection.prototype.addStream = function(stream) {
887
        var pc = this;
888
        pc._senders = pc._senders || [];
889
        origAddStream.apply(pc, [stream]);
890
        stream.getTracks().forEach(function(track) {
891
          pc._senders.push(shimSenderWithDtmf(pc, track));
892
        });
893
      };
894
895
      var origRemoveStream = window.RTCPeerConnection.prototype.removeStream;
896
      window.RTCPeerConnection.prototype.removeStream = function(stream) {
897
        var pc = this;
898
        pc._senders = pc._senders || [];
899
        origRemoveStream.apply(pc, [(pc._streams[stream.id] || stream)]);
900
901
        stream.getTracks().forEach(function(track) {
902
          var sender = pc._senders.find(function(s) {
903
            return s.track === track;
904
          });
905
          if (sender) {
906
            pc._senders.splice(pc._senders.indexOf(sender), 1); // remove sender
907
          }
908
        });
909
      };
910
    } else if (typeof window === 'object' && window.RTCPeerConnection &&
911
               'getSenders' in window.RTCPeerConnection.prototype &&
912
               'createDTMFSender' in window.RTCPeerConnection.prototype &&
913
               window.RTCRtpSender &&
914
               !('dtmf' in window.RTCRtpSender.prototype)) {
915
      var origGetSenders = window.RTCPeerConnection.prototype.getSenders;
916
      window.RTCPeerConnection.prototype.getSenders = function() {
917
        var pc = this;
918
        var senders = origGetSenders.apply(pc, []);
919
        senders.forEach(function(sender) {
920
          sender._pc = pc;
921
        });
922
        return senders;
923
      };
924
925
      Object.defineProperty(window.RTCRtpSender.prototype, 'dtmf', {
926
        get: function() {
927
          if (this._dtmf === undefined) {
928
            if (this.track.kind === 'audio') {
929
              this._dtmf = this._pc.createDTMFSender(this.track);
930
            } else {
931
              this._dtmf = null;
932
            }
933
          }
934
          return this._dtmf;
935
        },
936
      });
937
    }
938
  },
939
940
  shimSourceObject: function(window) {
941
    var URL = window && window.URL;
942
943
    if (typeof window === 'object') {
944
      if (window.HTMLMediaElement &&
945
        !('srcObject' in window.HTMLMediaElement.prototype)) {
946
        // Shim the srcObject property, once, when HTMLMediaElement is found.
947
        Object.defineProperty(window.HTMLMediaElement.prototype, 'srcObject', {
948
          get: function() {
949
            return this._srcObject;
950
          },
951
          set: function(stream) {
952
            var self = this;
953
            // Use _srcObject as a private property for this shim
954
            this._srcObject = stream;
955
            if (this.src) {
956
              URL.revokeObjectURL(this.src);
957
            }
958
959
            if (!stream) {
960
              this.src = '';
961
              return undefined;
962
            }
963
            this.src = URL.createObjectURL(stream);
964
            // We need to recreate the blob url when a track is added or
965
            // removed. Doing it manually since we want to avoid a recursion.
966
            stream.addEventListener('addtrack', function() {
967
              if (self.src) {
968
                URL.revokeObjectURL(self.src);
969
              }
970
              self.src = URL.createObjectURL(stream);
971
            });
972
            stream.addEventListener('removetrack', function() {
973
              if (self.src) {
974
                URL.revokeObjectURL(self.src);
975
              }
976
              self.src = URL.createObjectURL(stream);
977
            });
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...
978
          }
979
        });
980
      }
981
    }
982
  },
983
984
  shimAddTrack: function(window) {
985
    // shim addTrack (when getSenders is available)
986
    if (window.RTCPeerConnection.prototype.addTrack) {
987
      return;
988
    }
989
990
    // also shim pc.getLocalStreams when addTrack is shimmed
991
    // to return the original streams.
992
    var origGetLocalStreams = window.RTCPeerConnection.prototype
993
        .getLocalStreams;
994
    window.RTCPeerConnection.prototype.getLocalStreams = function() {
995
      var self = this;
996
      var nativeStreams = origGetLocalStreams.apply(this);
997
      self._reverseStreams = self._reverseStreams || {};
998
      return nativeStreams.map(function(stream) {
999
        return self._reverseStreams[stream.id];
1000
      });
1001
    };
1002
1003
    var origAddStream = window.RTCPeerConnection.prototype.addStream;
1004
    window.RTCPeerConnection.prototype.addStream = function(stream) {
1005
      var pc = this;
1006
      pc._streams = pc._streams || {};
1007
      pc._reverseStreams = pc._reverseStreams || {};
1008
1009
      // Add identity mapping for consistency with addTrack.
1010
      // Unless this is being used with a stream from addTrack.
1011
      if (!pc._reverseStreams[stream.id]) {
1012
        pc._streams[stream.id] = stream;
1013
        pc._reverseStreams[stream.id] = stream;
1014
      }
1015
      origAddStream.apply(pc, [stream]);
1016
    };
1017
1018
    var origRemoveStream = window.RTCPeerConnection.prototype.removeStream;
1019
    window.RTCPeerConnection.prototype.removeStream = function(stream) {
1020
      var pc = this;
1021
      pc._streams = pc._streams || {};
1022
      pc._reverseStreams = pc._reverseStreams || {};
1023
1024
      origRemoveStream.apply(pc, [(pc._streams[stream.id] || stream)]);
1025
      delete pc._reverseStreams[(pc._streams[stream.id] ?
1026
          pc._streams[stream.id].id : stream.id)];
1027
      delete pc._streams[stream.id];
1028
    };
1029
1030
    window.RTCPeerConnection.prototype.addTrack = function(track, stream) {
1031
      var pc = this;
1032
      if (pc.signalingState === 'closed') {
1033
        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...
1034
          'The RTCPeerConnection\'s signalingState is \'closed\'.',
1035
          'InvalidStateError');
1036
      }
1037
      var streams = [].slice.call(arguments, 1);
1038
      if (streams.length !== 1 ||
1039
          !streams[0].getTracks().find(function(t) {
1040
            return t === track;
1041
          })) {
1042
        // this is not fully correct but all we can manage without
1043
        // [[associated MediaStreams]] internal slot.
1044
        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...
1045
          'The adapter.js addTrack polyfill only supports a single ' +
1046
          ' stream which is associated with the specified track.',
1047
          'NotSupportedError');
1048
      }
1049
1050
      var alreadyExists = pc.getSenders().find(function(s) {
1051
        return s.track === track;
1052
      });
1053
      if (alreadyExists) {
1054
        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...
1055
            'InvalidAccessError');
1056
      }
1057
1058
      pc._streams = pc._streams || {};
1059
      pc._reverseStreams = pc._reverseStreams || {};
1060
      var oldStream = pc._streams[stream.id];
1061
      if (oldStream) {
1062
        // this is using odd Chrome behaviour, use with caution:
1063
        // https://bugs.chromium.org/p/webrtc/issues/detail?id=7815
1064
        oldStream.addTrack(track);
1065
        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...
1066
      } else {
1067
        var newStream = new window.MediaStream([track]);
1068
        pc._streams[stream.id] = newStream;
1069
        pc._reverseStreams[newStream.id] = stream;
1070
        pc.addStream(newStream);
1071
      }
1072
      return pc.getSenders().find(function(s) {
1073
        return s.track === track;
1074
      });
1075
    };
1076
  },
1077
1078
  shimPeerConnection: function(window) {
1079
    var browserDetails = utils.detectBrowser(window);
1080
1081
    // The RTCPeerConnection object.
1082
    if (!window.RTCPeerConnection) {
1083
      window.RTCPeerConnection = function(pcConfig, pcConstraints) {
1084
        // Translate iceTransportPolicy to iceTransports,
1085
        // see https://code.google.com/p/webrtc/issues/detail?id=4869
1086
        // this was fixed in M56 along with unprefixing RTCPeerConnection.
1087
        logging('PeerConnection');
1088
        if (pcConfig && pcConfig.iceTransportPolicy) {
1089
          pcConfig.iceTransports = pcConfig.iceTransportPolicy;
1090
        }
1091
1092
        return new window.webkitRTCPeerConnection(pcConfig, pcConstraints);
1093
      };
1094
      window.RTCPeerConnection.prototype =
1095
          window.webkitRTCPeerConnection.prototype;
1096
      // wrap static methods. Currently just generateCertificate.
1097
      if (window.webkitRTCPeerConnection.generateCertificate) {
1098
        Object.defineProperty(window.RTCPeerConnection, 'generateCertificate', {
1099
          get: function() {
1100
            return window.webkitRTCPeerConnection.generateCertificate;
1101
          }
1102
        });
1103
      }
1104
    } else {
1105
      // migrate from non-spec RTCIceServer.url to RTCIceServer.urls
1106
      var OrigPeerConnection = window.RTCPeerConnection;
1107
      window.RTCPeerConnection = function(pcConfig, pcConstraints) {
1108
        if (pcConfig && pcConfig.iceServers) {
1109
          var newIceServers = [];
1110
          for (var i = 0; i < pcConfig.iceServers.length; i++) {
1111
            var server = pcConfig.iceServers[i];
1112
            if (!server.hasOwnProperty('urls') &&
1113
                server.hasOwnProperty('url')) {
1114
              console.warn('RTCIceServer.url is deprecated! Use urls instead.');
1115
              server = JSON.parse(JSON.stringify(server));
1116
              server.urls = server.url;
1117
              newIceServers.push(server);
1118
            } else {
1119
              newIceServers.push(pcConfig.iceServers[i]);
1120
            }
1121
          }
1122
          pcConfig.iceServers = newIceServers;
1123
        }
1124
        return new OrigPeerConnection(pcConfig, pcConstraints);
1125
      };
1126
      window.RTCPeerConnection.prototype = OrigPeerConnection.prototype;
1127
      // wrap static methods. Currently just generateCertificate.
1128
      Object.defineProperty(window.RTCPeerConnection, 'generateCertificate', {
1129
        get: function() {
1130
          return OrigPeerConnection.generateCertificate;
1131
        }
1132
      });
1133
    }
1134
1135
    var origGetStats = window.RTCPeerConnection.prototype.getStats;
1136
    window.RTCPeerConnection.prototype.getStats = function(selector,
1137
        successCallback, errorCallback) {
1138
      var self = this;
1139
      var args = arguments;
1140
1141
      // If selector is a function then we are in the old style stats so just
1142
      // pass back the original getStats format to avoid breaking old users.
1143
      if (arguments.length > 0 && typeof selector === 'function') {
1144
        return origGetStats.apply(this, arguments);
1145
      }
1146
1147
      // When spec-style getStats is supported, return those when called with
1148
      // either no arguments or the selector argument is null.
1149
      if (origGetStats.length === 0 && (arguments.length === 0 ||
1150
          typeof arguments[0] !== 'function')) {
1151
        return origGetStats.apply(this, []);
1152
      }
1153
1154
      var fixChromeStats_ = function(response) {
1155
        var standardReport = {};
1156
        var reports = response.result();
1157
        reports.forEach(function(report) {
1158
          var standardStats = {
1159
            id: report.id,
1160
            timestamp: report.timestamp,
1161
            type: {
1162
              localcandidate: 'local-candidate',
1163
              remotecandidate: 'remote-candidate'
1164
            }[report.type] || report.type
1165
          };
1166
          report.names().forEach(function(name) {
1167
            standardStats[name] = report.stat(name);
1168
          });
1169
          standardReport[standardStats.id] = standardStats;
1170
        });
1171
1172
        return standardReport;
1173
      };
1174
1175
      // shim getStats with maplike support
1176
      var makeMapStats = function(stats) {
1177
        return new Map(Object.keys(stats).map(function(key) {
1178
          return [key, stats[key]];
1179
        }));
1180
      };
1181
1182
      if (arguments.length >= 2) {
1183
        var successCallbackWrapper_ = function(response) {
1184
          args[1](makeMapStats(fixChromeStats_(response)));
1185
        };
1186
1187
        return origGetStats.apply(this, [successCallbackWrapper_,
1188
          arguments[0]]);
1189
      }
1190
1191
      // promise-support
1192
      return new Promise(function(resolve, reject) {
1193
        origGetStats.apply(self, [
1194
          function(response) {
1195
            resolve(makeMapStats(fixChromeStats_(response)));
1196
          }, reject]);
1197
      }).then(successCallback, errorCallback);
1198
    };
1199
1200
    // add promise support -- natively available in Chrome 51
1201
    if (browserDetails.version < 51) {
1202
      ['setLocalDescription', 'setRemoteDescription', 'addIceCandidate']
1203
          .forEach(function(method) {
1204
            var nativeMethod = window.RTCPeerConnection.prototype[method];
1205
            window.RTCPeerConnection.prototype[method] = function() {
1206
              var args = arguments;
1207
              var self = this;
1208
              var promise = new Promise(function(resolve, reject) {
1209
                nativeMethod.apply(self, [args[0], resolve, reject]);
1210
              });
1211
              if (args.length < 2) {
1212
                return promise;
1213
              }
1214
              return promise.then(function() {
1215
                args[1].apply(null, []);
1216
              },
1217
              function(err) {
1218
                if (args.length >= 3) {
1219
                  args[2].apply(null, [err]);
1220
                }
1221
              });
1222
            };
1223
          });
1224
    }
1225
1226
    // promise support for createOffer and createAnswer. Available (without
1227
    // bugs) since M52: crbug/619289
1228
    if (browserDetails.version < 52) {
1229
      ['createOffer', 'createAnswer'].forEach(function(method) {
1230
        var nativeMethod = window.RTCPeerConnection.prototype[method];
1231
        window.RTCPeerConnection.prototype[method] = function() {
1232
          var self = this;
1233
          if (arguments.length < 1 || (arguments.length === 1 &&
1234
              typeof arguments[0] === 'object')) {
1235
            var opts = arguments.length === 1 ? arguments[0] : undefined;
1236
            return new Promise(function(resolve, reject) {
1237
              nativeMethod.apply(self, [resolve, reject, opts]);
1238
            });
1239
          }
1240
          return nativeMethod.apply(this, arguments);
1241
        };
1242
      });
1243
    }
1244
1245
    // shim implicit creation of RTCSessionDescription/RTCIceCandidate
1246
    ['setLocalDescription', 'setRemoteDescription', 'addIceCandidate']
1247
        .forEach(function(method) {
1248
          var nativeMethod = window.RTCPeerConnection.prototype[method];
1249
          window.RTCPeerConnection.prototype[method] = function() {
1250
            arguments[0] = new ((method === 'addIceCandidate') ?
1251
                window.RTCIceCandidate :
1252
                window.RTCSessionDescription)(arguments[0]);
1253
            return nativeMethod.apply(this, arguments);
1254
          };
1255
        });
1256
1257
    // support for addIceCandidate(null or undefined)
1258
    var nativeAddIceCandidate =
1259
        window.RTCPeerConnection.prototype.addIceCandidate;
1260
    window.RTCPeerConnection.prototype.addIceCandidate = function() {
1261
      if (!arguments[0]) {
1262
        if (arguments[1]) {
1263
          arguments[1].apply(null);
1264
        }
1265
        return Promise.resolve();
1266
      }
1267
      return nativeAddIceCandidate.apply(this, arguments);
1268
    };
1269
  }
1270
};
1271
1272
1273
// Expose public methods.
1274
module.exports = {
1275
  shimMediaStream: chromeShim.shimMediaStream,
1276
  shimOnTrack: chromeShim.shimOnTrack,
1277
  shimAddTrack: chromeShim.shimAddTrack,
1278
  shimGetSendersWithDtmf: chromeShim.shimGetSendersWithDtmf,
1279
  shimSourceObject: chromeShim.shimSourceObject,
1280
  shimPeerConnection: chromeShim.shimPeerConnection,
1281
  shimGetUserMedia: require('./getusermedia')
1282
};
1283
1284
},{"../utils.js":12,"./getusermedia":5}],5:[function(require,module,exports){
1285
/*
1286
 *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
1287
 *
1288
 *  Use of this source code is governed by a BSD-style license
1289
 *  that can be found in the LICENSE file in the root of the source
1290
 *  tree.
1291
 */
1292
 /* eslint-env node */
1293
'use strict';
1294
var utils = require('../utils.js');
1295
var logging = utils.log;
1296
1297
// Expose public methods.
1298
module.exports = function(window) {
1299
  var browserDetails = utils.detectBrowser(window);
1300
  var navigator = window && window.navigator;
1301
1302
  var constraintsToChrome_ = function(c) {
1303
    if (typeof c !== 'object' || c.mandatory || c.optional) {
1304
      return c;
1305
    }
1306
    var cc = {};
1307
    Object.keys(c).forEach(function(key) {
1308
      if (key === 'require' || key === 'advanced' || key === 'mediaSource') {
1309
        return;
1310
      }
1311
      var r = (typeof c[key] === 'object') ? c[key] : {ideal: c[key]};
1312
      if (r.exact !== undefined && typeof r.exact === 'number') {
1313
        r.min = r.max = r.exact;
1314
      }
1315
      var oldname_ = function(prefix, name) {
1316
        if (prefix) {
1317
          return prefix + name.charAt(0).toUpperCase() + name.slice(1);
1318
        }
1319
        return (name === 'deviceId') ? 'sourceId' : name;
1320
      };
1321
      if (r.ideal !== undefined) {
1322
        cc.optional = cc.optional || [];
1323
        var oc = {};
1324
        if (typeof r.ideal === 'number') {
1325
          oc[oldname_('min', key)] = r.ideal;
1326
          cc.optional.push(oc);
1327
          oc = {};
1328
          oc[oldname_('max', key)] = r.ideal;
1329
          cc.optional.push(oc);
1330
        } else {
1331
          oc[oldname_('', key)] = r.ideal;
1332
          cc.optional.push(oc);
1333
        }
1334
      }
1335
      if (r.exact !== undefined && typeof r.exact !== 'number') {
1336
        cc.mandatory = cc.mandatory || {};
1337
        cc.mandatory[oldname_('', key)] = r.exact;
1338
      } else {
1339
        ['min', 'max'].forEach(function(mix) {
1340
          if (r[mix] !== undefined) {
1341
            cc.mandatory = cc.mandatory || {};
1342
            cc.mandatory[oldname_(mix, key)] = r[mix];
1343
          }
1344
        });
1345
      }
1346
    });
1347
    if (c.advanced) {
1348
      cc.optional = (cc.optional || []).concat(c.advanced);
1349
    }
1350
    return cc;
1351
  };
1352
1353
  var shimConstraints_ = function(constraints, func) {
1354
    constraints = JSON.parse(JSON.stringify(constraints));
1355
    if (constraints && typeof constraints.audio === 'object') {
1356
      var remap = function(obj, a, b) {
1357
        if (a in obj && !(b in obj)) {
1358
          obj[b] = obj[a];
1359
          delete obj[a];
1360
        }
1361
      };
1362
      constraints = JSON.parse(JSON.stringify(constraints));
1363
      remap(constraints.audio, 'autoGainControl', 'googAutoGainControl');
1364
      remap(constraints.audio, 'noiseSuppression', 'googNoiseSuppression');
1365
      constraints.audio = constraintsToChrome_(constraints.audio);
1366
    }
1367
    if (constraints && typeof constraints.video === 'object') {
1368
      // Shim facingMode for mobile & surface pro.
1369
      var face = constraints.video.facingMode;
1370
      face = face && ((typeof face === 'object') ? face : {ideal: face});
1371
      var getSupportedFacingModeLies = browserDetails.version < 61;
1372
1373
      if ((face && (face.exact === 'user' || face.exact === 'environment' ||
1374
                    face.ideal === 'user' || face.ideal === 'environment')) &&
1375
          !(navigator.mediaDevices.getSupportedConstraints &&
1376
            navigator.mediaDevices.getSupportedConstraints().facingMode &&
1377
            !getSupportedFacingModeLies)) {
1378
        delete constraints.video.facingMode;
1379
        var matches;
1380
        if (face.exact === 'environment' || face.ideal === 'environment') {
1381
          matches = ['back', 'rear'];
1382
        } else if (face.exact === 'user' || face.ideal === 'user') {
1383
          matches = ['front'];
1384
        }
1385
        if (matches) {
1386
          // Look for matches in label, or use last cam for back (typical).
1387
          return navigator.mediaDevices.enumerateDevices()
1388
          .then(function(devices) {
1389
            devices = devices.filter(function(d) {
1390
              return d.kind === 'videoinput';
1391
            });
1392
            var dev = devices.find(function(d) {
1393
              return matches.some(function(match) {
1394
                return d.label.toLowerCase().indexOf(match) !== -1;
1395
              });
1396
            });
1397
            if (!dev && devices.length && matches.indexOf('back') !== -1) {
1398
              dev = devices[devices.length - 1]; // more likely the back cam
1399
            }
1400
            if (dev) {
1401
              constraints.video.deviceId = face.exact ? {exact: dev.deviceId} :
1402
                                                        {ideal: dev.deviceId};
1403
            }
1404
            constraints.video = constraintsToChrome_(constraints.video);
1405
            logging('chrome: ' + JSON.stringify(constraints));
1406
            return func(constraints);
1407
          });
1408
        }
1409
      }
1410
      constraints.video = constraintsToChrome_(constraints.video);
1411
    }
1412
    logging('chrome: ' + JSON.stringify(constraints));
1413
    return func(constraints);
1414
  };
1415
1416
  var shimError_ = function(e) {
1417
    return {
1418
      name: {
1419
        PermissionDeniedError: 'NotAllowedError',
1420
        InvalidStateError: 'NotReadableError',
1421
        DevicesNotFoundError: 'NotFoundError',
1422
        ConstraintNotSatisfiedError: 'OverconstrainedError',
1423
        TrackStartError: 'NotReadableError',
1424
        MediaDeviceFailedDueToShutdown: 'NotReadableError',
1425
        MediaDeviceKillSwitchOn: 'NotReadableError'
1426
      }[e.name] || e.name,
1427
      message: e.message,
1428
      constraint: e.constraintName,
1429
      toString: function() {
1430
        return this.name + (this.message && ': ') + this.message;
1431
      }
1432
    };
1433
  };
1434
1435
  var getUserMedia_ = function(constraints, onSuccess, onError) {
1436
    shimConstraints_(constraints, function(c) {
1437
      navigator.webkitGetUserMedia(c, onSuccess, function(e) {
1438
        onError(shimError_(e));
1439
      });
1440
    });
1441
  };
1442
1443
  navigator.getUserMedia = getUserMedia_;
1444
1445
  // Returns the result of getUserMedia as a Promise.
1446
  var getUserMediaPromise_ = function(constraints) {
1447
    return new Promise(function(resolve, reject) {
1448
      navigator.getUserMedia(constraints, resolve, reject);
1449
    });
1450
  };
1451
1452
  if (!navigator.mediaDevices) {
1453
    navigator.mediaDevices = {
1454
      getUserMedia: getUserMediaPromise_,
1455
      enumerateDevices: function() {
1456
        return new Promise(function(resolve) {
1457
          var kinds = {audio: 'audioinput', video: 'videoinput'};
1458
          return window.MediaStreamTrack.getSources(function(devices) {
1459
            resolve(devices.map(function(device) {
1460
              return {label: device.label,
1461
                kind: kinds[device.kind],
1462
                deviceId: device.id,
1463
                groupId: ''};
1464
            }));
1465
          });
1466
        });
1467
      },
1468
      getSupportedConstraints: function() {
1469
        return {
1470
          deviceId: true, echoCancellation: true, facingMode: true,
1471
          frameRate: true, height: true, width: true
1472
        };
1473
      }
1474
    };
1475
  }
1476
1477
  // A shim for getUserMedia method on the mediaDevices object.
1478
  // TODO(KaptenJansson) remove once implemented in Chrome stable.
1479
  if (!navigator.mediaDevices.getUserMedia) {
1480
    navigator.mediaDevices.getUserMedia = function(constraints) {
1481
      return getUserMediaPromise_(constraints);
1482
    };
1483
  } else {
1484
    // Even though Chrome 45 has navigator.mediaDevices and a getUserMedia
1485
    // function which returns a Promise, it does not accept spec-style
1486
    // constraints.
1487
    var origGetUserMedia = navigator.mediaDevices.getUserMedia.
1488
        bind(navigator.mediaDevices);
1489
    navigator.mediaDevices.getUserMedia = function(cs) {
1490
      return shimConstraints_(cs, function(c) {
1491
        return origGetUserMedia(c).then(function(stream) {
1492
          if (c.audio && !stream.getAudioTracks().length ||
1493
              c.video && !stream.getVideoTracks().length) {
1494
            stream.getTracks().forEach(function(track) {
1495
              track.stop();
1496
            });
1497
            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...
1498
          }
1499
          return stream;
1500
        }, function(e) {
1501
          return Promise.reject(shimError_(e));
1502
        });
1503
      });
1504
    };
1505
  }
1506
1507
  // Dummy devicechange event methods.
1508
  // TODO(KaptenJansson) remove once implemented in Chrome stable.
1509
  if (typeof navigator.mediaDevices.addEventListener === 'undefined') {
1510
    navigator.mediaDevices.addEventListener = function() {
1511
      logging('Dummy mediaDevices.addEventListener called.');
1512
    };
1513
  }
1514
  if (typeof navigator.mediaDevices.removeEventListener === 'undefined') {
1515
    navigator.mediaDevices.removeEventListener = function() {
1516
      logging('Dummy mediaDevices.removeEventListener called.');
1517
    };
1518
  }
1519
};
1520
1521
},{"../utils.js":12}],6:[function(require,module,exports){
1522
/*
1523
 *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
1524
 *
1525
 *  Use of this source code is governed by a BSD-style license
1526
 *  that can be found in the LICENSE file in the root of the source
1527
 *  tree.
1528
 */
1529
 /* eslint-env node */
1530
'use strict';
1531
1532
var utils = require('../utils');
1533
var shimRTCPeerConnection = require('./rtcpeerconnection_shim');
1534
1535
module.exports = {
1536
  shimGetUserMedia: require('./getusermedia'),
1537
  shimPeerConnection: function(window) {
1538
    var browserDetails = utils.detectBrowser(window);
1539
1540
    if (window.RTCIceGatherer) {
1541
      // ORTC defines an RTCIceCandidate object but no constructor.
1542
      // Not implemented in Edge.
1543
      if (!window.RTCIceCandidate) {
1544
        window.RTCIceCandidate = function(args) {
1545
          return args;
1546
        };
1547
      }
1548
      // ORTC does not have a session description object but
1549
      // other browsers (i.e. Chrome) that will support both PC and ORTC
1550
      // in the future might have this defined already.
1551
      if (!window.RTCSessionDescription) {
1552
        window.RTCSessionDescription = function(args) {
1553
          return args;
1554
        };
1555
      }
1556
      // this adds an additional event listener to MediaStrackTrack that signals
1557
      // when a tracks enabled property was changed. Workaround for a bug in
1558
      // addStream, see below. No longer required in 15025+
1559
      if (browserDetails.version < 15025) {
1560
        var origMSTEnabled = Object.getOwnPropertyDescriptor(
1561
            window.MediaStreamTrack.prototype, 'enabled');
1562
        Object.defineProperty(window.MediaStreamTrack.prototype, 'enabled', {
1563
          set: function(value) {
1564
            origMSTEnabled.set.call(this, value);
1565
            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...
1566
            ev.enabled = value;
1567
            this.dispatchEvent(ev);
1568
          }
1569
        });
1570
      }
1571
    }
1572
1573
    // ORTC defines the DTMF sender a bit different.
1574
    // https://github.com/w3c/ortc/issues/714
1575
    if (window.RTCRtpSender && !('dtmf' in window.RTCRtpSender.prototype)) {
1576
      Object.defineProperty(window.RTCRtpSender.prototype, 'dtmf', {
1577
        get: function() {
1578
          if (this._dtmf === undefined) {
1579
            if (this.track.kind === 'audio') {
1580
              this._dtmf = new window.RTCDtmfSender(this);
1581
            } else if (this.track.kind === 'video') {
1582
              this._dtmf = null;
1583
            }
1584
          }
1585
          return this._dtmf;
1586
        }
1587
      });
1588
    }
1589
1590
    window.RTCPeerConnection =
1591
        shimRTCPeerConnection(window, browserDetails.version);
1592
  },
1593
  shimReplaceTrack: function(window) {
1594
    // ORTC has replaceTrack -- https://github.com/w3c/ortc/issues/614
1595
    if (window.RTCRtpSender &&
1596
        !('replaceTrack' in window.RTCRtpSender.prototype)) {
1597
      window.RTCRtpSender.prototype.replaceTrack =
1598
          window.RTCRtpSender.prototype.setTrack;
1599
    }
1600
  }
1601
};
1602
1603
},{"../utils":12,"./getusermedia":7,"./rtcpeerconnection_shim":8}],7:[function(require,module,exports){
1604
/*
1605
 *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
1606
 *
1607
 *  Use of this source code is governed by a BSD-style license
1608
 *  that can be found in the LICENSE file in the root of the source
1609
 *  tree.
1610
 */
1611
 /* eslint-env node */
1612
'use strict';
1613
1614
// Expose public methods.
1615
module.exports = function(window) {
1616
  var navigator = window && window.navigator;
1617
1618
  var shimError_ = function(e) {
1619
    return {
1620
      name: {PermissionDeniedError: 'NotAllowedError'}[e.name] || e.name,
1621
      message: e.message,
1622
      constraint: e.constraint,
1623
      toString: function() {
1624
        return this.name;
1625
      }
1626
    };
1627
  };
1628
1629
  // getUserMedia error shim.
1630
  var origGetUserMedia = navigator.mediaDevices.getUserMedia.
1631
      bind(navigator.mediaDevices);
1632
  navigator.mediaDevices.getUserMedia = function(c) {
1633
    return origGetUserMedia(c).catch(function(e) {
1634
      return Promise.reject(shimError_(e));
1635
    });
1636
  };
1637
};
1638
1639
},{}],8:[function(require,module,exports){
1640
/*
1641
 *  Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
1642
 *
1643
 *  Use of this source code is governed by a BSD-style license
1644
 *  that can be found in the LICENSE file in the root of the source
1645
 *  tree.
1646
 */
1647
 /* eslint-env node */
1648
'use strict';
1649
1650
var SDPUtils = require('sdp');
1651
1652
// sort tracks such that they follow an a-v-a-v...
1653
// pattern.
1654
function sortTracks(tracks) {
1655
  var audioTracks = tracks.filter(function(track) {
1656
    return track.kind === 'audio';
1657
  });
1658
  var videoTracks = tracks.filter(function(track) {
1659
    return track.kind === 'video';
1660
  });
1661
  tracks = [];
1662
  while (audioTracks.length || videoTracks.length) {
1663
    if (audioTracks.length) {
1664
      tracks.push(audioTracks.shift());
1665
    }
1666
    if (videoTracks.length) {
1667
      tracks.push(videoTracks.shift());
1668
    }
1669
  }
1670
  return tracks;
1671
}
1672
1673
// Edge does not like
1674
// 1) stun:
1675
// 2) turn: that does not have all of turn:host:port?transport=udp
1676
// 3) turn: with ipv6 addresses
1677
// 4) turn: occurring muliple times
1678
function filterIceServers(iceServers, edgeVersion) {
1679
  var hasTurn = false;
1680
  iceServers = JSON.parse(JSON.stringify(iceServers));
1681
  return iceServers.filter(function(server) {
1682
    if (server && (server.urls || server.url)) {
1683
      var urls = server.urls || server.url;
1684
      if (server.url && !server.urls) {
1685
        console.warn('RTCIceServer.url is deprecated! Use urls instead.');
1686
      }
1687
      var isString = typeof urls === 'string';
1688
      if (isString) {
1689
        urls = [urls];
1690
      }
1691
      urls = urls.filter(function(url) {
1692
        var validTurn = url.indexOf('turn:') === 0 &&
1693
            url.indexOf('transport=udp') !== -1 &&
1694
            url.indexOf('turn:[') === -1 &&
1695
            !hasTurn;
1696
1697
        if (validTurn) {
1698
          hasTurn = true;
1699
          return true;
1700
        }
1701
        return url.indexOf('stun:') === 0 && edgeVersion >= 14393;
1702
      });
1703
1704
      delete server.url;
1705
      server.urls = isString ? urls[0] : urls;
1706
      return !!urls.length;
1707
    }
1708
    return false;
1709
  });
1710
}
1711
1712
// Determines the intersection of local and remote capabilities.
1713
function getCommonCapabilities(localCapabilities, remoteCapabilities) {
1714
  var commonCapabilities = {
1715
    codecs: [],
1716
    headerExtensions: [],
1717
    fecMechanisms: []
1718
  };
1719
1720
  var findCodecByPayloadType = function(pt, codecs) {
1721
    pt = parseInt(pt, 10);
1722
    for (var i = 0; i < codecs.length; i++) {
1723
      if (codecs[i].payloadType === pt ||
1724
          codecs[i].preferredPayloadType === pt) {
1725
        return codecs[i];
1726
      }
1727
    }
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...
1728
  };
1729
1730
  var rtxCapabilityMatches = function(lRtx, rRtx, lCodecs, rCodecs) {
1731
    var lCodec = findCodecByPayloadType(lRtx.parameters.apt, lCodecs);
1732
    var rCodec = findCodecByPayloadType(rRtx.parameters.apt, rCodecs);
1733
    return lCodec && rCodec &&
1734
        lCodec.name.toLowerCase() === rCodec.name.toLowerCase();
1735
  };
1736
1737
  localCapabilities.codecs.forEach(function(lCodec) {
1738
    for (var i = 0; i < remoteCapabilities.codecs.length; i++) {
1739
      var rCodec = remoteCapabilities.codecs[i];
1740
      if (lCodec.name.toLowerCase() === rCodec.name.toLowerCase() &&
1741
          lCodec.clockRate === rCodec.clockRate) {
1742
        if (lCodec.name.toLowerCase() === 'rtx' &&
1743
            lCodec.parameters && rCodec.parameters.apt) {
1744
          // for RTX we need to find the local rtx that has a apt
1745
          // which points to the same local codec as the remote one.
1746
          if (!rtxCapabilityMatches(lCodec, rCodec,
1747
              localCapabilities.codecs, remoteCapabilities.codecs)) {
1748
            continue;
1749
          }
1750
        }
1751
        rCodec = JSON.parse(JSON.stringify(rCodec)); // deepcopy
1752
        // number of channels is the highest common number of channels
1753
        rCodec.numChannels = Math.min(lCodec.numChannels,
1754
            rCodec.numChannels);
1755
        // push rCodec so we reply with offerer payload type
1756
        commonCapabilities.codecs.push(rCodec);
1757
1758
        // determine common feedback mechanisms
1759
        rCodec.rtcpFeedback = rCodec.rtcpFeedback.filter(function(fb) {
1760
          for (var j = 0; j < lCodec.rtcpFeedback.length; j++) {
1761
            if (lCodec.rtcpFeedback[j].type === fb.type &&
1762
                lCodec.rtcpFeedback[j].parameter === fb.parameter) {
1763
              return true;
1764
            }
1765
          }
1766
          return false;
1767
        });
1768
        // FIXME: also need to determine .parameters
1769
        //  see https://github.com/openpeer/ortc/issues/569
1770
        break;
1771
      }
1772
    }
1773
  });
1774
1775
  localCapabilities.headerExtensions.forEach(function(lHeaderExtension) {
1776
    for (var i = 0; i < remoteCapabilities.headerExtensions.length;
1777
         i++) {
1778
      var rHeaderExtension = remoteCapabilities.headerExtensions[i];
1779
      if (lHeaderExtension.uri === rHeaderExtension.uri) {
1780
        commonCapabilities.headerExtensions.push(rHeaderExtension);
1781
        break;
1782
      }
1783
    }
1784
  });
1785
1786
  // FIXME: fecMechanisms
1787
  return commonCapabilities;
1788
}
1789
1790
// is action=setLocalDescription with type allowed in signalingState
1791
function isActionAllowedInSignalingState(action, type, signalingState) {
1792
  return {
1793
    offer: {
1794
      setLocalDescription: ['stable', 'have-local-offer'],
1795
      setRemoteDescription: ['stable', 'have-remote-offer']
1796
    },
1797
    answer: {
1798
      setLocalDescription: ['have-remote-offer', 'have-local-pranswer'],
1799
      setRemoteDescription: ['have-local-offer', 'have-remote-pranswer']
1800
    }
1801
  }[type][action].indexOf(signalingState) !== -1;
1802
}
1803
1804
module.exports = function(window, edgeVersion) {
1805
  var RTCPeerConnection = function(config) {
1806
    var self = this;
1807
1808
    var _eventTarget = document.createDocumentFragment();
1809
    ['addEventListener', 'removeEventListener', 'dispatchEvent']
1810
        .forEach(function(method) {
1811
          self[method] = _eventTarget[method].bind(_eventTarget);
1812
        });
1813
1814
    this.needNegotiation = false;
1815
1816
    this.onicecandidate = null;
1817
    this.onaddstream = null;
1818
    this.ontrack = null;
1819
    this.onremovestream = null;
1820
    this.onsignalingstatechange = null;
1821
    this.oniceconnectionstatechange = null;
1822
    this.onicegatheringstatechange = null;
1823
    this.onnegotiationneeded = null;
1824
    this.ondatachannel = null;
1825
    this.canTrickleIceCandidates = null;
1826
1827
    this.localStreams = [];
1828
    this.remoteStreams = [];
1829
    this.getLocalStreams = function() {
1830
      return self.localStreams;
1831
    };
1832
    this.getRemoteStreams = function() {
1833
      return self.remoteStreams;
1834
    };
1835
1836
    this.localDescription = new window.RTCSessionDescription({
1837
      type: '',
1838
      sdp: ''
1839
    });
1840
    this.remoteDescription = new window.RTCSessionDescription({
1841
      type: '',
1842
      sdp: ''
1843
    });
1844
    this.signalingState = 'stable';
1845
    this.iceConnectionState = 'new';
1846
    this.iceGatheringState = 'new';
1847
1848
    this.iceOptions = {
1849
      gatherPolicy: 'all',
1850
      iceServers: []
1851
    };
1852
    if (config && config.iceTransportPolicy) {
1853
      switch (config.iceTransportPolicy) {
1854
        case 'all':
1855
        case 'relay':
1856
          this.iceOptions.gatherPolicy = config.iceTransportPolicy;
1857
          break;
1858
        default:
1859
          // don't set iceTransportPolicy.
1860
          break;
1861
      }
1862
    }
1863
    this.usingBundle = config && config.bundlePolicy === 'max-bundle';
1864
1865
    if (config && config.iceServers) {
1866
      this.iceOptions.iceServers = filterIceServers(config.iceServers,
1867
          edgeVersion);
1868
    }
1869
    this._config = config || {};
1870
1871
    // per-track iceGathers, iceTransports, dtlsTransports, rtpSenders, ...
1872
    // everything that is needed to describe a SDP m-line.
1873
    this.transceivers = [];
1874
1875
    // since the iceGatherer is currently created in createOffer but we
1876
    // must not emit candidates until after setLocalDescription we buffer
1877
    // them in this array.
1878
    this._localIceCandidatesBuffer = [];
1879
1880
    this._sdpSessionId = SDPUtils.generateSessionId();
1881
  };
1882
1883
  RTCPeerConnection.prototype._emitGatheringStateChange = function() {
1884
    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...
1885
    this.dispatchEvent(event);
1886
    if (this.onicegatheringstatechange !== null) {
1887
      this.onicegatheringstatechange(event);
1888
    }
1889
  };
1890
1891
  RTCPeerConnection.prototype._emitBufferedCandidates = function() {
1892
    var self = this;
1893
    var sections = SDPUtils.splitSections(self.localDescription.sdp);
1894
    // FIXME: need to apply ice candidates in a way which is async but
1895
    // in-order
1896
    this._localIceCandidatesBuffer.forEach(function(event) {
1897
      var end = !event.candidate || Object.keys(event.candidate).length === 0;
1898
      if (end) {
1899
        for (var j = 1; j < sections.length; j++) {
1900
          if (sections[j].indexOf('\r\na=end-of-candidates\r\n') === -1) {
1901
            sections[j] += 'a=end-of-candidates\r\n';
1902
          }
1903
        }
1904
      } else {
1905
        sections[event.candidate.sdpMLineIndex + 1] +=
1906
            'a=' + event.candidate.candidate + '\r\n';
1907
      }
1908
      self.localDescription.sdp = sections.join('');
1909
      self.dispatchEvent(event);
1910
      if (self.onicecandidate !== null) {
1911
        self.onicecandidate(event);
1912
      }
1913
      if (!event.candidate && self.iceGatheringState !== 'complete') {
1914
        var complete = self.transceivers.every(function(transceiver) {
1915
          return transceiver.iceGatherer &&
1916
              transceiver.iceGatherer.state === 'completed';
1917
        });
1918
        if (complete && self.iceGatheringStateChange !== 'complete') {
1919
          self.iceGatheringState = 'complete';
1920
          self._emitGatheringStateChange();
1921
        }
1922
      }
1923
    });
1924
    this._localIceCandidatesBuffer = [];
1925
  };
1926
1927
  RTCPeerConnection.prototype.getConfiguration = function() {
1928
    return this._config;
1929
  };
1930
1931
  // internal helper to create a transceiver object.
1932
  // (whih is not yet the same as the WebRTC 1.0 transceiver)
1933
  RTCPeerConnection.prototype._createTransceiver = function(kind) {
1934
    var hasBundleTransport = this.transceivers.length > 0;
1935
    var transceiver = {
1936
      track: null,
1937
      iceGatherer: null,
1938
      iceTransport: null,
1939
      dtlsTransport: null,
1940
      localCapabilities: null,
1941
      remoteCapabilities: null,
1942
      rtpSender: null,
1943
      rtpReceiver: null,
1944
      kind: kind,
1945
      mid: null,
1946
      sendEncodingParameters: null,
1947
      recvEncodingParameters: null,
1948
      stream: null,
1949
      wantReceive: true
1950
    };
1951
    if (this.usingBundle && hasBundleTransport) {
1952
      transceiver.iceTransport = this.transceivers[0].iceTransport;
1953
      transceiver.dtlsTransport = this.transceivers[0].dtlsTransport;
1954
    } else {
1955
      var transports = this._createIceAndDtlsTransports();
1956
      transceiver.iceTransport = transports.iceTransport;
1957
      transceiver.dtlsTransport = transports.dtlsTransport;
1958
    }
1959
    this.transceivers.push(transceiver);
1960
    return transceiver;
1961
  };
1962
1963
  RTCPeerConnection.prototype.addTrack = function(track, stream) {
1964
    var transceiver;
1965
    for (var i = 0; i < this.transceivers.length; i++) {
1966
      if (!this.transceivers[i].track &&
1967
          this.transceivers[i].kind === track.kind) {
1968
        transceiver = this.transceivers[i];
1969
      }
1970
    }
1971
    if (!transceiver) {
1972
      transceiver = this._createTransceiver(track.kind);
1973
    }
1974
1975
    transceiver.track = track;
1976
    transceiver.stream = stream;
1977
    transceiver.rtpSender = new window.RTCRtpSender(track,
1978
        transceiver.dtlsTransport);
1979
1980
    this._maybeFireNegotiationNeeded();
1981
    return transceiver.rtpSender;
1982
  };
1983
1984
  RTCPeerConnection.prototype.addStream = function(stream) {
1985
    var self = this;
1986
    if (edgeVersion >= 15025) {
1987
      this.localStreams.push(stream);
1988
      stream.getTracks().forEach(function(track) {
1989
        self.addTrack(track, stream);
1990
      });
1991
    } else {
1992
      // Clone is necessary for local demos mostly, attaching directly
1993
      // to two different senders does not work (build 10547).
1994
      // Fixed in 15025 (or earlier)
1995
      var clonedStream = stream.clone();
1996
      stream.getTracks().forEach(function(track, idx) {
1997
        var clonedTrack = clonedStream.getTracks()[idx];
1998
        track.addEventListener('enabled', function(event) {
1999
          clonedTrack.enabled = event.enabled;
2000
        });
2001
      });
2002
      clonedStream.getTracks().forEach(function(track) {
2003
        self.addTrack(track, clonedStream);
2004
      });
2005
      this.localStreams.push(clonedStream);
2006
    }
2007
    this._maybeFireNegotiationNeeded();
2008
  };
2009
2010
  RTCPeerConnection.prototype.removeStream = function(stream) {
2011
    var idx = this.localStreams.indexOf(stream);
2012
    if (idx > -1) {
2013
      this.localStreams.splice(idx, 1);
2014
      this._maybeFireNegotiationNeeded();
2015
    }
2016
  };
2017
2018
  RTCPeerConnection.prototype.getSenders = function() {
2019
    return this.transceivers.filter(function(transceiver) {
2020
      return !!transceiver.rtpSender;
2021
    })
2022
    .map(function(transceiver) {
2023
      return transceiver.rtpSender;
2024
    });
2025
  };
2026
2027
  RTCPeerConnection.prototype.getReceivers = function() {
2028
    return this.transceivers.filter(function(transceiver) {
2029
      return !!transceiver.rtpReceiver;
2030
    })
2031
    .map(function(transceiver) {
2032
      return transceiver.rtpReceiver;
2033
    });
2034
  };
2035
2036
  // Create ICE gatherer and hook it up.
2037
  RTCPeerConnection.prototype._createIceGatherer = function(mid,
2038
      sdpMLineIndex) {
2039
    var self = this;
2040
    var iceGatherer = new window.RTCIceGatherer(self.iceOptions);
2041
    iceGatherer.onlocalcandidate = function(evt) {
2042
      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...
2043
      event.candidate = {sdpMid: mid, sdpMLineIndex: sdpMLineIndex};
2044
2045
      var cand = evt.candidate;
2046
      var end = !cand || Object.keys(cand).length === 0;
2047
      // Edge emits an empty object for RTCIceCandidateComplete‥
2048
      if (end) {
2049
        // polyfill since RTCIceGatherer.state is not implemented in
2050
        // Edge 10547 yet.
2051
        if (iceGatherer.state === undefined) {
2052
          iceGatherer.state = 'completed';
2053
        }
2054
      } else {
2055
        // RTCIceCandidate doesn't have a component, needs to be added
2056
        cand.component = 1;
2057
        event.candidate.candidate = SDPUtils.writeCandidate(cand);
2058
      }
2059
2060
      // update local description.
2061
      var sections = SDPUtils.splitSections(self.localDescription.sdp);
2062
      if (!end) {
2063
        sections[event.candidate.sdpMLineIndex + 1] +=
2064
            'a=' + event.candidate.candidate + '\r\n';
2065
      } else {
2066
        sections[event.candidate.sdpMLineIndex + 1] +=
2067
            'a=end-of-candidates\r\n';
2068
      }
2069
      self.localDescription.sdp = sections.join('');
2070
      var transceivers = self._pendingOffer ? self._pendingOffer :
2071
          self.transceivers;
2072
      var complete = transceivers.every(function(transceiver) {
2073
        return transceiver.iceGatherer &&
2074
            transceiver.iceGatherer.state === 'completed';
2075
      });
2076
2077
      // Emit candidate if localDescription is set.
2078
      // Also emits null candidate when all gatherers are complete.
2079
      switch (self.iceGatheringState) {
2080
        case 'new':
2081
          if (!end) {
2082
            self._localIceCandidatesBuffer.push(event);
2083
          }
2084
          if (end && complete) {
2085
            self._localIceCandidatesBuffer.push(
2086
                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...
2087
          }
2088
          break;
2089
        case 'gathering':
2090
          self._emitBufferedCandidates();
2091
          if (!end) {
2092
            self.dispatchEvent(event);
2093
            if (self.onicecandidate !== null) {
2094
              self.onicecandidate(event);
2095
            }
2096
          }
2097
          if (complete) {
2098
            self.dispatchEvent(new Event('icecandidate'));
2099
            if (self.onicecandidate !== null) {
2100
              self.onicecandidate(new Event('icecandidate'));
2101
            }
2102
            self.iceGatheringState = 'complete';
2103
            self._emitGatheringStateChange();
2104
          }
2105
          break;
2106
        case 'complete':
2107
          // should not happen... currently!
2108
          break;
2109
        default: // no-op.
2110
          break;
2111
      }
2112
    };
2113
    return iceGatherer;
2114
  };
2115
2116
  // Create ICE transport and DTLS transport.
2117
  RTCPeerConnection.prototype._createIceAndDtlsTransports = function() {
2118
    var self = this;
2119
    var iceTransport = new window.RTCIceTransport(null);
2120
    iceTransport.onicestatechange = function() {
2121
      self._updateConnectionState();
2122
    };
2123
2124
    var dtlsTransport = new window.RTCDtlsTransport(iceTransport);
2125
    dtlsTransport.ondtlsstatechange = function() {
2126
      self._updateConnectionState();
2127
    };
2128
    dtlsTransport.onerror = function() {
2129
      // onerror does not set state to failed by itself.
2130
      Object.defineProperty(dtlsTransport, 'state',
2131
          {value: 'failed', writable: true});
2132
      self._updateConnectionState();
2133
    };
2134
2135
    return {
2136
      iceTransport: iceTransport,
2137
      dtlsTransport: dtlsTransport
2138
    };
2139
  };
2140
2141
  // Destroy ICE gatherer, ICE transport and DTLS transport.
2142
  // Without triggering the callbacks.
2143
  RTCPeerConnection.prototype._disposeIceAndDtlsTransports = function(
2144
      sdpMLineIndex) {
2145
    var iceGatherer = this.transceivers[sdpMLineIndex].iceGatherer;
2146
    if (iceGatherer) {
2147
      delete iceGatherer.onlocalcandidate;
2148
      delete this.transceivers[sdpMLineIndex].iceGatherer;
2149
    }
2150
    var iceTransport = this.transceivers[sdpMLineIndex].iceTransport;
2151
    if (iceTransport) {
2152
      delete iceTransport.onicestatechange;
2153
      delete this.transceivers[sdpMLineIndex].iceTransport;
2154
    }
2155
    var dtlsTransport = this.transceivers[sdpMLineIndex].dtlsTransport;
2156
    if (dtlsTransport) {
2157
      delete dtlsTransport.ondtlsstatechange;
2158
      delete dtlsTransport.onerror;
2159
      delete this.transceivers[sdpMLineIndex].dtlsTransport;
2160
    }
2161
  };
2162
2163
  // Start the RTP Sender and Receiver for a transceiver.
2164
  RTCPeerConnection.prototype._transceive = function(transceiver,
2165
      send, recv) {
2166
    var params = getCommonCapabilities(transceiver.localCapabilities,
2167
        transceiver.remoteCapabilities);
2168
    if (send && transceiver.rtpSender) {
2169
      params.encodings = transceiver.sendEncodingParameters;
2170
      params.rtcp = {
2171
        cname: SDPUtils.localCName,
2172
        compound: transceiver.rtcpParameters.compound
2173
      };
2174
      if (transceiver.recvEncodingParameters.length) {
2175
        params.rtcp.ssrc = transceiver.recvEncodingParameters[0].ssrc;
2176
      }
2177
      transceiver.rtpSender.send(params);
2178
    }
2179
    if (recv && transceiver.rtpReceiver) {
2180
      // remove RTX field in Edge 14942
2181
      if (transceiver.kind === 'video'
2182
          && transceiver.recvEncodingParameters
2183
          && edgeVersion < 15019) {
2184
        transceiver.recvEncodingParameters.forEach(function(p) {
2185
          delete p.rtx;
2186
        });
2187
      }
2188
      params.encodings = transceiver.recvEncodingParameters;
2189
      params.rtcp = {
2190
        cname: transceiver.rtcpParameters.cname,
2191
        compound: transceiver.rtcpParameters.compound
2192
      };
2193
      if (transceiver.sendEncodingParameters.length) {
2194
        params.rtcp.ssrc = transceiver.sendEncodingParameters[0].ssrc;
2195
      }
2196
      transceiver.rtpReceiver.receive(params);
2197
    }
2198
  };
2199
2200
  RTCPeerConnection.prototype.setLocalDescription = function(description) {
2201
    var self = this;
2202
2203
    if (!isActionAllowedInSignalingState('setLocalDescription',
2204
        description.type, this.signalingState)) {
2205
      var e = new Error('Can not set local ' + description.type +
2206
          ' in state ' + this.signalingState);
2207
      e.name = 'InvalidStateError';
2208
      if (arguments.length > 2 && typeof arguments[2] === 'function') {
2209
        window.setTimeout(arguments[2], 0, e);
2210
      }
2211
      return Promise.reject(e);
2212
    }
2213
2214
    var sections;
2215
    var sessionpart;
2216
    if (description.type === 'offer') {
2217
      // FIXME: What was the purpose of this empty if statement?
2218
      // if (!this._pendingOffer) {
2219
      // } else {
2220
      if (this._pendingOffer) {
2221
        // VERY limited support for SDP munging. Limited to:
2222
        // * changing the order of codecs
2223
        sections = SDPUtils.splitSections(description.sdp);
2224
        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...
2225
        sections.forEach(function(mediaSection, sdpMLineIndex) {
2226
          var caps = SDPUtils.parseRtpParameters(mediaSection);
2227
          self._pendingOffer[sdpMLineIndex].localCapabilities = caps;
2228
        });
2229
        this.transceivers = this._pendingOffer;
2230
        delete this._pendingOffer;
2231
      }
2232
    } else if (description.type === 'answer') {
2233
      sections = SDPUtils.splitSections(self.remoteDescription.sdp);
2234
      sessionpart = sections.shift();
2235
      var isIceLite = SDPUtils.matchPrefix(sessionpart,
2236
          'a=ice-lite').length > 0;
2237
      sections.forEach(function(mediaSection, sdpMLineIndex) {
2238
        var transceiver = self.transceivers[sdpMLineIndex];
2239
        var iceGatherer = transceiver.iceGatherer;
2240
        var iceTransport = transceiver.iceTransport;
2241
        var dtlsTransport = transceiver.dtlsTransport;
2242
        var localCapabilities = transceiver.localCapabilities;
2243
        var remoteCapabilities = transceiver.remoteCapabilities;
2244
2245
        var rejected = SDPUtils.isRejected(mediaSection);
2246
2247
        if (!rejected && !transceiver.isDatachannel) {
2248
          var remoteIceParameters = SDPUtils.getIceParameters(
2249
              mediaSection, sessionpart);
2250
          var remoteDtlsParameters = SDPUtils.getDtlsParameters(
2251
              mediaSection, sessionpart);
2252
          if (isIceLite) {
2253
            remoteDtlsParameters.role = 'server';
2254
          }
2255
2256
          if (!self.usingBundle || sdpMLineIndex === 0) {
2257
            iceTransport.start(iceGatherer, remoteIceParameters,
2258
                isIceLite ? 'controlling' : 'controlled');
2259
            dtlsTransport.start(remoteDtlsParameters);
2260
          }
2261
2262
          // Calculate intersection of capabilities.
2263
          var params = getCommonCapabilities(localCapabilities,
2264
              remoteCapabilities);
2265
2266
          // Start the RTCRtpSender. The RTCRtpReceiver for this
2267
          // transceiver has already been started in setRemoteDescription.
2268
          self._transceive(transceiver,
2269
              params.codecs.length > 0,
2270
              false);
2271
        }
2272
      });
2273
    }
2274
2275
    this.localDescription = {
2276
      type: description.type,
2277
      sdp: description.sdp
2278
    };
2279
    switch (description.type) {
2280
      case 'offer':
2281
        this._updateSignalingState('have-local-offer');
2282
        break;
2283
      case 'answer':
2284
        this._updateSignalingState('stable');
2285
        break;
2286
      default:
2287
        throw new TypeError('unsupported type "' + description.type +
2288
            '"');
2289
    }
2290
2291
    // If a success callback was provided, emit ICE candidates after it
2292
    // has been executed. Otherwise, emit callback after the Promise is
2293
    // resolved.
2294
    var hasCallback = arguments.length > 1 &&
2295
      typeof arguments[1] === 'function';
2296
    if (hasCallback) {
2297
      var cb = arguments[1];
2298
      window.setTimeout(function() {
2299
        cb();
2300
        if (self.iceGatheringState === 'new') {
2301
          self.iceGatheringState = 'gathering';
2302
          self._emitGatheringStateChange();
2303
        }
2304
        self._emitBufferedCandidates();
2305
      }, 0);
2306
    }
2307
    var p = Promise.resolve();
2308
    p.then(function() {
2309
      if (!hasCallback) {
2310
        if (self.iceGatheringState === 'new') {
2311
          self.iceGatheringState = 'gathering';
2312
          self._emitGatheringStateChange();
2313
        }
2314
        // Usually candidates will be emitted earlier.
2315
        window.setTimeout(self._emitBufferedCandidates.bind(self), 500);
2316
      }
2317
    });
2318
    return p;
2319
  };
2320
2321
  RTCPeerConnection.prototype.setRemoteDescription = function(description) {
2322
    var self = this;
2323
2324
    if (!isActionAllowedInSignalingState('setRemoteDescription',
2325
        description.type, this.signalingState)) {
2326
      var e = new Error('Can not set remote ' + description.type +
2327
          ' in state ' + this.signalingState);
2328
      e.name = 'InvalidStateError';
2329
      if (arguments.length > 2 && typeof arguments[2] === 'function') {
2330
        window.setTimeout(arguments[2], 0, e);
2331
      }
2332
      return Promise.reject(e);
2333
    }
2334
2335
    var streams = {};
2336
    var receiverList = [];
2337
    var sections = SDPUtils.splitSections(description.sdp);
2338
    var sessionpart = sections.shift();
2339
    var isIceLite = SDPUtils.matchPrefix(sessionpart,
2340
        'a=ice-lite').length > 0;
2341
    var usingBundle = SDPUtils.matchPrefix(sessionpart,
2342
        'a=group:BUNDLE ').length > 0;
2343
    this.usingBundle = usingBundle;
2344
    var iceOptions = SDPUtils.matchPrefix(sessionpart,
2345
        'a=ice-options:')[0];
2346
    if (iceOptions) {
2347
      this.canTrickleIceCandidates = iceOptions.substr(14).split(' ')
2348
          .indexOf('trickle') >= 0;
2349
    } else {
2350
      this.canTrickleIceCandidates = false;
2351
    }
2352
2353
    sections.forEach(function(mediaSection, sdpMLineIndex) {
2354
      var lines = SDPUtils.splitLines(mediaSection);
2355
      var kind = SDPUtils.getKind(mediaSection);
2356
      var rejected = SDPUtils.isRejected(mediaSection);
2357
      var protocol = lines[0].substr(2).split(' ')[2];
2358
2359
      var direction = SDPUtils.getDirection(mediaSection, sessionpart);
2360
      var remoteMsid = SDPUtils.parseMsid(mediaSection);
2361
2362
      var mid = SDPUtils.getMid(mediaSection) || SDPUtils.generateIdentifier();
2363
2364
      // Reject datachannels which are not implemented yet.
2365
      if (kind === 'application' && protocol === 'DTLS/SCTP') {
2366
        self.transceivers[sdpMLineIndex] = {
2367
          mid: mid,
2368
          isDatachannel: true
2369
        };
2370
        return;
2371
      }
2372
2373
      var transceiver;
2374
      var iceGatherer;
2375
      var iceTransport;
2376
      var dtlsTransport;
2377
      var rtpReceiver;
2378
      var sendEncodingParameters;
2379
      var recvEncodingParameters;
2380
      var localCapabilities;
2381
2382
      var track;
2383
      // FIXME: ensure the mediaSection has rtcp-mux set.
2384
      var remoteCapabilities = SDPUtils.parseRtpParameters(mediaSection);
2385
      var remoteIceParameters;
2386
      var remoteDtlsParameters;
2387
      if (!rejected) {
2388
        remoteIceParameters = SDPUtils.getIceParameters(mediaSection,
2389
            sessionpart);
2390
        remoteDtlsParameters = SDPUtils.getDtlsParameters(mediaSection,
2391
            sessionpart);
2392
        remoteDtlsParameters.role = 'client';
2393
      }
2394
      recvEncodingParameters =
2395
          SDPUtils.parseRtpEncodingParameters(mediaSection);
2396
2397
      var rtcpParameters = SDPUtils.parseRtcpParameters(mediaSection);
2398
2399
      var isComplete = SDPUtils.matchPrefix(mediaSection,
2400
          'a=end-of-candidates', sessionpart).length > 0;
2401
      var cands = SDPUtils.matchPrefix(mediaSection, 'a=candidate:')
2402
          .map(function(cand) {
2403
            return SDPUtils.parseCandidate(cand);
2404
          })
2405
          .filter(function(cand) {
2406
            return cand.component === '1' || cand.component === 1;
2407
          });
2408
2409
      // Check if we can use BUNDLE and dispose transports.
2410
      if ((description.type === 'offer' || description.type === 'answer') &&
2411
          !rejected && usingBundle && sdpMLineIndex > 0 &&
2412
          self.transceivers[sdpMLineIndex]) {
2413
        self._disposeIceAndDtlsTransports(sdpMLineIndex);
2414
        self.transceivers[sdpMLineIndex].iceGatherer =
2415
            self.transceivers[0].iceGatherer;
2416
        self.transceivers[sdpMLineIndex].iceTransport =
2417
            self.transceivers[0].iceTransport;
2418
        self.transceivers[sdpMLineIndex].dtlsTransport =
2419
            self.transceivers[0].dtlsTransport;
2420
        if (self.transceivers[sdpMLineIndex].rtpSender) {
2421
          self.transceivers[sdpMLineIndex].rtpSender.setTransport(
2422
              self.transceivers[0].dtlsTransport);
2423
        }
2424
        if (self.transceivers[sdpMLineIndex].rtpReceiver) {
2425
          self.transceivers[sdpMLineIndex].rtpReceiver.setTransport(
2426
              self.transceivers[0].dtlsTransport);
2427
        }
2428
      }
2429
      if (description.type === 'offer' && !rejected) {
2430
        transceiver = self.transceivers[sdpMLineIndex] ||
2431
            self._createTransceiver(kind);
2432
        transceiver.mid = mid;
2433
2434
        if (!transceiver.iceGatherer) {
2435
          transceiver.iceGatherer = usingBundle && sdpMLineIndex > 0 ?
2436
              self.transceivers[0].iceGatherer :
2437
              self._createIceGatherer(mid, sdpMLineIndex);
2438
        }
2439
2440
        if (isComplete && cands.length &&
2441
            (!usingBundle || sdpMLineIndex === 0)) {
2442
          transceiver.iceTransport.setRemoteCandidates(cands);
2443
        }
2444
2445
        localCapabilities = window.RTCRtpReceiver.getCapabilities(kind);
2446
2447
        // filter RTX until additional stuff needed for RTX is implemented
2448
        // in adapter.js
2449
        if (edgeVersion < 15019) {
2450
          localCapabilities.codecs = localCapabilities.codecs.filter(
2451
              function(codec) {
2452
                return codec.name !== 'rtx';
2453
              });
2454
        }
2455
2456
        sendEncodingParameters = [{
2457
          ssrc: (2 * sdpMLineIndex + 2) * 1001
2458
        }];
2459
2460
        if (direction === 'sendrecv' || direction === 'sendonly') {
2461
          rtpReceiver = new window.RTCRtpReceiver(transceiver.dtlsTransport,
2462
              kind);
2463
2464
          track = rtpReceiver.track;
2465
          // FIXME: does not work with Plan B.
2466
          if (remoteMsid) {
2467
            if (!streams[remoteMsid.stream]) {
2468
              streams[remoteMsid.stream] = new window.MediaStream();
2469
              Object.defineProperty(streams[remoteMsid.stream], 'id', {
2470
                get: function() {
2471
                  return remoteMsid.stream;
2472
                }
2473
              });
2474
            }
2475
            Object.defineProperty(track, 'id', {
2476
              get: function() {
2477
                return remoteMsid.track;
2478
              }
2479
            });
2480
            streams[remoteMsid.stream].addTrack(track);
2481
            receiverList.push([track, rtpReceiver,
2482
              streams[remoteMsid.stream]]);
2483
          } else {
2484
            if (!streams.default) {
2485
              streams.default = new window.MediaStream();
2486
            }
2487
            streams.default.addTrack(track);
2488
            receiverList.push([track, rtpReceiver, streams.default]);
2489
          }
2490
        }
2491
2492
        transceiver.localCapabilities = localCapabilities;
2493
        transceiver.remoteCapabilities = remoteCapabilities;
2494
        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 2460 is false. Are you sure this can never be the case?
Loading history...
2495
        transceiver.rtcpParameters = rtcpParameters;
2496
        transceiver.sendEncodingParameters = sendEncodingParameters;
2497
        transceiver.recvEncodingParameters = recvEncodingParameters;
2498
2499
        // Start the RTCRtpReceiver now. The RTPSender is started in
2500
        // setLocalDescription.
2501
        self._transceive(self.transceivers[sdpMLineIndex],
2502
            false,
2503
            direction === 'sendrecv' || direction === 'sendonly');
2504
      } else if (description.type === 'answer' && !rejected) {
2505
        transceiver = self.transceivers[sdpMLineIndex];
2506
        iceGatherer = transceiver.iceGatherer;
2507
        iceTransport = transceiver.iceTransport;
2508
        dtlsTransport = transceiver.dtlsTransport;
2509
        rtpReceiver = transceiver.rtpReceiver;
2510
        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...
2511
        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...
2512
2513
        self.transceivers[sdpMLineIndex].recvEncodingParameters =
2514
            recvEncodingParameters;
2515
        self.transceivers[sdpMLineIndex].remoteCapabilities =
2516
            remoteCapabilities;
2517
        self.transceivers[sdpMLineIndex].rtcpParameters = rtcpParameters;
2518
2519
        if (!usingBundle || sdpMLineIndex === 0) {
2520
          if ((isIceLite || isComplete) && cands.length) {
2521
            iceTransport.setRemoteCandidates(cands);
2522
          }
2523
          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 2387 is false. Are you sure the function start handles undefined variables?
Loading history...
2524
              'controlling');
2525
          dtlsTransport.start(remoteDtlsParameters);
0 ignored issues
show
Bug introduced by
The variable remoteDtlsParameters does not seem to be initialized in case !rejected on line 2387 is false. Are you sure the function start handles undefined variables?
Loading history...
2526
        }
2527
2528
        self._transceive(transceiver,
2529
            direction === 'sendrecv' || direction === 'recvonly',
2530
            direction === 'sendrecv' || direction === 'sendonly');
2531
2532
        if (rtpReceiver &&
2533
            (direction === 'sendrecv' || direction === 'sendonly')) {
2534
          track = rtpReceiver.track;
2535
          if (remoteMsid) {
2536
            if (!streams[remoteMsid.stream]) {
2537
              streams[remoteMsid.stream] = new window.MediaStream();
2538
            }
2539
            streams[remoteMsid.stream].addTrack(track);
2540
            receiverList.push([track, rtpReceiver, streams[remoteMsid.stream]]);
2541
          } else {
2542
            if (!streams.default) {
2543
              streams.default = new window.MediaStream();
2544
            }
2545
            streams.default.addTrack(track);
2546
            receiverList.push([track, rtpReceiver, streams.default]);
2547
          }
2548
        } else {
2549
          // FIXME: actually the receiver should be created later.
2550
          delete transceiver.rtpReceiver;
2551
        }
2552
      }
2553
    });
2554
2555
    this.remoteDescription = {
2556
      type: description.type,
2557
      sdp: description.sdp
2558
    };
2559
    switch (description.type) {
2560
      case 'offer':
2561
        this._updateSignalingState('have-remote-offer');
2562
        break;
2563
      case 'answer':
2564
        this._updateSignalingState('stable');
2565
        break;
2566
      default:
2567
        throw new TypeError('unsupported type "' + description.type +
2568
            '"');
2569
    }
2570
    Object.keys(streams).forEach(function(sid) {
2571
      var stream = streams[sid];
2572
      if (stream.getTracks().length) {
2573
        self.remoteStreams.push(stream);
2574
        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...
2575
        event.stream = stream;
2576
        self.dispatchEvent(event);
2577
        if (self.onaddstream !== null) {
2578
          window.setTimeout(function() {
2579
            self.onaddstream(event);
2580
          }, 0);
2581
        }
2582
2583
        receiverList.forEach(function(item) {
2584
          var track = item[0];
2585
          var receiver = item[1];
2586
          if (stream.id !== item[2].id) {
2587
            return;
2588
          }
2589
          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...
2590
          trackEvent.track = track;
2591
          trackEvent.receiver = receiver;
2592
          trackEvent.streams = [stream];
2593
          self.dispatchEvent(trackEvent);
2594
          if (self.ontrack !== null) {
2595
            window.setTimeout(function() {
2596
              self.ontrack(trackEvent);
2597
            }, 0);
2598
          }
2599
        });
2600
      }
2601
    });
2602
2603
    // check whether addIceCandidate({}) was called within four seconds after
2604
    // setRemoteDescription.
2605
    window.setTimeout(function() {
2606
      if (!(self && self.transceivers)) {
2607
        return;
2608
      }
2609
      self.transceivers.forEach(function(transceiver) {
2610
        if (transceiver.iceTransport &&
2611
            transceiver.iceTransport.state === 'new' &&
2612
            transceiver.iceTransport.getRemoteCandidates().length > 0) {
2613
          console.warn('Timeout for addRemoteCandidate. Consider sending ' +
2614
              'an end-of-candidates notification');
2615
          transceiver.iceTransport.addRemoteCandidate({});
2616
        }
2617
      });
2618
    }, 4000);
2619
2620
    if (arguments.length > 1 && typeof arguments[1] === 'function') {
2621
      window.setTimeout(arguments[1], 0);
2622
    }
2623
    return Promise.resolve();
2624
  };
2625
2626
  RTCPeerConnection.prototype.close = function() {
2627
    this.transceivers.forEach(function(transceiver) {
2628
      /* not yet
2629
      if (transceiver.iceGatherer) {
2630
        transceiver.iceGatherer.close();
2631
      }
2632
      */
2633
      if (transceiver.iceTransport) {
2634
        transceiver.iceTransport.stop();
2635
      }
2636
      if (transceiver.dtlsTransport) {
2637
        transceiver.dtlsTransport.stop();
2638
      }
2639
      if (transceiver.rtpSender) {
2640
        transceiver.rtpSender.stop();
2641
      }
2642
      if (transceiver.rtpReceiver) {
2643
        transceiver.rtpReceiver.stop();
2644
      }
2645
    });
2646
    // FIXME: clean up tracks, local streams, remote streams, etc
2647
    this._updateSignalingState('closed');
2648
  };
2649
2650
  // Update the signaling state.
2651
  RTCPeerConnection.prototype._updateSignalingState = function(newState) {
2652
    this.signalingState = newState;
2653
    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...
2654
    this.dispatchEvent(event);
2655
    if (this.onsignalingstatechange !== null) {
2656
      this.onsignalingstatechange(event);
2657
    }
2658
  };
2659
2660
  // Determine whether to fire the negotiationneeded event.
2661
  RTCPeerConnection.prototype._maybeFireNegotiationNeeded = function() {
2662
    var self = this;
2663
    if (this.signalingState !== 'stable' || this.needNegotiation === true) {
2664
      return;
2665
    }
2666
    this.needNegotiation = true;
2667
    window.setTimeout(function() {
2668
      if (self.needNegotiation === false) {
2669
        return;
2670
      }
2671
      self.needNegotiation = false;
2672
      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...
2673
      self.dispatchEvent(event);
2674
      if (self.onnegotiationneeded !== null) {
2675
        self.onnegotiationneeded(event);
2676
      }
2677
    }, 0);
2678
  };
2679
2680
  // Update the connection state.
2681
  RTCPeerConnection.prototype._updateConnectionState = function() {
2682
    var self = this;
2683
    var newState;
2684
    var states = {
2685
      'new': 0,
2686
      closed: 0,
2687
      connecting: 0,
2688
      checking: 0,
2689
      connected: 0,
2690
      completed: 0,
2691
      disconnected: 0,
2692
      failed: 0
2693
    };
2694
    this.transceivers.forEach(function(transceiver) {
2695
      states[transceiver.iceTransport.state]++;
2696
      states[transceiver.dtlsTransport.state]++;
2697
    });
2698
    // ICETransport.completed and connected are the same for this purpose.
2699
    states.connected += states.completed;
2700
2701
    newState = 'new';
2702
    if (states.failed > 0) {
2703
      newState = 'failed';
2704
    } else if (states.connecting > 0 || states.checking > 0) {
2705
      newState = 'connecting';
2706
    } else if (states.disconnected > 0) {
2707
      newState = 'disconnected';
2708
    } else if (states.new > 0) {
2709
      newState = 'new';
2710
    } else if (states.connected > 0 || states.completed > 0) {
2711
      newState = 'connected';
2712
    }
2713
2714
    if (newState !== self.iceConnectionState) {
2715
      self.iceConnectionState = newState;
2716
      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...
2717
      this.dispatchEvent(event);
2718
      if (this.oniceconnectionstatechange !== null) {
2719
        this.oniceconnectionstatechange(event);
2720
      }
2721
    }
2722
  };
2723
2724
  RTCPeerConnection.prototype.createOffer = function() {
2725
    var self = this;
2726
    if (this._pendingOffer) {
2727
      throw new Error('createOffer called while there is a pending offer.');
2728
    }
2729
    var offerOptions;
2730
    if (arguments.length === 1 && typeof arguments[0] !== 'function') {
2731
      offerOptions = arguments[0];
2732
    } else if (arguments.length === 3) {
2733
      offerOptions = arguments[2];
2734
    }
2735
2736
    var numAudioTracks = this.transceivers.filter(function(t) {
2737
      return t.kind === 'audio';
2738
    }).length;
2739
    var numVideoTracks = this.transceivers.filter(function(t) {
2740
      return t.kind === 'video';
2741
    }).length;
2742
2743
    // Determine number of audio and video tracks we need to send/recv.
2744
    if (offerOptions) {
2745
      // Reject Chrome legacy constraints.
2746
      if (offerOptions.mandatory || offerOptions.optional) {
2747
        throw new TypeError(
2748
            'Legacy mandatory/optional constraints not supported.');
2749
      }
2750
      if (offerOptions.offerToReceiveAudio !== undefined) {
2751
        if (offerOptions.offerToReceiveAudio === true) {
2752
          numAudioTracks = 1;
2753
        } else if (offerOptions.offerToReceiveAudio === false) {
2754
          numAudioTracks = 0;
2755
        } else {
2756
          numAudioTracks = offerOptions.offerToReceiveAudio;
2757
        }
2758
      }
2759
      if (offerOptions.offerToReceiveVideo !== undefined) {
2760
        if (offerOptions.offerToReceiveVideo === true) {
2761
          numVideoTracks = 1;
2762
        } else if (offerOptions.offerToReceiveVideo === false) {
2763
          numVideoTracks = 0;
2764
        } else {
2765
          numVideoTracks = offerOptions.offerToReceiveVideo;
2766
        }
2767
      }
2768
    }
2769
2770
    this.transceivers.forEach(function(transceiver) {
2771
      if (transceiver.kind === 'audio') {
2772
        numAudioTracks--;
2773
        if (numAudioTracks < 0) {
2774
          transceiver.wantReceive = false;
2775
        }
2776
      } else if (transceiver.kind === 'video') {
2777
        numVideoTracks--;
2778
        if (numVideoTracks < 0) {
2779
          transceiver.wantReceive = false;
2780
        }
2781
      }
2782
    });
2783
2784
    // Create M-lines for recvonly streams.
2785
    while (numAudioTracks > 0 || numVideoTracks > 0) {
2786
      if (numAudioTracks > 0) {
2787
        this._createTransceiver('audio');
2788
        numAudioTracks--;
2789
      }
2790
      if (numVideoTracks > 0) {
2791
        this._createTransceiver('video');
2792
        numVideoTracks--;
2793
      }
2794
    }
2795
    // reorder tracks
2796
    var transceivers = sortTracks(this.transceivers);
2797
2798
    var sdp = SDPUtils.writeSessionBoilerplate(this._sdpSessionId);
2799
    transceivers.forEach(function(transceiver, sdpMLineIndex) {
2800
      // For each track, create an ice gatherer, ice transport,
2801
      // dtls transport, potentially rtpsender and rtpreceiver.
2802
      var track = transceiver.track;
2803
      var kind = transceiver.kind;
2804
      var mid = SDPUtils.generateIdentifier();
2805
      transceiver.mid = mid;
2806
2807
      if (!transceiver.iceGatherer) {
2808
        transceiver.iceGatherer = self.usingBundle && sdpMLineIndex > 0 ?
2809
            transceivers[0].iceGatherer :
2810
            self._createIceGatherer(mid, sdpMLineIndex);
2811
      }
2812
2813
      var localCapabilities = window.RTCRtpSender.getCapabilities(kind);
2814
      // filter RTX until additional stuff needed for RTX is implemented
2815
      // in adapter.js
2816
      if (edgeVersion < 15019) {
2817
        localCapabilities.codecs = localCapabilities.codecs.filter(
2818
            function(codec) {
2819
              return codec.name !== 'rtx';
2820
            });
2821
      }
2822
      localCapabilities.codecs.forEach(function(codec) {
2823
        // work around https://bugs.chromium.org/p/webrtc/issues/detail?id=6552
2824
        // by adding level-asymmetry-allowed=1
2825
        if (codec.name === 'H264' &&
2826
            codec.parameters['level-asymmetry-allowed'] === undefined) {
2827
          codec.parameters['level-asymmetry-allowed'] = '1';
2828
        }
2829
      });
2830
2831
      // generate an ssrc now, to be used later in rtpSender.send
2832
      var sendEncodingParameters = [{
2833
        ssrc: (2 * sdpMLineIndex + 1) * 1001
2834
      }];
2835
      if (track) {
2836
        // add RTX
2837
        if (edgeVersion >= 15019 && kind === 'video') {
2838
          sendEncodingParameters[0].rtx = {
2839
            ssrc: (2 * sdpMLineIndex + 1) * 1001 + 1
2840
          };
2841
        }
2842
      }
2843
2844
      if (transceiver.wantReceive) {
2845
        transceiver.rtpReceiver = new window.RTCRtpReceiver(
2846
          transceiver.dtlsTransport,
2847
          kind
2848
        );
2849
      }
2850
2851
      transceiver.localCapabilities = localCapabilities;
2852
      transceiver.sendEncodingParameters = sendEncodingParameters;
2853
    });
2854
2855
    // always offer BUNDLE and dispose on return if not supported.
2856
    if (this._config.bundlePolicy !== 'max-compat') {
2857
      sdp += 'a=group:BUNDLE ' + transceivers.map(function(t) {
2858
        return t.mid;
2859
      }).join(' ') + '\r\n';
2860
    }
2861
    sdp += 'a=ice-options:trickle\r\n';
2862
2863
    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...
2864
      sdp += SDPUtils.writeMediaSection(transceiver,
2865
          transceiver.localCapabilities, 'offer', transceiver.stream);
2866
      sdp += 'a=rtcp-rsize\r\n';
2867
    });
2868
2869
    this._pendingOffer = transceivers;
2870
    var desc = new window.RTCSessionDescription({
2871
      type: 'offer',
2872
      sdp: sdp
2873
    });
2874
    if (arguments.length && typeof arguments[0] === 'function') {
2875
      window.setTimeout(arguments[0], 0, desc);
2876
    }
2877
    return Promise.resolve(desc);
2878
  };
2879
2880
  RTCPeerConnection.prototype.createAnswer = function() {
2881
    var sdp = SDPUtils.writeSessionBoilerplate(this._sdpSessionId);
2882
    if (this.usingBundle) {
2883
      sdp += 'a=group:BUNDLE ' + this.transceivers.map(function(t) {
2884
        return t.mid;
2885
      }).join(' ') + '\r\n';
2886
    }
2887
    this.transceivers.forEach(function(transceiver, sdpMLineIndex) {
2888
      if (transceiver.isDatachannel) {
2889
        sdp += 'm=application 0 DTLS/SCTP 5000\r\n' +
2890
            'c=IN IP4 0.0.0.0\r\n' +
2891
            'a=mid:' + transceiver.mid + '\r\n';
2892
        return;
2893
      }
2894
2895
      // FIXME: look at direction.
2896
      if (transceiver.stream) {
2897
        var localTrack;
2898
        if (transceiver.kind === 'audio') {
2899
          localTrack = transceiver.stream.getAudioTracks()[0];
2900
        } else if (transceiver.kind === 'video') {
2901
          localTrack = transceiver.stream.getVideoTracks()[0];
2902
        }
2903
        if (localTrack) {
2904
          // add RTX
2905
          if (edgeVersion >= 15019 && transceiver.kind === 'video') {
2906
            transceiver.sendEncodingParameters[0].rtx = {
2907
              ssrc: (2 * sdpMLineIndex + 2) * 1001 + 1
2908
            };
2909
          }
2910
        }
2911
      }
2912
2913
      // Calculate intersection of capabilities.
2914
      var commonCapabilities = getCommonCapabilities(
2915
          transceiver.localCapabilities,
2916
          transceiver.remoteCapabilities);
2917
2918
      var hasRtx = commonCapabilities.codecs.filter(function(c) {
2919
        return c.name.toLowerCase() === 'rtx';
2920
      }).length;
2921
      if (!hasRtx && transceiver.sendEncodingParameters[0].rtx) {
2922
        delete transceiver.sendEncodingParameters[0].rtx;
2923
      }
2924
2925
      sdp += SDPUtils.writeMediaSection(transceiver, commonCapabilities,
2926
          'answer', transceiver.stream);
2927
      if (transceiver.rtcpParameters &&
2928
          transceiver.rtcpParameters.reducedSize) {
2929
        sdp += 'a=rtcp-rsize\r\n';
2930
      }
2931
    });
2932
2933
    var desc = new window.RTCSessionDescription({
2934
      type: 'answer',
2935
      sdp: sdp
2936
    });
2937
    if (arguments.length && typeof arguments[0] === 'function') {
2938
      window.setTimeout(arguments[0], 0, desc);
2939
    }
2940
    return Promise.resolve(desc);
2941
  };
2942
2943
  RTCPeerConnection.prototype.addIceCandidate = function(candidate) {
2944
    if (!candidate) {
2945
      for (var j = 0; j < this.transceivers.length; j++) {
2946
        this.transceivers[j].iceTransport.addRemoteCandidate({});
2947
        if (this.usingBundle) {
2948
          return Promise.resolve();
2949
        }
2950
      }
2951
    } else {
2952
      var mLineIndex = candidate.sdpMLineIndex;
2953
      if (candidate.sdpMid) {
2954
        for (var i = 0; i < this.transceivers.length; i++) {
2955
          if (this.transceivers[i].mid === candidate.sdpMid) {
2956
            mLineIndex = i;
2957
            break;
2958
          }
2959
        }
2960
      }
2961
      var transceiver = this.transceivers[mLineIndex];
2962
      if (transceiver) {
2963
        var cand = Object.keys(candidate.candidate).length > 0 ?
2964
            SDPUtils.parseCandidate(candidate.candidate) : {};
2965
        // Ignore Chrome's invalid candidates since Edge does not like them.
2966
        if (cand.protocol === 'tcp' && (cand.port === 0 || cand.port === 9)) {
2967
          return Promise.resolve();
2968
        }
2969
        // Ignore RTCP candidates, we assume RTCP-MUX.
2970
        if (cand.component &&
2971
            !(cand.component === '1' || cand.component === 1)) {
2972
          return Promise.resolve();
2973
        }
2974
        transceiver.iceTransport.addRemoteCandidate(cand);
2975
2976
        // update the remoteDescription.
2977
        var sections = SDPUtils.splitSections(this.remoteDescription.sdp);
2978
        sections[mLineIndex + 1] += (cand.type ? candidate.candidate.trim()
2979
            : 'a=end-of-candidates') + '\r\n';
2980
        this.remoteDescription.sdp = sections.join('');
2981
      }
2982
    }
2983
    if (arguments.length > 1 && typeof arguments[1] === 'function') {
2984
      window.setTimeout(arguments[1], 0);
2985
    }
2986
    return Promise.resolve();
2987
  };
2988
2989
  RTCPeerConnection.prototype.getStats = function() {
2990
    var promises = [];
2991
    this.transceivers.forEach(function(transceiver) {
2992
      ['rtpSender', 'rtpReceiver', 'iceGatherer', 'iceTransport',
2993
        'dtlsTransport'].forEach(function(method) {
2994
          if (transceiver[method]) {
2995
            promises.push(transceiver[method].getStats());
2996
          }
2997
        });
2998
    });
2999
    var cb = arguments.length > 1 && typeof arguments[1] === 'function' &&
3000
        arguments[1];
3001
    var fixStatsType = function(stat) {
3002
      return {
3003
        inboundrtp: 'inbound-rtp',
3004
        outboundrtp: 'outbound-rtp',
3005
        candidatepair: 'candidate-pair',
3006
        localcandidate: 'local-candidate',
3007
        remotecandidate: 'remote-candidate'
3008
      }[stat.type] || stat.type;
3009
    };
3010
    return new Promise(function(resolve) {
3011
      // shim getStats with maplike support
3012
      var results = new Map();
3013
      Promise.all(promises).then(function(res) {
3014
        res.forEach(function(result) {
3015
          Object.keys(result).forEach(function(id) {
3016
            result[id].type = fixStatsType(result[id]);
3017
            results.set(id, result[id]);
3018
          });
3019
        });
3020
        if (cb) {
3021
          window.setTimeout(cb, 0, results);
3022
        }
3023
        resolve(results);
3024
      });
3025
    });
3026
  };
3027
  return RTCPeerConnection;
3028
};
3029
3030
},{"sdp":1}],9:[function(require,module,exports){
3031
/*
3032
 *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
3033
 *
3034
 *  Use of this source code is governed by a BSD-style license
3035
 *  that can be found in the LICENSE file in the root of the source
3036
 *  tree.
3037
 */
3038
 /* eslint-env node */
3039
'use strict';
3040
3041
var utils = require('../utils');
3042
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
3043
var firefoxShim = {
3044
  shimOnTrack: function(window) {
3045
    if (typeof window === 'object' && window.RTCPeerConnection && !('ontrack' in
3046
        window.RTCPeerConnection.prototype)) {
3047
      Object.defineProperty(window.RTCPeerConnection.prototype, 'ontrack', {
3048
        get: function() {
3049
          return this._ontrack;
3050
        },
3051
        set: function(f) {
3052
          if (this._ontrack) {
3053
            this.removeEventListener('track', this._ontrack);
3054
            this.removeEventListener('addstream', this._ontrackpoly);
3055
          }
3056
          this.addEventListener('track', this._ontrack = f);
3057
          this.addEventListener('addstream', this._ontrackpoly = function(e) {
3058
            e.stream.getTracks().forEach(function(track) {
3059
              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...
3060
              event.track = track;
3061
              event.receiver = {track: track};
3062
              event.streams = [e.stream];
3063
              this.dispatchEvent(event);
3064
            }.bind(this));
3065
          }.bind(this));
3066
        }
3067
      });
3068
    }
3069
  },
3070
3071
  shimSourceObject: function(window) {
3072
    // Firefox has supported mozSrcObject since FF22, unprefixed in 42.
3073
    if (typeof window === 'object') {
3074
      if (window.HTMLMediaElement &&
3075
        !('srcObject' in window.HTMLMediaElement.prototype)) {
3076
        // Shim the srcObject property, once, when HTMLMediaElement is found.
3077
        Object.defineProperty(window.HTMLMediaElement.prototype, 'srcObject', {
3078
          get: function() {
3079
            return this.mozSrcObject;
3080
          },
3081
          set: function(stream) {
3082
            this.mozSrcObject = stream;
3083
          }
3084
        });
3085
      }
3086
    }
3087
  },
3088
3089
  shimPeerConnection: function(window) {
3090
    var browserDetails = utils.detectBrowser(window);
3091
3092
    if (typeof window !== 'object' || !(window.RTCPeerConnection ||
3093
        window.mozRTCPeerConnection)) {
3094
      return; // probably media.peerconnection.enabled=false in about:config
3095
    }
3096
    // The RTCPeerConnection object.
3097
    if (!window.RTCPeerConnection) {
3098
      window.RTCPeerConnection = function(pcConfig, pcConstraints) {
3099
        if (browserDetails.version < 38) {
3100
          // .urls is not supported in FF < 38.
3101
          // create RTCIceServers with a single url.
3102
          if (pcConfig && pcConfig.iceServers) {
3103
            var newIceServers = [];
3104
            for (var i = 0; i < pcConfig.iceServers.length; i++) {
3105
              var server = pcConfig.iceServers[i];
3106
              if (server.hasOwnProperty('urls')) {
3107
                for (var j = 0; j < server.urls.length; j++) {
3108
                  var newServer = {
3109
                    url: server.urls[j]
3110
                  };
3111
                  if (server.urls[j].indexOf('turn') === 0) {
3112
                    newServer.username = server.username;
3113
                    newServer.credential = server.credential;
3114
                  }
3115
                  newIceServers.push(newServer);
3116
                }
3117
              } else {
3118
                newIceServers.push(pcConfig.iceServers[i]);
3119
              }
3120
            }
3121
            pcConfig.iceServers = newIceServers;
3122
          }
3123
        }
3124
        return new window.mozRTCPeerConnection(pcConfig, pcConstraints);
3125
      };
3126
      window.RTCPeerConnection.prototype =
3127
          window.mozRTCPeerConnection.prototype;
3128
3129
      // wrap static methods. Currently just generateCertificate.
3130
      if (window.mozRTCPeerConnection.generateCertificate) {
3131
        Object.defineProperty(window.RTCPeerConnection, 'generateCertificate', {
3132
          get: function() {
3133
            return window.mozRTCPeerConnection.generateCertificate;
3134
          }
3135
        });
3136
      }
3137
3138
      window.RTCSessionDescription = window.mozRTCSessionDescription;
3139
      window.RTCIceCandidate = window.mozRTCIceCandidate;
3140
    }
3141
3142
    // shim away need for obsolete RTCIceCandidate/RTCSessionDescription.
3143
    ['setLocalDescription', 'setRemoteDescription', 'addIceCandidate']
3144
        .forEach(function(method) {
3145
          var nativeMethod = window.RTCPeerConnection.prototype[method];
3146
          window.RTCPeerConnection.prototype[method] = function() {
3147
            arguments[0] = new ((method === 'addIceCandidate') ?
3148
                window.RTCIceCandidate :
3149
                window.RTCSessionDescription)(arguments[0]);
3150
            return nativeMethod.apply(this, arguments);
3151
          };
3152
        });
3153
3154
    // support for addIceCandidate(null or undefined)
3155
    var nativeAddIceCandidate =
3156
        window.RTCPeerConnection.prototype.addIceCandidate;
3157
    window.RTCPeerConnection.prototype.addIceCandidate = function() {
3158
      if (!arguments[0]) {
3159
        if (arguments[1]) {
3160
          arguments[1].apply(null);
3161
        }
3162
        return Promise.resolve();
3163
      }
3164
      return nativeAddIceCandidate.apply(this, arguments);
3165
    };
3166
3167
    // shim getStats with maplike support
3168
    var makeMapStats = function(stats) {
3169
      var map = new Map();
3170
      Object.keys(stats).forEach(function(key) {
3171
        map.set(key, stats[key]);
3172
        map[key] = stats[key];
3173
      });
3174
      return map;
3175
    };
3176
3177
    var modernStatsTypes = {
3178
      inboundrtp: 'inbound-rtp',
3179
      outboundrtp: 'outbound-rtp',
3180
      candidatepair: 'candidate-pair',
3181
      localcandidate: 'local-candidate',
3182
      remotecandidate: 'remote-candidate'
3183
    };
3184
3185
    var nativeGetStats = window.RTCPeerConnection.prototype.getStats;
3186
    window.RTCPeerConnection.prototype.getStats = function(
3187
      selector,
3188
      onSucc,
3189
      onErr
3190
    ) {
3191
      return nativeGetStats.apply(this, [selector || null])
3192
        .then(function(stats) {
3193
          if (browserDetails.version < 48) {
3194
            stats = makeMapStats(stats);
3195
          }
3196
          if (browserDetails.version < 53 && !onSucc) {
3197
            // Shim only promise getStats with spec-hyphens in type names
3198
            // Leave callback version alone; misc old uses of forEach before Map
3199
            try {
3200
              stats.forEach(function(stat) {
3201
                stat.type = modernStatsTypes[stat.type] || stat.type;
3202
              });
3203
            } catch (e) {
3204
              if (e.name !== 'TypeError') {
3205
                throw e;
3206
              }
3207
              // Avoid TypeError: "type" is read-only, in old versions. 34-43ish
3208
              stats.forEach(function(stat, i) {
3209
                stats.set(i, Object.assign({}, stat, {
3210
                  type: modernStatsTypes[stat.type] || stat.type
3211
                }));
3212
              });
3213
            }
3214
          }
3215
          return stats;
3216
        })
3217
        .then(onSucc, onErr);
3218
    };
3219
  }
3220
};
3221
3222
// Expose public methods.
3223
module.exports = {
3224
  shimOnTrack: firefoxShim.shimOnTrack,
3225
  shimSourceObject: firefoxShim.shimSourceObject,
3226
  shimPeerConnection: firefoxShim.shimPeerConnection,
3227
  shimGetUserMedia: require('./getusermedia')
3228
};
3229
3230
},{"../utils":12,"./getusermedia":10}],10:[function(require,module,exports){
3231
/*
3232
 *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
3233
 *
3234
 *  Use of this source code is governed by a BSD-style license
3235
 *  that can be found in the LICENSE file in the root of the source
3236
 *  tree.
3237
 */
3238
 /* eslint-env node */
3239
'use strict';
3240
3241
var utils = require('../utils');
3242
var logging = utils.log;
3243
3244
// Expose public methods.
3245
module.exports = function(window) {
3246
  var browserDetails = utils.detectBrowser(window);
3247
  var navigator = window && window.navigator;
3248
  var MediaStreamTrack = window && window.MediaStreamTrack;
3249
3250
  var shimError_ = function(e) {
3251
    return {
3252
      name: {
3253
        InternalError: 'NotReadableError',
3254
        NotSupportedError: 'TypeError',
3255
        PermissionDeniedError: 'NotAllowedError',
3256
        SecurityError: 'NotAllowedError'
3257
      }[e.name] || e.name,
3258
      message: {
3259
        'The operation is insecure.': 'The request is not allowed by the ' +
3260
        'user agent or the platform in the current context.'
3261
      }[e.message] || e.message,
3262
      constraint: e.constraint,
3263
      toString: function() {
3264
        return this.name + (this.message && ': ') + this.message;
3265
      }
3266
    };
3267
  };
3268
3269
  // getUserMedia constraints shim.
3270
  var getUserMedia_ = function(constraints, onSuccess, onError) {
3271
    var constraintsToFF37_ = function(c) {
3272
      if (typeof c !== 'object' || c.require) {
3273
        return c;
3274
      }
3275
      var require = [];
3276
      Object.keys(c).forEach(function(key) {
3277
        if (key === 'require' || key === 'advanced' || key === 'mediaSource') {
3278
          return;
3279
        }
3280
        var r = c[key] = (typeof c[key] === 'object') ?
3281
            c[key] : {ideal: c[key]};
3282
        if (r.min !== undefined ||
3283
            r.max !== undefined || r.exact !== undefined) {
3284
          require.push(key);
3285
        }
3286
        if (r.exact !== undefined) {
3287
          if (typeof r.exact === 'number') {
3288
            r. min = r.max = r.exact;
3289
          } else {
3290
            c[key] = r.exact;
3291
          }
3292
          delete r.exact;
3293
        }
3294
        if (r.ideal !== undefined) {
3295
          c.advanced = c.advanced || [];
3296
          var oc = {};
3297
          if (typeof r.ideal === 'number') {
3298
            oc[key] = {min: r.ideal, max: r.ideal};
3299
          } else {
3300
            oc[key] = r.ideal;
3301
          }
3302
          c.advanced.push(oc);
3303
          delete r.ideal;
3304
          if (!Object.keys(r).length) {
3305
            delete c[key];
3306
          }
3307
        }
3308
      });
3309
      if (require.length) {
3310
        c.require = require;
3311
      }
3312
      return c;
3313
    };
3314
    constraints = JSON.parse(JSON.stringify(constraints));
3315
    if (browserDetails.version < 38) {
3316
      logging('spec: ' + JSON.stringify(constraints));
3317
      if (constraints.audio) {
3318
        constraints.audio = constraintsToFF37_(constraints.audio);
3319
      }
3320
      if (constraints.video) {
3321
        constraints.video = constraintsToFF37_(constraints.video);
3322
      }
3323
      logging('ff37: ' + JSON.stringify(constraints));
3324
    }
3325
    return navigator.mozGetUserMedia(constraints, onSuccess, function(e) {
3326
      onError(shimError_(e));
3327
    });
3328
  };
3329
3330
  // Returns the result of getUserMedia as a Promise.
3331
  var getUserMediaPromise_ = function(constraints) {
3332
    return new Promise(function(resolve, reject) {
3333
      getUserMedia_(constraints, resolve, reject);
3334
    });
3335
  };
3336
3337
  // Shim for mediaDevices on older versions.
3338
  if (!navigator.mediaDevices) {
3339
    navigator.mediaDevices = {getUserMedia: getUserMediaPromise_,
3340
      addEventListener: function() { },
3341
      removeEventListener: function() { }
3342
    };
3343
  }
3344
  navigator.mediaDevices.enumerateDevices =
3345
      navigator.mediaDevices.enumerateDevices || function() {
3346
        return new Promise(function(resolve) {
3347
          var infos = [
3348
            {kind: 'audioinput', deviceId: 'default', label: '', groupId: ''},
3349
            {kind: 'videoinput', deviceId: 'default', label: '', groupId: ''}
3350
          ];
3351
          resolve(infos);
3352
        });
3353
      };
3354
3355
  if (browserDetails.version < 41) {
3356
    // Work around http://bugzil.la/1169665
3357
    var orgEnumerateDevices =
3358
        navigator.mediaDevices.enumerateDevices.bind(navigator.mediaDevices);
3359
    navigator.mediaDevices.enumerateDevices = function() {
3360
      return orgEnumerateDevices().then(undefined, function(e) {
3361
        if (e.name === 'NotFoundError') {
3362
          return [];
3363
        }
3364
        throw e;
3365
      });
3366
    };
3367
  }
3368
  if (browserDetails.version < 49) {
3369
    var origGetUserMedia = navigator.mediaDevices.getUserMedia.
3370
        bind(navigator.mediaDevices);
3371
    navigator.mediaDevices.getUserMedia = function(c) {
3372
      return origGetUserMedia(c).then(function(stream) {
3373
        // Work around https://bugzil.la/802326
3374
        if (c.audio && !stream.getAudioTracks().length ||
3375
            c.video && !stream.getVideoTracks().length) {
3376
          stream.getTracks().forEach(function(track) {
3377
            track.stop();
3378
          });
3379
          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...
3380
                                 'NotFoundError');
3381
        }
3382
        return stream;
3383
      }, function(e) {
3384
        return Promise.reject(shimError_(e));
3385
      });
3386
    };
3387
  }
3388
  if (!(browserDetails.version > 55 &&
3389
      'autoGainControl' in navigator.mediaDevices.getSupportedConstraints())) {
3390
    var remap = function(obj, a, b) {
3391
      if (a in obj && !(b in obj)) {
3392
        obj[b] = obj[a];
3393
        delete obj[a];
3394
      }
3395
    };
3396
3397
    var nativeGetUserMedia = navigator.mediaDevices.getUserMedia.
3398
        bind(navigator.mediaDevices);
3399
    navigator.mediaDevices.getUserMedia = function(c) {
3400
      if (typeof c === 'object' && typeof c.audio === 'object') {
3401
        c = JSON.parse(JSON.stringify(c));
3402
        remap(c.audio, 'autoGainControl', 'mozAutoGainControl');
3403
        remap(c.audio, 'noiseSuppression', 'mozNoiseSuppression');
3404
      }
3405
      return nativeGetUserMedia(c);
3406
    };
3407
3408
    if (MediaStreamTrack && MediaStreamTrack.prototype.getSettings) {
3409
      var nativeGetSettings = MediaStreamTrack.prototype.getSettings;
3410
      MediaStreamTrack.prototype.getSettings = function() {
3411
        var obj = nativeGetSettings.apply(this, arguments);
3412
        remap(obj, 'mozAutoGainControl', 'autoGainControl');
3413
        remap(obj, 'mozNoiseSuppression', 'noiseSuppression');
3414
        return obj;
3415
      };
3416
    }
3417
3418
    if (MediaStreamTrack && MediaStreamTrack.prototype.applyConstraints) {
3419
      var nativeApplyConstraints = MediaStreamTrack.prototype.applyConstraints;
3420
      MediaStreamTrack.prototype.applyConstraints = function(c) {
3421
        if (this.kind === 'audio' && typeof c === 'object') {
3422
          c = JSON.parse(JSON.stringify(c));
3423
          remap(c, 'autoGainControl', 'mozAutoGainControl');
3424
          remap(c, 'noiseSuppression', 'mozNoiseSuppression');
3425
        }
3426
        return nativeApplyConstraints.apply(this, [c]);
3427
      };
3428
    }
3429
  }
3430
  navigator.getUserMedia = function(constraints, onSuccess, onError) {
3431
    if (browserDetails.version < 44) {
3432
      return getUserMedia_(constraints, onSuccess, onError);
3433
    }
3434
    // Replace Firefox 44+'s deprecation warning with unprefixed version.
3435
    console.warn('navigator.getUserMedia has been replaced by ' +
3436
                 'navigator.mediaDevices.getUserMedia');
3437
    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...
3438
  };
3439
};
3440
3441
},{"../utils":12}],11:[function(require,module,exports){
3442
/*
3443
 *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
3444
 *
3445
 *  Use of this source code is governed by a BSD-style license
3446
 *  that can be found in the LICENSE file in the root of the source
3447
 *  tree.
3448
 */
3449
'use strict';
3450
var utils = require('../utils');
3451
3452
var safariShim = {
3453
  // TODO: DrAlex, should be here, double check against LayoutTests
3454
3455
  // TODO: once the back-end for the mac port is done, add.
3456
  // TODO: check for webkitGTK+
3457
  // shimPeerConnection: function() { },
3458
3459
  shimLocalStreamsAPI: function(window) {
3460
    if (typeof window !== 'object' || !window.RTCPeerConnection) {
3461
      return;
3462
    }
3463
    if (!('getLocalStreams' in window.RTCPeerConnection.prototype)) {
3464
      window.RTCPeerConnection.prototype.getLocalStreams = function() {
3465
        if (!this._localStreams) {
3466
          this._localStreams = [];
3467
        }
3468
        return this._localStreams;
3469
      };
3470
    }
3471
    if (!('getStreamById' in window.RTCPeerConnection.prototype)) {
3472
      window.RTCPeerConnection.prototype.getStreamById = function(id) {
3473
        var result = null;
3474
        if (this._localStreams) {
3475
          this._localStreams.forEach(function(stream) {
3476
            if (stream.id === id) {
3477
              result = stream;
3478
            }
3479
          });
3480
        }
3481
        if (this._remoteStreams) {
3482
          this._remoteStreams.forEach(function(stream) {
3483
            if (stream.id === id) {
3484
              result = stream;
3485
            }
3486
          });
3487
        }
3488
        return result;
3489
      };
3490
    }
3491
    if (!('addStream' in window.RTCPeerConnection.prototype)) {
3492
      var _addTrack = window.RTCPeerConnection.prototype.addTrack;
3493
      window.RTCPeerConnection.prototype.addStream = function(stream) {
3494
        if (!this._localStreams) {
3495
          this._localStreams = [];
3496
        }
3497
        if (this._localStreams.indexOf(stream) === -1) {
3498
          this._localStreams.push(stream);
3499
        }
3500
        var self = this;
3501
        stream.getTracks().forEach(function(track) {
3502
          _addTrack.call(self, track, stream);
3503
        });
3504
      };
3505
3506
      window.RTCPeerConnection.prototype.addTrack = function(track, stream) {
3507
        if (stream) {
3508
          if (!this._localStreams) {
3509
            this._localStreams = [stream];
3510
          } else if (this._localStreams.indexOf(stream) === -1) {
3511
            this._localStreams.push(stream);
3512
          }
3513
        }
3514
        _addTrack.call(this, track, stream);
3515
      };
3516
    }
3517
    if (!('removeStream' in window.RTCPeerConnection.prototype)) {
3518
      window.RTCPeerConnection.prototype.removeStream = function(stream) {
3519
        if (!this._localStreams) {
3520
          this._localStreams = [];
3521
        }
3522
        var index = this._localStreams.indexOf(stream);
3523
        if (index === -1) {
3524
          return;
3525
        }
3526
        this._localStreams.splice(index, 1);
3527
        var self = this;
3528
        var tracks = stream.getTracks();
3529
        this.getSenders().forEach(function(sender) {
3530
          if (tracks.indexOf(sender.track) !== -1) {
3531
            self.removeTrack(sender);
3532
          }
3533
        });
3534
      };
3535
    }
3536
  },
3537
  shimRemoteStreamsAPI: function(window) {
3538
    if (typeof window !== 'object' || !window.RTCPeerConnection) {
3539
      return;
3540
    }
3541
    if (!('getRemoteStreams' in window.RTCPeerConnection.prototype)) {
3542
      window.RTCPeerConnection.prototype.getRemoteStreams = function() {
3543
        return this._remoteStreams ? this._remoteStreams : [];
3544
      };
3545
    }
3546
    if (!('onaddstream' in window.RTCPeerConnection.prototype)) {
3547
      Object.defineProperty(window.RTCPeerConnection.prototype, 'onaddstream', {
3548
        get: function() {
3549
          return this._onaddstream;
3550
        },
3551
        set: function(f) {
3552
          if (this._onaddstream) {
3553
            this.removeEventListener('addstream', this._onaddstream);
3554
            this.removeEventListener('track', this._onaddstreampoly);
3555
          }
3556
          this.addEventListener('addstream', this._onaddstream = f);
3557
          this.addEventListener('track', this._onaddstreampoly = function(e) {
3558
            var stream = e.streams[0];
3559
            if (!this._remoteStreams) {
3560
              this._remoteStreams = [];
3561
            }
3562
            if (this._remoteStreams.indexOf(stream) >= 0) {
3563
              return;
3564
            }
3565
            this._remoteStreams.push(stream);
3566
            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...
3567
            event.stream = e.streams[0];
3568
            this.dispatchEvent(event);
3569
          }.bind(this));
3570
        }
3571
      });
3572
    }
3573
  },
3574
  shimCallbacksAPI: function(window) {
3575
    if (typeof window !== 'object' || !window.RTCPeerConnection) {
3576
      return;
3577
    }
3578
    var prototype = window.RTCPeerConnection.prototype;
3579
    var createOffer = prototype.createOffer;
3580
    var createAnswer = prototype.createAnswer;
3581
    var setLocalDescription = prototype.setLocalDescription;
3582
    var setRemoteDescription = prototype.setRemoteDescription;
3583
    var addIceCandidate = prototype.addIceCandidate;
3584
3585
    prototype.createOffer = function(successCallback, failureCallback) {
3586
      var options = (arguments.length >= 2) ? arguments[2] : arguments[0];
3587
      var promise = createOffer.apply(this, [options]);
3588
      if (!failureCallback) {
3589
        return promise;
3590
      }
3591
      promise.then(successCallback, failureCallback);
3592
      return Promise.resolve();
3593
    };
3594
3595
    prototype.createAnswer = function(successCallback, failureCallback) {
3596
      var options = (arguments.length >= 2) ? arguments[2] : arguments[0];
3597
      var promise = createAnswer.apply(this, [options]);
3598
      if (!failureCallback) {
3599
        return promise;
3600
      }
3601
      promise.then(successCallback, failureCallback);
3602
      return Promise.resolve();
3603
    };
3604
3605
    var withCallback = function(description, successCallback, failureCallback) {
3606
      var promise = setLocalDescription.apply(this, [description]);
3607
      if (!failureCallback) {
3608
        return promise;
3609
      }
3610
      promise.then(successCallback, failureCallback);
3611
      return Promise.resolve();
3612
    };
3613
    prototype.setLocalDescription = withCallback;
3614
3615
    withCallback = function(description, successCallback, failureCallback) {
3616
      var promise = setRemoteDescription.apply(this, [description]);
3617
      if (!failureCallback) {
3618
        return promise;
3619
      }
3620
      promise.then(successCallback, failureCallback);
3621
      return Promise.resolve();
3622
    };
3623
    prototype.setRemoteDescription = withCallback;
3624
3625
    withCallback = function(candidate, successCallback, failureCallback) {
3626
      var promise = addIceCandidate.apply(this, [candidate]);
3627
      if (!failureCallback) {
3628
        return promise;
3629
      }
3630
      promise.then(successCallback, failureCallback);
3631
      return Promise.resolve();
3632
    };
3633
    prototype.addIceCandidate = withCallback;
3634
  },
3635
  shimGetUserMedia: function(window) {
3636
    var navigator = window && window.navigator;
3637
3638
    if (!navigator.getUserMedia) {
3639
      if (navigator.webkitGetUserMedia) {
3640
        navigator.getUserMedia = navigator.webkitGetUserMedia.bind(navigator);
3641
      } else if (navigator.mediaDevices &&
3642
          navigator.mediaDevices.getUserMedia) {
3643
        navigator.getUserMedia = function(constraints, cb, errcb) {
3644
          navigator.mediaDevices.getUserMedia(constraints)
3645
          .then(cb, errcb);
3646
        }.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...
3647
      }
3648
    }
3649
  },
3650
  shimRTCIceServerUrls: function(window) {
3651
    // migrate from non-spec RTCIceServer.url to RTCIceServer.urls
3652
    var OrigPeerConnection = window.RTCPeerConnection;
3653
    window.RTCPeerConnection = function(pcConfig, pcConstraints) {
3654
      if (pcConfig && pcConfig.iceServers) {
3655
        var newIceServers = [];
3656
        for (var i = 0; i < pcConfig.iceServers.length; i++) {
3657
          var server = pcConfig.iceServers[i];
3658
          if (!server.hasOwnProperty('urls') &&
3659
              server.hasOwnProperty('url')) {
3660
            utils.deprecated('RTCIceServer.url', 'RTCIceServer.urls');
3661
            server = JSON.parse(JSON.stringify(server));
3662
            server.urls = server.url;
3663
            delete server.url;
3664
            newIceServers.push(server);
3665
          } else {
3666
            newIceServers.push(pcConfig.iceServers[i]);
3667
          }
3668
        }
3669
        pcConfig.iceServers = newIceServers;
3670
      }
3671
      return new OrigPeerConnection(pcConfig, pcConstraints);
3672
    };
3673
    window.RTCPeerConnection.prototype = OrigPeerConnection.prototype;
3674
    // wrap static methods. Currently just generateCertificate.
3675
    Object.defineProperty(window.RTCPeerConnection, 'generateCertificate', {
3676
      get: function() {
3677
        return OrigPeerConnection.generateCertificate;
3678
      }
3679
    });
3680
  }
3681
};
3682
3683
// Expose public methods.
3684
module.exports = {
3685
  shimCallbacksAPI: safariShim.shimCallbacksAPI,
3686
  shimLocalStreamsAPI: safariShim.shimLocalStreamsAPI,
3687
  shimRemoteStreamsAPI: safariShim.shimRemoteStreamsAPI,
3688
  shimGetUserMedia: safariShim.shimGetUserMedia,
3689
  shimRTCIceServerUrls: safariShim.shimRTCIceServerUrls
3690
  // TODO
3691
  // shimPeerConnection: safariShim.shimPeerConnection
3692
};
3693
3694
},{"../utils":12}],12:[function(require,module,exports){
3695
/*
3696
 *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
3697
 *
3698
 *  Use of this source code is governed by a BSD-style license
3699
 *  that can be found in the LICENSE file in the root of the source
3700
 *  tree.
3701
 */
3702
 /* eslint-env node */
3703
'use strict';
3704
3705
var logDisabled_ = true;
3706
var deprecationWarnings_ = true;
3707
3708
// Utility methods.
3709
var utils = {
3710
  disableLog: function(bool) {
3711
    if (typeof bool !== 'boolean') {
3712
      return new Error('Argument type: ' + typeof bool +
3713
          '. Please use a boolean.');
3714
    }
3715
    logDisabled_ = bool;
3716
    return (bool) ? 'adapter.js logging disabled' :
3717
        'adapter.js logging enabled';
3718
  },
3719
3720
  /**
3721
   * Disable or enable deprecation warnings
3722
   * @param {!boolean} bool set to true to disable warnings.
3723
   */
3724
  disableWarnings: function(bool) {
3725
    if (typeof bool !== 'boolean') {
3726
      return new Error('Argument type: ' + typeof bool +
3727
          '. Please use a boolean.');
3728
    }
3729
    deprecationWarnings_ = !bool;
3730
    return 'adapter.js deprecation warnings ' + (bool ? 'disabled' : 'enabled');
3731
  },
3732
3733
  log: function() {
3734
    if (typeof window === 'object') {
3735
      if (logDisabled_) {
3736
        return;
3737
      }
3738
      if (typeof console !== 'undefined' && typeof console.log === 'function') {
3739
        console.log.apply(console, arguments);
3740
      }
3741
    }
3742
  },
3743
3744
  /**
3745
   * Shows a deprecation warning suggesting the modern and spec-compatible API.
3746
   */
3747
  deprecated: function(oldMethod, newMethod) {
3748
    if (!deprecationWarnings_) {
3749
      return;
3750
    }
3751
    console.warn(oldMethod + ' is deprecated, please use ' + newMethod +
3752
        ' instead.');
3753
  },
3754
3755
  /**
3756
   * Extract browser version out of the provided user agent string.
3757
   *
3758
   * @param {!string} uastring userAgent string.
3759
   * @param {!string} expr Regular expression used as match criteria.
3760
   * @param {!number} pos position in the version string to be returned.
3761
   * @return {!number} browser version.
3762
   */
3763
  extractVersion: function(uastring, expr, pos) {
3764
    var match = uastring.match(expr);
3765
    return match && match.length >= pos && parseInt(match[pos], 10);
3766
  },
3767
3768
  /**
3769
   * Browser detector.
3770
   *
3771
   * @return {object} result containing browser and version
3772
   *     properties.
3773
   */
3774
  detectBrowser: function(window) {
3775
    var navigator = window && window.navigator;
3776
3777
    // Returned result object.
3778
    var result = {};
3779
    result.browser = null;
3780
    result.version = null;
3781
3782
    // Fail early if it's not a browser
3783
    if (typeof window === 'undefined' || !window.navigator) {
3784
      result.browser = 'Not a browser.';
3785
      return result;
3786
    }
3787
3788
    // Firefox.
3789
    if (navigator.mozGetUserMedia) {
3790
      result.browser = 'firefox';
3791
      result.version = this.extractVersion(navigator.userAgent,
3792
          /Firefox\/(\d+)\./, 1);
3793
    } else if (navigator.webkitGetUserMedia) {
3794
      // Chrome, Chromium, Webview, Opera, all use the chrome shim for now
3795
      if (window.webkitRTCPeerConnection) {
3796
        result.browser = 'chrome';
3797
        result.version = this.extractVersion(navigator.userAgent,
3798
          /Chrom(e|ium)\/(\d+)\./, 2);
3799
      } else { // Safari (in an unpublished version) or unknown webkit-based.
3800
        if (navigator.userAgent.match(/Version\/(\d+).(\d+)/)) {
3801
          result.browser = 'safari';
3802
          result.version = this.extractVersion(navigator.userAgent,
3803
            /AppleWebKit\/(\d+)\./, 1);
3804
        } else { // unknown webkit-based browser.
3805
          result.browser = 'Unsupported webkit-based browser ' +
3806
              'with GUM support but no WebRTC support.';
3807
          return result;
3808
        }
3809
      }
3810
    } else if (navigator.mediaDevices &&
3811
        navigator.userAgent.match(/Edge\/(\d+).(\d+)$/)) { // Edge.
3812
      result.browser = 'edge';
3813
      result.version = this.extractVersion(navigator.userAgent,
3814
          /Edge\/(\d+).(\d+)$/, 2);
3815
    } else if (navigator.mediaDevices &&
3816
        navigator.userAgent.match(/AppleWebKit\/(\d+)\./)) {
3817
        // Safari, with webkitGetUserMedia removed.
3818
      result.browser = 'safari';
3819
      result.version = this.extractVersion(navigator.userAgent,
3820
          /AppleWebKit\/(\d+)\./, 1);
3821
    } else { // Default fallthrough: not supported.
3822
      result.browser = 'Not a supported browser.';
3823
      return result;
3824
    }
3825
3826
    return result;
3827
  },
3828
3829
  // shimCreateObjectURL must be called before shimSourceObject to avoid loop.
3830
3831
  shimCreateObjectURL: function(window) {
3832
    var URL = window && window.URL;
3833
3834
    if (!(typeof window === 'object' && window.HTMLMediaElement &&
3835
          'srcObject' in window.HTMLMediaElement.prototype)) {
3836
      // Only shim CreateObjectURL using srcObject if srcObject exists.
3837
      return undefined;
3838
    }
3839
3840
    var nativeCreateObjectURL = URL.createObjectURL.bind(URL);
3841
    var nativeRevokeObjectURL = URL.revokeObjectURL.bind(URL);
3842
    var streams = new Map(), newId = 0;
3843
3844
    URL.createObjectURL = function(stream) {
3845
      if ('getTracks' in stream) {
3846
        var url = 'polyblob:' + (++newId);
3847
        streams.set(url, stream);
3848
        utils.deprecated('URL.createObjectURL(stream)',
3849
            'elem.srcObject = stream');
3850
        return url;
3851
      }
3852
      return nativeCreateObjectURL(stream);
3853
    };
3854
    URL.revokeObjectURL = function(url) {
3855
      nativeRevokeObjectURL(url);
3856
      streams.delete(url);
3857
    };
3858
3859
    var dsc = Object.getOwnPropertyDescriptor(window.HTMLMediaElement.prototype,
3860
                                              'src');
3861
    Object.defineProperty(window.HTMLMediaElement.prototype, 'src', {
3862
      get: function() {
3863
        return dsc.get.apply(this);
3864
      },
3865
      set: function(url) {
3866
        this.srcObject = streams.get(url) || null;
3867
        return dsc.set.apply(this, [url]);
3868
      }
3869
    });
3870
3871
    var nativeSetAttribute = window.HTMLMediaElement.prototype.setAttribute;
3872
    window.HTMLMediaElement.prototype.setAttribute = function() {
3873
      if (arguments.length === 2 &&
3874
          ('' + arguments[0]).toLowerCase() === 'src') {
3875
        this.srcObject = streams.get(arguments[1]) || null;
3876
      }
3877
      return nativeSetAttribute.apply(this, arguments);
3878
    };
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...
3879
  }
3880
};
3881
3882
// Export.
3883
module.exports = {
3884
  log: utils.log,
3885
  deprecated: utils.deprecated,
3886
  disableLog: utils.disableLog,
3887
  disableWarnings: utils.disableWarnings,
3888
  extractVersion: utils.extractVersion,
3889
  shimCreateObjectURL: utils.shimCreateObjectURL,
3890
  detectBrowser: utils.detectBrowser.bind(utils)
3891
};
3892
3893
},{}]},{},[2])(2)
3894
});
3895