1 (function(f
){if(typeof exports
==="object"&&typeof module
!=="undefined"){module
.exports
=f()}else if(typeof define
==="function"&&define
.amd
){define([],f
)}else{var g
;if(typeof window
!=="undefined"){g
=window
}else if(typeof global
!=="undefined"){g
=global
}else if(typeof self
!=="undefined"){g
=self
}else{g
=this}g
.adapter
= f()}})(function(){var define
,module
,exports
;return (function e(t
,n
,r
){function s(o
,u
){if(!n
[o
]){if(!t
[o
]){var a
=typeof require
=="function"&&require
;if(!u
&&a
)return a(o
,!0);if(i
)return i(o
,!0);var f
=new Error("Cannot find module '"+o
+"'");throw f
.code
="MODULE_NOT_FOUND",f
}var l
=n
[o
]={exports
:{}};t
[o
][0].call(l
.exports
,function(e
){var n
=t
[o
][1][e
];return s(n
?n
:e
)},l
,l
.exports
,e
,t
,n
,r
)}return n
[o
].exports
}var i
=typeof require
=="function"&&require
;for(var o
=0;o
<r
.length
;o
++)s(r
[o
]);return s
})({1:[function(require
,module
,exports
){
8 // Generate an alphanumeric identifier for cname or mids.
9 // TODO: use UUIDs instead? https://gist.github.com/jed/982883
10 SDPUtils
.generateIdentifier = function() {
11 return Math
.random().toString(36).substr(2, 10);
14 // The RTCP CNAME used by all peerconnections from the same JS.
15 SDPUtils
.localCName
= SDPUtils
.generateIdentifier();
17 // Splits SDP into lines, dealing with both CRLF and LF.
18 SDPUtils
.splitLines = function(blob
) {
19 return blob
.trim().split('\n').map(function(line
) {
23 // Splits SDP into sessionpart and mediasections. Ensures CRLF.
24 SDPUtils
.splitSections = function(blob
) {
25 var parts
= blob
.split('\nm=');
26 return parts
.map(function(part
, index
) {
27 return (index
> 0 ? 'm=' + part
: part
).trim() + '\r\n';
31 // Returns lines that start with a certain prefix.
32 SDPUtils
.matchPrefix = function(blob
, prefix
) {
33 return SDPUtils
.splitLines(blob
).filter(function(line
) {
34 return line
.indexOf(prefix
) === 0;
38 // Parses an ICE candidate line. Sample input:
39 // candidate:702786350 2 udp 41819902 8.8.8.8 60769 typ relay raddr 8.8.8.8
41 SDPUtils
.parseCandidate = function(line
) {
43 // Parse both variants.
44 if (line
.indexOf('a=candidate:') === 0) {
45 parts
= line
.substring(12).split(' ');
47 parts
= line
.substring(10).split(' ');
52 component
: parseInt(parts
[1], 10),
53 protocol
: parts
[2].toLowerCase(),
54 priority
: parseInt(parts
[3], 10),
56 port
: parseInt(parts
[5], 10),
57 // skip parts[6] == 'typ'
61 for (var i
= 8; i
< parts
.length
; i
+= 2) {
64 candidate
.relatedAddress
= parts
[i
+ 1];
67 candidate
.relatedPort
= parseInt(parts
[i
+ 1], 10);
70 candidate
.tcpType
= parts
[i
+ 1];
72 default: // extension handling, in particular ufrag
73 candidate
[parts
[i
]] = parts
[i
+ 1];
80 // Translates a candidate object into SDP candidate attribute.
81 SDPUtils
.writeCandidate = function(candidate
) {
83 sdp
.push(candidate
.foundation
);
84 sdp
.push(candidate
.component
);
85 sdp
.push(candidate
.protocol
.toUpperCase());
86 sdp
.push(candidate
.priority
);
87 sdp
.push(candidate
.ip
);
88 sdp
.push(candidate
.port
);
90 var type
= candidate
.type
;
93 if (type
!== 'host' && candidate
.relatedAddress
&&
94 candidate
.relatedPort
) {
96 sdp
.push(candidate
.relatedAddress
); // was: relAddr
98 sdp
.push(candidate
.relatedPort
); // was: relPort
100 if (candidate
.tcpType
&& candidate
.protocol
.toLowerCase() === 'tcp') {
102 sdp
.push(candidate
.tcpType
);
104 if (candidate
.ufrag
) {
106 sdp
.push(candidate
.ufrag
);
108 return 'candidate:' + sdp
.join(' ');
111 // Parses an ice-options line, returns an array of option tags.
112 // a=ice-options:foo bar
113 SDPUtils
.parseIceOptions = function(line
) {
114 return line
.substr(14).split(' ');
117 // Parses an rtpmap line, returns RTCRtpCoddecParameters. Sample input:
118 // a=rtpmap:111 opus/48000/2
119 SDPUtils
.parseRtpMap = function(line
) {
120 var parts
= line
.substr(9).split(' ');
122 payloadType
: parseInt(parts
.shift(), 10) // was: id
125 parts
= parts
[0].split('/');
127 parsed
.name
= parts
[0];
128 parsed
.clockRate
= parseInt(parts
[1], 10); // was: clockrate
130 parsed
.numChannels
= parts
.length
=== 3 ? parseInt(parts
[2], 10) : 1;
134 // Generate an a=rtpmap line from RTCRtpCodecCapability or
135 // RTCRtpCodecParameters.
136 SDPUtils
.writeRtpMap = function(codec
) {
137 var pt
= codec
.payloadType
;
138 if (codec
.preferredPayloadType
!== undefined) {
139 pt
= codec
.preferredPayloadType
;
141 return 'a=rtpmap:' + pt
+ ' ' + codec
.name
+ '/' + codec
.clockRate
+
142 (codec
.numChannels
!== 1 ? '/' + codec
.numChannels
: '') + '\r\n';
145 // Parses an a=extmap line (headerextension from RFC 5285). Sample input:
146 // a=extmap:2 urn:ietf:params:rtp-hdrext:toffset
147 // a=extmap:2/sendonly urn:ietf:params:rtp-hdrext:toffset
148 SDPUtils
.parseExtmap = function(line
) {
149 var parts
= line
.substr(9).split(' ');
151 id
: parseInt(parts
[0], 10),
152 direction
: parts
[0].indexOf('/') > 0 ? parts
[0].split('/')[1] : 'sendrecv',
157 // Generates a=extmap line from RTCRtpHeaderExtensionParameters or
158 // RTCRtpHeaderExtension.
159 SDPUtils
.writeExtmap = function(headerExtension
) {
160 return 'a=extmap:' + (headerExtension
.id
|| headerExtension
.preferredId
) +
161 (headerExtension
.direction
&& headerExtension
.direction
!== 'sendrecv'
162 ? '/' + headerExtension
.direction
164 ' ' + headerExtension
.uri
+ '\r\n';
167 // Parses an ftmp line, returns dictionary. Sample input:
168 // a=fmtp:96 vbr=on;cng=on
169 // Also deals with vbr=on; cng=on
170 SDPUtils
.parseFmtp = function(line
) {
173 var parts
= line
.substr(line
.indexOf(' ') + 1).split(';');
174 for (var j
= 0; j
< parts
.length
; j
++) {
175 kv
= parts
[j
].trim().split('=');
176 parsed
[kv
[0].trim()] = kv
[1];
181 // Generates an a=ftmp line from RTCRtpCodecCapability or RTCRtpCodecParameters.
182 SDPUtils
.writeFmtp = function(codec
) {
184 var pt
= codec
.payloadType
;
185 if (codec
.preferredPayloadType
!== undefined) {
186 pt
= codec
.preferredPayloadType
;
188 if (codec
.parameters
&& Object
.keys(codec
.parameters
).length
) {
190 Object
.keys(codec
.parameters
).forEach(function(param
) {
191 params
.push(param
+ '=' + codec
.parameters
[param
]);
193 line
+= 'a=fmtp:' + pt
+ ' ' + params
.join(';') + '\r\n';
198 // Parses an rtcp-fb line, returns RTCPRtcpFeedback object. Sample input:
199 // a=rtcp-fb:98 nack rpsi
200 SDPUtils
.parseRtcpFb = function(line
) {
201 var parts
= line
.substr(line
.indexOf(' ') + 1).split(' ');
204 parameter
: parts
.join(' ')
207 // Generate a=rtcp-fb lines from RTCRtpCodecCapability or RTCRtpCodecParameters.
208 SDPUtils
.writeRtcpFb = function(codec
) {
210 var pt
= codec
.payloadType
;
211 if (codec
.preferredPayloadType
!== undefined) {
212 pt
= codec
.preferredPayloadType
;
214 if (codec
.rtcpFeedback
&& codec
.rtcpFeedback
.length
) {
215 // FIXME: special handling for trr-int?
216 codec
.rtcpFeedback
.forEach(function(fb
) {
217 lines
+= 'a=rtcp-fb:' + pt
+ ' ' + fb
.type
+
218 (fb
.parameter
&& fb
.parameter
.length
? ' ' + fb
.parameter
: '') +
225 // Parses an RFC 5576 ssrc media attribute. Sample input:
226 // a=ssrc:3735928559 cname:something
227 SDPUtils
.parseSsrcMedia = function(line
) {
228 var sp
= line
.indexOf(' ');
230 ssrc
: parseInt(line
.substr(7, sp
- 7), 10)
232 var colon
= line
.indexOf(':', sp
);
234 parts
.attribute
= line
.substr(sp
+ 1, colon
- sp
- 1);
235 parts
.value
= line
.substr(colon
+ 1);
237 parts
.attribute
= line
.substr(sp
+ 1);
242 // Extracts the MID (RFC 5888) from a media section.
243 // returns the MID or undefined if no mid line was found.
244 SDPUtils
.getMid = function(mediaSection
) {
245 var mid
= SDPUtils
.matchPrefix(mediaSection
, 'a=mid:')[0];
247 return mid
.substr(6);
251 SDPUtils
.parseFingerprint = function(line
) {
252 var parts
= line
.substr(14).split(' ');
254 algorithm
: parts
[0].toLowerCase(), // algorithm is case-sensitive in Edge.
259 // Extracts DTLS parameters from SDP media section or sessionpart.
260 // FIXME: for consistency with other functions this should only
261 // get the fingerprint line as input. See also getIceParameters.
262 SDPUtils
.getDtlsParameters = function(mediaSection
, sessionpart
) {
263 var lines
= SDPUtils
.matchPrefix(mediaSection
+ sessionpart
,
265 // Note: a=setup line is ignored since we use the 'auto' role.
266 // Note2: 'algorithm' is not case sensitive except in Edge.
269 fingerprints
: lines
.map(SDPUtils
.parseFingerprint
)
273 // Serializes DTLS parameters to SDP.
274 SDPUtils
.writeDtlsParameters = function(params
, setupType
) {
275 var sdp
= 'a=setup:' + setupType
+ '\r\n';
276 params
.fingerprints
.forEach(function(fp
) {
277 sdp
+= 'a=fingerprint:' + fp
.algorithm
+ ' ' + fp
.value
+ '\r\n';
281 // Parses ICE information from SDP media section or sessionpart.
282 // FIXME: for consistency with other functions this should only
283 // get the ice-ufrag and ice-pwd lines as input.
284 SDPUtils
.getIceParameters = function(mediaSection
, sessionpart
) {
285 var lines
= SDPUtils
.splitLines(mediaSection
);
286 // Search in session part, too.
287 lines
= lines
.concat(SDPUtils
.splitLines(sessionpart
));
288 var iceParameters
= {
289 usernameFragment
: lines
.filter(function(line
) {
290 return line
.indexOf('a=ice-ufrag:') === 0;
292 password
: lines
.filter(function(line
) {
293 return line
.indexOf('a=ice-pwd:') === 0;
296 return iceParameters
;
299 // Serializes ICE parameters to SDP.
300 SDPUtils
.writeIceParameters = function(params
) {
301 return 'a=ice-ufrag:' + params
.usernameFragment
+ '\r\n' +
302 'a=ice-pwd:' + params
.password
+ '\r\n';
305 // Parses the SDP media section and returns RTCRtpParameters.
306 SDPUtils
.parseRtpParameters = function(mediaSection
) {
309 headerExtensions
: [],
313 var lines
= SDPUtils
.splitLines(mediaSection
);
314 var mline
= lines
[0].split(' ');
315 for (var i
= 3; i
< mline
.length
; i
++) { // find all codecs from mline[3..]
317 var rtpmapline
= SDPUtils
.matchPrefix(
318 mediaSection
, 'a=rtpmap:' + pt
+ ' ')[0];
320 var codec
= SDPUtils
.parseRtpMap(rtpmapline
);
321 var fmtps
= SDPUtils
.matchPrefix(
322 mediaSection
, 'a=fmtp:' + pt
+ ' ');
323 // Only the first a=fmtp:<pt> is considered.
324 codec
.parameters
= fmtps
.length
? SDPUtils
.parseFmtp(fmtps
[0]) : {};
325 codec
.rtcpFeedback
= SDPUtils
.matchPrefix(
326 mediaSection
, 'a=rtcp-fb:' + pt
+ ' ')
327 .map(SDPUtils
.parseRtcpFb
);
328 description
.codecs
.push(codec
);
329 // parse FEC mechanisms from rtpmap lines.
330 switch (codec
.name
.toUpperCase()) {
333 description
.fecMechanisms
.push(codec
.name
.toUpperCase());
335 default: // only RED and ULPFEC are recognized as FEC mechanisms.
340 SDPUtils
.matchPrefix(mediaSection
, 'a=extmap:').forEach(function(line
) {
341 description
.headerExtensions
.push(SDPUtils
.parseExtmap(line
));
343 // FIXME: parse rtcp.
347 // Generates parts of the SDP media section describing the capabilities /
349 SDPUtils
.writeRtpDescription = function(kind
, caps
) {
353 sdp
+= 'm=' + kind
+ ' ';
354 sdp
+= caps
.codecs
.length
> 0 ? '9' : '0'; // reject if no codecs.
355 sdp
+= ' UDP/TLS/RTP/SAVPF ';
356 sdp
+= caps
.codecs
.map(function(codec
) {
357 if (codec
.preferredPayloadType
!== undefined) {
358 return codec
.preferredPayloadType
;
360 return codec
.payloadType
;
361 }).join(' ') + '\r\n';
363 sdp
+= 'c=IN IP4 0.0.0.0\r\n';
364 sdp
+= 'a=rtcp:9 IN IP4 0.0.0.0\r\n';
366 // Add a=rtpmap lines for each codec. Also fmtp and rtcp-fb.
367 caps
.codecs
.forEach(function(codec
) {
368 sdp
+= SDPUtils
.writeRtpMap(codec
);
369 sdp
+= SDPUtils
.writeFmtp(codec
);
370 sdp
+= SDPUtils
.writeRtcpFb(codec
);
373 caps
.codecs
.forEach(function(codec
) {
374 if (codec
.maxptime
> maxptime
) {
375 maxptime
= codec
.maxptime
;
379 sdp
+= 'a=maxptime:' + maxptime
+ '\r\n';
381 sdp
+= 'a=rtcp-mux\r\n';
383 caps
.headerExtensions
.forEach(function(extension
) {
384 sdp
+= SDPUtils
.writeExtmap(extension
);
386 // FIXME: write fecMechanisms.
390 // Parses the SDP media section and returns an array of
391 // RTCRtpEncodingParameters.
392 SDPUtils
.parseRtpEncodingParameters = function(mediaSection
) {
393 var encodingParameters
= [];
394 var description
= SDPUtils
.parseRtpParameters(mediaSection
);
395 var hasRed
= description
.fecMechanisms
.indexOf('RED') !== -1;
396 var hasUlpfec
= description
.fecMechanisms
.indexOf('ULPFEC') !== -1;
398 // filter a=ssrc:... cname:, ignore PlanB-msid
399 var ssrcs
= SDPUtils
.matchPrefix(mediaSection
, 'a=ssrc:')
400 .map(function(line
) {
401 return SDPUtils
.parseSsrcMedia(line
);
403 .filter(function(parts
) {
404 return parts
.attribute
=== 'cname';
406 var primarySsrc
= ssrcs
.length
> 0 && ssrcs
[0].ssrc
;
409 var flows
= SDPUtils
.matchPrefix(mediaSection
, 'a=ssrc-group:FID')
410 .map(function(line
) {
411 var parts
= line
.split(' ');
413 return parts
.map(function(part
) {
414 return parseInt(part
, 10);
417 if (flows
.length
> 0 && flows
[0].length
> 1 && flows
[0][0] === primarySsrc
) {
418 secondarySsrc
= flows
[0][1];
421 description
.codecs
.forEach(function(codec
) {
422 if (codec
.name
.toUpperCase() === 'RTX' && codec
.parameters
.apt
) {
425 codecPayloadType
: parseInt(codec
.parameters
.apt
, 10),
430 encodingParameters
.push(encParam
);
432 encParam
= JSON
.parse(JSON
.stringify(encParam
));
435 mechanism
: hasUlpfec
? 'red+ulpfec' : 'red'
437 encodingParameters
.push(encParam
);
441 if (encodingParameters
.length
=== 0 && primarySsrc
) {
442 encodingParameters
.push({
447 // we support both b=AS and b=TIAS but interpret AS as TIAS.
448 var bandwidth
= SDPUtils
.matchPrefix(mediaSection
, 'b=');
449 if (bandwidth
.length
) {
450 if (bandwidth
[0].indexOf('b=TIAS:') === 0) {
451 bandwidth
= parseInt(bandwidth
[0].substr(7), 10);
452 } else if (bandwidth
[0].indexOf('b=AS:') === 0) {
453 // use formula from JSEP to convert b=AS to TIAS value.
454 bandwidth
= parseInt(bandwidth
[0].substr(5), 10) * 1000 * 0.95
457 bandwidth
= undefined;
459 encodingParameters
.forEach(function(params
) {
460 params
.maxBitrate
= bandwidth
;
463 return encodingParameters
;
466 // parses http://draft.ortc.org/#rtcrtcpparameters*
467 SDPUtils
.parseRtcpParameters = function(mediaSection
) {
468 var rtcpParameters
= {};
471 // Gets the first SSRC. Note that with RTX there might be multiple
473 var remoteSsrc
= SDPUtils
.matchPrefix(mediaSection
, 'a=ssrc:')
474 .map(function(line
) {
475 return SDPUtils
.parseSsrcMedia(line
);
477 .filter(function(obj
) {
478 return obj
.attribute
=== 'cname';
481 rtcpParameters
.cname
= remoteSsrc
.value
;
482 rtcpParameters
.ssrc
= remoteSsrc
.ssrc
;
485 // Edge uses the compound attribute instead of reducedSize
486 // compound is !reducedSize
487 var rsize
= SDPUtils
.matchPrefix(mediaSection
, 'a=rtcp-rsize');
488 rtcpParameters
.reducedSize
= rsize
.length
> 0;
489 rtcpParameters
.compound
= rsize
.length
=== 0;
491 // parses the rtcp-mux attrÑ–bute.
492 // Note that Edge does not support unmuxed RTCP.
493 var mux
= SDPUtils
.matchPrefix(mediaSection
, 'a=rtcp-mux');
494 rtcpParameters
.mux
= mux
.length
> 0;
496 return rtcpParameters
;
499 // parses either a=msid: or a=ssrc:... msid lines and returns
500 // the id of the MediaStream and MediaStreamTrack.
501 SDPUtils
.parseMsid = function(mediaSection
) {
503 var spec
= SDPUtils
.matchPrefix(mediaSection
, 'a=msid:');
504 if (spec
.length
=== 1) {
505 parts
= spec
[0].substr(7).split(' ');
506 return {stream
: parts
[0], track
: parts
[1]};
508 var planB
= SDPUtils
.matchPrefix(mediaSection
, 'a=ssrc:')
509 .map(function(line
) {
510 return SDPUtils
.parseSsrcMedia(line
);
512 .filter(function(parts
) {
513 return parts
.attribute
=== 'msid';
515 if (planB
.length
> 0) {
516 parts
= planB
[0].value
.split(' ');
517 return {stream
: parts
[0], track
: parts
[1]};
521 // Generate a session ID for SDP.
522 // https://tools.ietf.org/html/draft-ietf-rtcweb-jsep-20#section-5.2.1
523 // recommends using a cryptographically random +ve 64-bit value
524 // but right now this should be acceptable and within the right range
525 SDPUtils
.generateSessionId = function() {
526 return Math
.random().toString().substr(2, 21);
529 // Write boilder plate for start of SDP
530 // sessId argument is optional - if not supplied it will
531 // be generated randomly
532 SDPUtils
.writeSessionBoilerplate = function(sessId
) {
537 sessionId
= SDPUtils
.generateSessionId();
539 // FIXME: sess-id should be an NTP timestamp.
541 'o=thisisadapterortc ' + sessionId
+ ' 2 IN IP4 127.0.0.1\r\n' +
546 SDPUtils
.writeMediaSection = function(transceiver
, caps
, type
, stream
) {
547 var sdp
= SDPUtils
.writeRtpDescription(transceiver
.kind
, caps
);
549 // Map ICE parameters (ufrag, pwd) to SDP.
550 sdp
+= SDPUtils
.writeIceParameters(
551 transceiver
.iceGatherer
.getLocalParameters());
553 // Map DTLS parameters to SDP.
554 sdp
+= SDPUtils
.writeDtlsParameters(
555 transceiver
.dtlsTransport
.getLocalParameters(),
556 type
=== 'offer' ? 'actpass' : 'active');
558 sdp
+= 'a=mid:' + transceiver
.mid
+ '\r\n';
560 if (transceiver
.direction
) {
561 sdp
+= 'a=' + transceiver
.direction
+ '\r\n';
562 } else if (transceiver
.rtpSender
&& transceiver
.rtpReceiver
) {
563 sdp
+= 'a=sendrecv\r\n';
564 } else if (transceiver
.rtpSender
) {
565 sdp
+= 'a=sendonly\r\n';
566 } else if (transceiver
.rtpReceiver
) {
567 sdp
+= 'a=recvonly\r\n';
569 sdp
+= 'a=inactive\r\n';
572 if (transceiver
.rtpSender
) {
574 var msid
= 'msid:' + stream
.id
+ ' ' +
575 transceiver
.rtpSender
.track
.id
+ '\r\n';
579 sdp
+= 'a=ssrc:' + transceiver
.sendEncodingParameters
[0].ssrc
+
581 if (transceiver
.sendEncodingParameters
[0].rtx
) {
582 sdp
+= 'a=ssrc:' + transceiver
.sendEncodingParameters
[0].rtx
.ssrc
+
584 sdp
+= 'a=ssrc-group:FID ' +
585 transceiver
.sendEncodingParameters
[0].ssrc
+ ' ' +
586 transceiver
.sendEncodingParameters
[0].rtx
.ssrc
+
590 // FIXME: this should be written by writeRtpDescription.
591 sdp
+= 'a=ssrc:' + transceiver
.sendEncodingParameters
[0].ssrc
+
592 ' cname:' + SDPUtils
.localCName
+ '\r\n';
593 if (transceiver
.rtpSender
&& transceiver
.sendEncodingParameters
[0].rtx
) {
594 sdp
+= 'a=ssrc:' + transceiver
.sendEncodingParameters
[0].rtx
.ssrc
+
595 ' cname:' + SDPUtils
.localCName
+ '\r\n';
600 // Gets the direction from the mediaSection or the sessionpart.
601 SDPUtils
.getDirection = function(mediaSection
, sessionpart
) {
602 // Look for sendrecv, sendonly, recvonly, inactive, default to sendrecv.
603 var lines
= SDPUtils
.splitLines(mediaSection
);
604 for (var i
= 0; i
< lines
.length
; i
++) {
610 return lines
[i
].substr(2);
612 // FIXME: What should happen here?
616 return SDPUtils
.getDirection(sessionpart
);
621 SDPUtils
.getKind = function(mediaSection
) {
622 var lines
= SDPUtils
.splitLines(mediaSection
);
623 var mline
= lines
[0].split(' ');
624 return mline
[0].substr(2);
627 SDPUtils
.isRejected = function(mediaSection
) {
628 return mediaSection
.split(' ', 2)[1] === '0';
631 // Expose public methods.
632 module
.exports
= SDPUtils
;
634 },{}],2:[function(require
,module
,exports
){
637 * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
639 * Use of this source code is governed by a BSD-style license
640 * that can be found in the LICENSE file in the root of the source
643 /* eslint-env node */
647 var adapterFactory
= require('./adapter_factory.js');
648 module
.exports
= adapterFactory({window
: global
.window
});
650 }).call(this,typeof global
!== "undefined" ? global
: typeof self
!== "undefined" ? self
: typeof window
!== "undefined" ? window
: {})
651 },{"./adapter_factory.js":3}],3:[function(require
,module
,exports
){
653 * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
655 * Use of this source code is governed by a BSD-style license
656 * that can be found in the LICENSE file in the root of the source
659 /* eslint-env node */
663 // Shimming starts here.
664 module
.exports = function(dependencies
) {
665 var window
= dependencies
&& dependencies
.window
;
668 var utils
= require('./utils');
669 var logging
= utils
.log
;
670 var browserDetails
= utils
.detectBrowser(window
);
672 // Export to the adapter global object visible in the browser.
674 browserDetails
: browserDetails
,
675 extractVersion
: utils
.extractVersion
,
676 disableLog
: utils
.disableLog
,
677 disableWarnings
: utils
.disableWarnings
680 // Uncomment the line below if you want logging to occur, including logging
681 // for the switch statement below. Can also be turned on in the browser via
682 // adapter.disableLog(false), but then logging from the switch statement below
684 // require('./utils').disableLog(false);
687 var chromeShim
= require('./chrome/chrome_shim') || null;
688 var edgeShim
= require('./edge/edge_shim') || null;
689 var firefoxShim
= require('./firefox/firefox_shim') || null;
690 var safariShim
= require('./safari/safari_shim') || null;
692 // Shim browser if found.
693 switch (browserDetails
.browser
) {
695 if (!chromeShim
|| !chromeShim
.shimPeerConnection
) {
696 logging('Chrome shim is not included in this adapter release.');
699 logging('adapter.js shimming chrome.');
700 // Export to the adapter global object visible in the browser.
701 adapter
.browserShim
= chromeShim
;
703 chromeShim
.shimGetUserMedia(window
);
704 chromeShim
.shimMediaStream(window
);
705 utils
.shimCreateObjectURL(window
);
706 chromeShim
.shimSourceObject(window
);
707 chromeShim
.shimPeerConnection(window
);
708 chromeShim
.shimOnTrack(window
);
709 chromeShim
.shimGetSendersWithDtmf(window
);
712 if (!firefoxShim
|| !firefoxShim
.shimPeerConnection
) {
713 logging('Firefox shim is not included in this adapter release.');
716 logging('adapter.js shimming firefox.');
717 // Export to the adapter global object visible in the browser.
718 adapter
.browserShim
= firefoxShim
;
720 firefoxShim
.shimGetUserMedia(window
);
721 utils
.shimCreateObjectURL(window
);
722 firefoxShim
.shimSourceObject(window
);
723 firefoxShim
.shimPeerConnection(window
);
724 firefoxShim
.shimOnTrack(window
);
727 if (!edgeShim
|| !edgeShim
.shimPeerConnection
) {
728 logging('MS edge shim is not included in this adapter release.');
731 logging('adapter.js shimming edge.');
732 // Export to the adapter global object visible in the browser.
733 adapter
.browserShim
= edgeShim
;
735 edgeShim
.shimGetUserMedia(window
);
736 utils
.shimCreateObjectURL(window
);
737 edgeShim
.shimPeerConnection(window
);
738 edgeShim
.shimReplaceTrack(window
);
742 logging('Safari shim is not included in this adapter release.');
745 logging('adapter.js shimming safari.');
746 // Export to the adapter global object visible in the browser.
747 adapter
.browserShim
= safariShim
;
748 // shim window.URL.createObjectURL Safari (technical preview)
749 utils
.shimCreateObjectURL(window
);
750 safariShim
.shimRTCIceServerUrls(window
);
751 safariShim
.shimCallbacksAPI(window
);
752 safariShim
.shimLocalStreamsAPI(window
);
753 safariShim
.shimRemoteStreamsAPI(window
);
754 safariShim
.shimGetUserMedia(window
);
757 logging('Unsupported browser!');
764 },{"./chrome/chrome_shim":4,"./edge/edge_shim":6,"./firefox/firefox_shim":9,"./safari/safari_shim":11,"./utils":12}],4:[function(require
,module
,exports
){
767 * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
769 * Use of this source code is governed by a BSD-style license
770 * that can be found in the LICENSE file in the root of the source
773 /* eslint-env node */
775 var utils
= require('../utils.js');
776 var logging
= utils
.log
;
779 shimMediaStream: function(window
) {
780 window
.MediaStream
= window
.MediaStream
|| window
.webkitMediaStream
;
783 shimOnTrack: function(window
) {
784 if (typeof window
=== 'object' && window
.RTCPeerConnection
&& !('ontrack' in
785 window
.RTCPeerConnection
.prototype)) {
786 Object
.defineProperty(window
.RTCPeerConnection
.prototype, 'ontrack', {
788 return this._ontrack
;
793 this.removeEventListener('track', this._ontrack
);
794 this.removeEventListener('addstream', this._ontrackpoly
);
796 this.addEventListener('track', this._ontrack
= f
);
797 this.addEventListener('addstream', this._ontrackpoly = function(e
) {
798 // onaddstream does not fire when a track is added to an existing
799 // stream. But stream.onaddtrack is implemented so we use that.
800 e
.stream
.addEventListener('addtrack', function(te
) {
802 if (window
.RTCPeerConnection
.prototype.getReceivers
) {
803 receiver
= self
.getReceivers().find(function(r
) {
804 return r
.track
.id
=== te
.track
.id
;
807 receiver
= {track
: te
.track
};
810 var event
= new Event('track');
811 event
.track
= te
.track
;
812 event
.receiver
= receiver
;
813 event
.streams
= [e
.stream
];
814 self
.dispatchEvent(event
);
816 e
.stream
.getTracks().forEach(function(track
) {
818 if (window
.RTCPeerConnection
.prototype.getReceivers
) {
819 receiver
= self
.getReceivers().find(function(r
) {
820 return r
.track
.id
=== track
.id
;
823 receiver
= {track
: track
};
825 var event
= new Event('track');
827 event
.receiver
= receiver
;
828 event
.streams
= [e
.stream
];
829 this.dispatchEvent(event
);
837 shimGetSendersWithDtmf: function(window
) {
838 if (typeof window
=== 'object' && window
.RTCPeerConnection
&&
839 !('getSenders' in window
.RTCPeerConnection
.prototype) &&
840 'createDTMFSender' in window
.RTCPeerConnection
.prototype) {
841 window
.RTCPeerConnection
.prototype.getSenders = function() {
842 return this._senders
|| [];
844 var origAddStream
= window
.RTCPeerConnection
.prototype.addStream
;
845 var origRemoveStream
= window
.RTCPeerConnection
.prototype.removeStream
;
847 if (!window
.RTCPeerConnection
.prototype.addTrack
) {
848 window
.RTCPeerConnection
.prototype.addTrack = function(track
, stream
) {
850 if (pc
.signalingState
=== 'closed') {
851 throw new DOMException(
852 'The RTCPeerConnection\'s signalingState is \'closed\'.',
853 'InvalidStateError');
855 var streams
= [].slice
.call(arguments
, 1);
856 if (streams
.length
!== 1 ||
857 !streams
[0].getTracks().find(function(t
) {
860 // this is not fully correct but all we can manage without
861 // [[associated MediaStreams]] internal slot.
862 throw new DOMException(
863 'The adapter.js addTrack polyfill only supports a single ' +
864 ' stream which is associated with the specified track.',
865 'NotSupportedError');
868 pc
._senders
= pc
._senders
|| [];
869 var alreadyExists
= pc
._senders
.find(function(t
) {
870 return t
.track
=== track
;
873 throw new DOMException('Track already exists.',
874 'InvalidAccessError');
877 pc
._streams
= pc
._streams
|| {};
878 var oldStream
= pc
._streams
[stream
.id
];
880 oldStream
.addTrack(track
);
881 pc
.removeStream(oldStream
);
882 pc
.addStream(oldStream
);
884 var newStream
= new window
.MediaStream([track
]);
885 pc
._streams
[stream
.id
] = newStream
;
886 pc
.addStream(newStream
);
892 if (this._dtmf
=== undefined) {
893 if (track
.kind
=== 'audio') {
894 this._dtmf
= pc
.createDTMFSender(track
);
902 pc
._senders
.push(sender
);
906 window
.RTCPeerConnection
.prototype.addStream = function(stream
) {
908 pc
._senders
= pc
._senders
|| [];
909 origAddStream
.apply(pc
, [stream
]);
910 stream
.getTracks().forEach(function(track
) {
914 if (this._dtmf
=== undefined) {
915 if (track
.kind
=== 'audio') {
916 this._dtmf
= pc
.createDTMFSender(track
);
927 window
.RTCPeerConnection
.prototype.removeStream = function(stream
) {
929 pc
._senders
= pc
._senders
|| [];
930 origRemoveStream
.apply(pc
, [stream
]);
931 stream
.getTracks().forEach(function(track
) {
932 var sender
= pc
._senders
.find(function(s
) {
933 return s
.track
=== track
;
936 pc
._senders
.splice(pc
._senders
.indexOf(sender
), 1); // remove sender
940 } else if (typeof window
=== 'object' && window
.RTCPeerConnection
&&
941 'getSenders' in window
.RTCPeerConnection
.prototype &&
942 'createDTMFSender' in window
.RTCPeerConnection
.prototype &&
943 window
.RTCRtpSender
&&
944 !('dtmf' in window
.RTCRtpSender
.prototype)) {
945 var origGetSenders
= window
.RTCPeerConnection
.prototype.getSenders
;
946 window
.RTCPeerConnection
.prototype.getSenders = function() {
948 var senders
= origGetSenders
.apply(pc
, []);
949 senders
.forEach(function(sender
) {
955 Object
.defineProperty(window
.RTCRtpSender
.prototype, 'dtmf', {
957 if (this._dtmf
=== undefined) {
958 if (this.track
.kind
=== 'audio') {
959 this._dtmf
= this._pc
.createDTMFSender(this.track
);
970 shimSourceObject: function(window
) {
971 var URL
= window
&& window
.URL
;
973 if (typeof window
=== 'object') {
974 if (window
.HTMLMediaElement
&&
975 !('srcObject' in window
.HTMLMediaElement
.prototype)) {
976 // Shim the srcObject property, once, when HTMLMediaElement is found.
977 Object
.defineProperty(window
.HTMLMediaElement
.prototype, 'srcObject', {
979 return this._srcObject
;
981 set: function(stream
) {
983 // Use _srcObject as a private property for this shim
984 this._srcObject
= stream
;
986 URL
.revokeObjectURL(this.src
);
993 this.src
= URL
.createObjectURL(stream
);
994 // We need to recreate the blob url when a track is added or
995 // removed. Doing it manually since we want to avoid a recursion.
996 stream
.addEventListener('addtrack', function() {
998 URL
.revokeObjectURL(self
.src
);
1000 self
.src
= URL
.createObjectURL(stream
);
1002 stream
.addEventListener('removetrack', function() {
1004 URL
.revokeObjectURL(self
.src
);
1006 self
.src
= URL
.createObjectURL(stream
);
1014 shimPeerConnection: function(window
) {
1015 var browserDetails
= utils
.detectBrowser(window
);
1017 // The RTCPeerConnection object.
1018 if (!window
.RTCPeerConnection
) {
1019 window
.RTCPeerConnection = function(pcConfig
, pcConstraints
) {
1020 // Translate iceTransportPolicy to iceTransports,
1021 // see https://code.google.com/p/webrtc/issues/detail?id=4869
1022 // this was fixed in M56 along with unprefixing RTCPeerConnection.
1023 logging('PeerConnection');
1024 if (pcConfig
&& pcConfig
.iceTransportPolicy
) {
1025 pcConfig
.iceTransports
= pcConfig
.iceTransportPolicy
;
1028 return new window
.webkitRTCPeerConnection(pcConfig
, pcConstraints
);
1030 window
.RTCPeerConnection
.prototype =
1031 window
.webkitRTCPeerConnection
.prototype;
1032 // wrap static methods. Currently just generateCertificate.
1033 if (window
.webkitRTCPeerConnection
.generateCertificate
) {
1034 Object
.defineProperty(window
.RTCPeerConnection
, 'generateCertificate', {
1036 return window
.webkitRTCPeerConnection
.generateCertificate
;
1041 // migrate from non-spec RTCIceServer.url to RTCIceServer.urls
1042 var OrigPeerConnection
= window
.RTCPeerConnection
;
1043 window
.RTCPeerConnection = function(pcConfig
, pcConstraints
) {
1044 if (pcConfig
&& pcConfig
.iceServers
) {
1045 var newIceServers
= [];
1046 for (var i
= 0; i
< pcConfig
.iceServers
.length
; i
++) {
1047 var server
= pcConfig
.iceServers
[i
];
1048 if (!server
.hasOwnProperty('urls') &&
1049 server
.hasOwnProperty('url')) {
1050 console
.warn('RTCIceServer.url is deprecated! Use urls instead.');
1051 server
= JSON
.parse(JSON
.stringify(server
));
1052 server
.urls
= server
.url
;
1053 newIceServers
.push(server
);
1055 newIceServers
.push(pcConfig
.iceServers
[i
]);
1058 pcConfig
.iceServers
= newIceServers
;
1060 return new OrigPeerConnection(pcConfig
, pcConstraints
);
1062 window
.RTCPeerConnection
.prototype = OrigPeerConnection
.prototype;
1063 // wrap static methods. Currently just generateCertificate.
1064 Object
.defineProperty(window
.RTCPeerConnection
, 'generateCertificate', {
1066 return OrigPeerConnection
.generateCertificate
;
1071 var origGetStats
= window
.RTCPeerConnection
.prototype.getStats
;
1072 window
.RTCPeerConnection
.prototype.getStats = function(selector
,
1073 successCallback
, errorCallback
) {
1075 var args
= arguments
;
1077 // If selector is a function then we are in the old style stats so just
1078 // pass back the original getStats format to avoid breaking old users.
1079 if (arguments
.length
> 0 && typeof selector
=== 'function') {
1080 return origGetStats
.apply(this, arguments
);
1083 // When spec-style getStats is supported, return those when called with
1084 // either no arguments or the selector argument is null.
1085 if (origGetStats
.length
=== 0 && (arguments
.length
=== 0 ||
1086 typeof arguments
[0] !== 'function')) {
1087 return origGetStats
.apply(this, []);
1090 var fixChromeStats_ = function(response
) {
1091 var standardReport
= {};
1092 var reports
= response
.result();
1093 reports
.forEach(function(report
) {
1094 var standardStats
= {
1096 timestamp
: report
.timestamp
,
1098 localcandidate
: 'local-candidate',
1099 remotecandidate
: 'remote-candidate'
1100 }[report
.type
] || report
.type
1102 report
.names().forEach(function(name
) {
1103 standardStats
[name
] = report
.stat(name
);
1105 standardReport
[standardStats
.id
] = standardStats
;
1108 return standardReport
;
1111 // shim getStats with maplike support
1112 var makeMapStats = function(stats
) {
1113 return new Map(Object
.keys(stats
).map(function(key
) {
1114 return [key
, stats
[key
]];
1118 if (arguments
.length
>= 2) {
1119 var successCallbackWrapper_ = function(response
) {
1120 args
[1](makeMapStats(fixChromeStats_(response
)));
1123 return origGetStats
.apply(this, [successCallbackWrapper_
,
1128 return new Promise(function(resolve
, reject
) {
1129 origGetStats
.apply(self
, [
1130 function(response
) {
1131 resolve(makeMapStats(fixChromeStats_(response
)));
1133 }).then(successCallback
, errorCallback
);
1136 // add promise support -- natively available in Chrome 51
1137 if (browserDetails
.version
< 51) {
1138 ['setLocalDescription', 'setRemoteDescription', 'addIceCandidate']
1139 .forEach(function(method
) {
1140 var nativeMethod
= window
.RTCPeerConnection
.prototype[method
];
1141 window
.RTCPeerConnection
.prototype[method
] = function() {
1142 var args
= arguments
;
1144 var promise
= new Promise(function(resolve
, reject
) {
1145 nativeMethod
.apply(self
, [args
[0], resolve
, reject
]);
1147 if (args
.length
< 2) {
1150 return promise
.then(function() {
1151 args
[1].apply(null, []);
1154 if (args
.length
>= 3) {
1155 args
[2].apply(null, [err
]);
1162 // promise support for createOffer and createAnswer. Available (without
1163 // bugs) since M52: crbug/619289
1164 if (browserDetails
.version
< 52) {
1165 ['createOffer', 'createAnswer'].forEach(function(method
) {
1166 var nativeMethod
= window
.RTCPeerConnection
.prototype[method
];
1167 window
.RTCPeerConnection
.prototype[method
] = function() {
1169 if (arguments
.length
< 1 || (arguments
.length
=== 1 &&
1170 typeof arguments
[0] === 'object')) {
1171 var opts
= arguments
.length
=== 1 ? arguments
[0] : undefined;
1172 return new Promise(function(resolve
, reject
) {
1173 nativeMethod
.apply(self
, [resolve
, reject
, opts
]);
1176 return nativeMethod
.apply(this, arguments
);
1181 // shim implicit creation of RTCSessionDescription/RTCIceCandidate
1182 ['setLocalDescription', 'setRemoteDescription', 'addIceCandidate']
1183 .forEach(function(method
) {
1184 var nativeMethod
= window
.RTCPeerConnection
.prototype[method
];
1185 window
.RTCPeerConnection
.prototype[method
] = function() {
1186 arguments
[0] = new ((method
=== 'addIceCandidate') ?
1187 window
.RTCIceCandidate
:
1188 window
.RTCSessionDescription
)(arguments
[0]);
1189 return nativeMethod
.apply(this, arguments
);
1193 // support for addIceCandidate(null or undefined)
1194 var nativeAddIceCandidate
=
1195 window
.RTCPeerConnection
.prototype.addIceCandidate
;
1196 window
.RTCPeerConnection
.prototype.addIceCandidate = function() {
1197 if (!arguments
[0]) {
1199 arguments
[1].apply(null);
1201 return Promise
.resolve();
1203 return nativeAddIceCandidate
.apply(this, arguments
);
1209 // Expose public methods.
1211 shimMediaStream
: chromeShim
.shimMediaStream
,
1212 shimOnTrack
: chromeShim
.shimOnTrack
,
1213 shimGetSendersWithDtmf
: chromeShim
.shimGetSendersWithDtmf
,
1214 shimSourceObject
: chromeShim
.shimSourceObject
,
1215 shimPeerConnection
: chromeShim
.shimPeerConnection
,
1216 shimGetUserMedia
: require('./getusermedia')
1219 },{"../utils.js":12,"./getusermedia":5}],5:[function(require
,module
,exports
){
1221 * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
1223 * Use of this source code is governed by a BSD-style license
1224 * that can be found in the LICENSE file in the root of the source
1227 /* eslint-env node */
1229 var utils
= require('../utils.js');
1230 var logging
= utils
.log
;
1232 // Expose public methods.
1233 module
.exports = function(window
) {
1234 var browserDetails
= utils
.detectBrowser(window
);
1235 var navigator
= window
&& window
.navigator
;
1237 var constraintsToChrome_ = function(c
) {
1238 if (typeof c
!== 'object' || c
.mandatory
|| c
.optional
) {
1242 Object
.keys(c
).forEach(function(key
) {
1243 if (key
=== 'require' || key
=== 'advanced' || key
=== 'mediaSource') {
1246 var r
= (typeof c
[key
] === 'object') ? c
[key
] : {ideal
: c
[key
]};
1247 if (r
.exact
!== undefined && typeof r
.exact
=== 'number') {
1248 r
.min
= r
.max
= r
.exact
;
1250 var oldname_ = function(prefix
, name
) {
1252 return prefix
+ name
.charAt(0).toUpperCase() + name
.slice(1);
1254 return (name
=== 'deviceId') ? 'sourceId' : name
;
1256 if (r
.ideal
!== undefined) {
1257 cc
.optional
= cc
.optional
|| [];
1259 if (typeof r
.ideal
=== 'number') {
1260 oc
[oldname_('min', key
)] = r
.ideal
;
1261 cc
.optional
.push(oc
);
1263 oc
[oldname_('max', key
)] = r
.ideal
;
1264 cc
.optional
.push(oc
);
1266 oc
[oldname_('', key
)] = r
.ideal
;
1267 cc
.optional
.push(oc
);
1270 if (r
.exact
!== undefined && typeof r
.exact
!== 'number') {
1271 cc
.mandatory
= cc
.mandatory
|| {};
1272 cc
.mandatory
[oldname_('', key
)] = r
.exact
;
1274 ['min', 'max'].forEach(function(mix
) {
1275 if (r
[mix
] !== undefined) {
1276 cc
.mandatory
= cc
.mandatory
|| {};
1277 cc
.mandatory
[oldname_(mix
, key
)] = r
[mix
];
1283 cc
.optional
= (cc
.optional
|| []).concat(c
.advanced
);
1288 var shimConstraints_ = function(constraints
, func
) {
1289 constraints
= JSON
.parse(JSON
.stringify(constraints
));
1290 if (constraints
&& typeof constraints
.audio
=== 'object') {
1291 var remap = function(obj
, a
, b
) {
1292 if (a
in obj
&& !(b
in obj
)) {
1297 constraints
= JSON
.parse(JSON
.stringify(constraints
));
1298 remap(constraints
.audio
, 'autoGainControl', 'googAutoGainControl');
1299 remap(constraints
.audio
, 'noiseSuppression', 'googNoiseSuppression');
1300 constraints
.audio
= constraintsToChrome_(constraints
.audio
);
1302 if (constraints
&& typeof constraints
.video
=== 'object') {
1303 // Shim facingMode for mobile & surface pro.
1304 var face
= constraints
.video
.facingMode
;
1305 face
= face
&& ((typeof face
=== 'object') ? face
: {ideal
: face
});
1306 var getSupportedFacingModeLies
= browserDetails
.version
< 61;
1308 if ((face
&& (face
.exact
=== 'user' || face
.exact
=== 'environment' ||
1309 face
.ideal
=== 'user' || face
.ideal
=== 'environment')) &&
1310 !(navigator
.mediaDevices
.getSupportedConstraints
&&
1311 navigator
.mediaDevices
.getSupportedConstraints().facingMode
&&
1312 !getSupportedFacingModeLies
)) {
1313 delete constraints
.video
.facingMode
;
1315 if (face
.exact
=== 'environment' || face
.ideal
=== 'environment') {
1316 matches
= ['back', 'rear'];
1317 } else if (face
.exact
=== 'user' || face
.ideal
=== 'user') {
1318 matches
= ['front'];
1321 // Look for matches in label, or use last cam for back (typical).
1322 return navigator
.mediaDevices
.enumerateDevices()
1323 .then(function(devices
) {
1324 devices
= devices
.filter(function(d
) {
1325 return d
.kind
=== 'videoinput';
1327 var dev
= devices
.find(function(d
) {
1328 return matches
.some(function(match
) {
1329 return d
.label
.toLowerCase().indexOf(match
) !== -1;
1332 if (!dev
&& devices
.length
&& matches
.indexOf('back') !== -1) {
1333 dev
= devices
[devices
.length
- 1]; // more likely the back cam
1336 constraints
.video
.deviceId
= face
.exact
? {exact
: dev
.deviceId
} :
1337 {ideal
: dev
.deviceId
};
1339 constraints
.video
= constraintsToChrome_(constraints
.video
);
1340 logging('chrome: ' + JSON
.stringify(constraints
));
1341 return func(constraints
);
1345 constraints
.video
= constraintsToChrome_(constraints
.video
);
1347 logging('chrome: ' + JSON
.stringify(constraints
));
1348 return func(constraints
);
1351 var shimError_ = function(e
) {
1354 PermissionDeniedError
: 'NotAllowedError',
1355 InvalidStateError
: 'NotReadableError',
1356 DevicesNotFoundError
: 'NotFoundError',
1357 ConstraintNotSatisfiedError
: 'OverconstrainedError',
1358 TrackStartError
: 'NotReadableError',
1359 MediaDeviceFailedDueToShutdown
: 'NotReadableError',
1360 MediaDeviceKillSwitchOn
: 'NotReadableError'
1361 }[e
.name
] || e
.name
,
1363 constraint
: e
.constraintName
,
1364 toString: function() {
1365 return this.name
+ (this.message
&& ': ') + this.message
;
1370 var getUserMedia_ = function(constraints
, onSuccess
, onError
) {
1371 shimConstraints_(constraints
, function(c
) {
1372 navigator
.webkitGetUserMedia(c
, onSuccess
, function(e
) {
1373 onError(shimError_(e
));
1378 navigator
.getUserMedia
= getUserMedia_
;
1380 // Returns the result of getUserMedia as a Promise.
1381 var getUserMediaPromise_ = function(constraints
) {
1382 return new Promise(function(resolve
, reject
) {
1383 navigator
.getUserMedia(constraints
, resolve
, reject
);
1387 if (!navigator
.mediaDevices
) {
1388 navigator
.mediaDevices
= {
1389 getUserMedia
: getUserMediaPromise_
,
1390 enumerateDevices: function() {
1391 return new Promise(function(resolve
) {
1392 var kinds
= {audio
: 'audioinput', video
: 'videoinput'};
1393 return window
.MediaStreamTrack
.getSources(function(devices
) {
1394 resolve(devices
.map(function(device
) {
1395 return {label
: device
.label
,
1396 kind
: kinds
[device
.kind
],
1397 deviceId
: device
.id
,
1403 getSupportedConstraints: function() {
1405 deviceId
: true, echoCancellation
: true, facingMode
: true,
1406 frameRate
: true, height
: true, width
: true
1412 // A shim for getUserMedia method on the mediaDevices object.
1413 // TODO(KaptenJansson) remove once implemented in Chrome stable.
1414 if (!navigator
.mediaDevices
.getUserMedia
) {
1415 navigator
.mediaDevices
.getUserMedia = function(constraints
) {
1416 return getUserMediaPromise_(constraints
);
1419 // Even though Chrome 45 has navigator.mediaDevices and a getUserMedia
1420 // function which returns a Promise, it does not accept spec-style
1422 var origGetUserMedia
= navigator
.mediaDevices
.getUserMedia
.
1423 bind(navigator
.mediaDevices
);
1424 navigator
.mediaDevices
.getUserMedia = function(cs
) {
1425 return shimConstraints_(cs
, function(c
) {
1426 return origGetUserMedia(c
).then(function(stream
) {
1427 if (c
.audio
&& !stream
.getAudioTracks().length
||
1428 c
.video
&& !stream
.getVideoTracks().length
) {
1429 stream
.getTracks().forEach(function(track
) {
1432 throw new DOMException('', 'NotFoundError');
1436 return Promise
.reject(shimError_(e
));
1442 // Dummy devicechange event methods.
1443 // TODO(KaptenJansson) remove once implemented in Chrome stable.
1444 if (typeof navigator
.mediaDevices
.addEventListener
=== 'undefined') {
1445 navigator
.mediaDevices
.addEventListener = function() {
1446 logging('Dummy mediaDevices.addEventListener called.');
1449 if (typeof navigator
.mediaDevices
.removeEventListener
=== 'undefined') {
1450 navigator
.mediaDevices
.removeEventListener = function() {
1451 logging('Dummy mediaDevices.removeEventListener called.');
1456 },{"../utils.js":12}],6:[function(require
,module
,exports
){
1458 * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
1460 * Use of this source code is governed by a BSD-style license
1461 * that can be found in the LICENSE file in the root of the source
1464 /* eslint-env node */
1467 var utils
= require('../utils');
1468 var shimRTCPeerConnection
= require('./rtcpeerconnection_shim');
1471 shimGetUserMedia
: require('./getusermedia'),
1472 shimPeerConnection: function(window
) {
1473 var browserDetails
= utils
.detectBrowser(window
);
1475 if (window
.RTCIceGatherer
) {
1476 // ORTC defines an RTCIceCandidate object but no constructor.
1477 // Not implemented in Edge.
1478 if (!window
.RTCIceCandidate
) {
1479 window
.RTCIceCandidate = function(args
) {
1483 // ORTC does not have a session description object but
1484 // other browsers (i.e. Chrome) that will support both PC and ORTC
1485 // in the future might have this defined already.
1486 if (!window
.RTCSessionDescription
) {
1487 window
.RTCSessionDescription = function(args
) {
1491 // this adds an additional event listener to MediaStrackTrack that signals
1492 // when a tracks enabled property was changed. Workaround for a bug in
1493 // addStream, see below. No longer required in 15025+
1494 if (browserDetails
.version
< 15025) {
1495 var origMSTEnabled
= Object
.getOwnPropertyDescriptor(
1496 window
.MediaStreamTrack
.prototype, 'enabled');
1497 Object
.defineProperty(window
.MediaStreamTrack
.prototype, 'enabled', {
1498 set: function(value
) {
1499 origMSTEnabled
.set.call(this, value
);
1500 var ev
= new Event('enabled');
1502 this.dispatchEvent(ev
);
1507 window
.RTCPeerConnection
=
1508 shimRTCPeerConnection(window
, browserDetails
.version
);
1510 shimReplaceTrack: function(window
) {
1511 // ORTC has replaceTrack -- https://github.com/w3c/ortc/issues/614
1512 if (window
.RTCRtpSender
&&
1513 !('replaceTrack' in window
.RTCRtpSender
.prototype)) {
1514 window
.RTCRtpSender
.prototype.replaceTrack
=
1515 window
.RTCRtpSender
.prototype.setTrack
;
1520 },{"../utils":12,"./getusermedia":7,"./rtcpeerconnection_shim":8}],7:[function(require
,module
,exports
){
1522 * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
1524 * Use of this source code is governed by a BSD-style license
1525 * that can be found in the LICENSE file in the root of the source
1528 /* eslint-env node */
1531 // Expose public methods.
1532 module
.exports = function(window
) {
1533 var navigator
= window
&& window
.navigator
;
1535 var shimError_ = function(e
) {
1537 name
: {PermissionDeniedError
: 'NotAllowedError'}[e
.name
] || e
.name
,
1539 constraint
: e
.constraint
,
1540 toString: function() {
1546 // getUserMedia error shim.
1547 var origGetUserMedia
= navigator
.mediaDevices
.getUserMedia
.
1548 bind(navigator
.mediaDevices
);
1549 navigator
.mediaDevices
.getUserMedia = function(c
) {
1550 return origGetUserMedia(c
).catch(function(e
) {
1551 return Promise
.reject(shimError_(e
));
1556 },{}],8:[function(require
,module
,exports
){
1558 * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
1560 * Use of this source code is governed by a BSD-style license
1561 * that can be found in the LICENSE file in the root of the source
1564 /* eslint-env node */
1567 var SDPUtils
= require('sdp');
1569 // sort tracks such that they follow an a-v-a-v...
1571 function sortTracks(tracks
) {
1572 var audioTracks
= tracks
.filter(function(track
) {
1573 return track
.kind
=== 'audio';
1575 var videoTracks
= tracks
.filter(function(track
) {
1576 return track
.kind
=== 'video';
1579 while (audioTracks
.length
|| videoTracks
.length
) {
1580 if (audioTracks
.length
) {
1581 tracks
.push(audioTracks
.shift());
1583 if (videoTracks
.length
) {
1584 tracks
.push(videoTracks
.shift());
1590 // Edge does not like
1592 // 2) turn: that does not have all of turn:host:port?transport=udp
1593 // 3) turn: with ipv6 addresses
1594 // 4) turn: occurring muliple times
1595 function filterIceServers(iceServers
, edgeVersion
) {
1596 var hasTurn
= false;
1597 iceServers
= JSON
.parse(JSON
.stringify(iceServers
));
1598 return iceServers
.filter(function(server
) {
1599 if (server
&& (server
.urls
|| server
.url
)) {
1600 var urls
= server
.urls
|| server
.url
;
1601 if (server
.url
&& !server
.urls
) {
1602 console
.warn('RTCIceServer.url is deprecated! Use urls instead.');
1604 var isString
= typeof urls
=== 'string';
1608 urls
= urls
.filter(function(url
) {
1609 var validTurn
= url
.indexOf('turn:') === 0 &&
1610 url
.indexOf('transport=udp') !== -1 &&
1611 url
.indexOf('turn:[') === -1 &&
1618 return url
.indexOf('stun:') === 0 && edgeVersion
>= 14393;
1622 server
.urls
= isString
? urls
[0] : urls
;
1623 return !!urls
.length
;
1629 // Determines the intersection of local and remote capabilities.
1630 function getCommonCapabilities(localCapabilities
, remoteCapabilities
) {
1631 var commonCapabilities
= {
1633 headerExtensions
: [],
1637 var findCodecByPayloadType = function(pt
, codecs
) {
1638 pt
= parseInt(pt
, 10);
1639 for (var i
= 0; i
< codecs
.length
; i
++) {
1640 if (codecs
[i
].payloadType
=== pt
||
1641 codecs
[i
].preferredPayloadType
=== pt
) {
1647 var rtxCapabilityMatches = function(lRtx
, rRtx
, lCodecs
, rCodecs
) {
1648 var lCodec
= findCodecByPayloadType(lRtx
.parameters
.apt
, lCodecs
);
1649 var rCodec
= findCodecByPayloadType(rRtx
.parameters
.apt
, rCodecs
);
1650 return lCodec
&& rCodec
&&
1651 lCodec
.name
.toLowerCase() === rCodec
.name
.toLowerCase();
1654 localCapabilities
.codecs
.forEach(function(lCodec
) {
1655 for (var i
= 0; i
< remoteCapabilities
.codecs
.length
; i
++) {
1656 var rCodec
= remoteCapabilities
.codecs
[i
];
1657 if (lCodec
.name
.toLowerCase() === rCodec
.name
.toLowerCase() &&
1658 lCodec
.clockRate
=== rCodec
.clockRate
) {
1659 if (lCodec
.name
.toLowerCase() === 'rtx' &&
1660 lCodec
.parameters
&& rCodec
.parameters
.apt
) {
1661 // for RTX we need to find the local rtx that has a apt
1662 // which points to the same local codec as the remote one.
1663 if (!rtxCapabilityMatches(lCodec
, rCodec
,
1664 localCapabilities
.codecs
, remoteCapabilities
.codecs
)) {
1668 rCodec
= JSON
.parse(JSON
.stringify(rCodec
)); // deepcopy
1669 // number of channels is the highest common number of channels
1670 rCodec
.numChannels
= Math
.min(lCodec
.numChannels
,
1671 rCodec
.numChannels
);
1672 // push rCodec so we reply with offerer payload type
1673 commonCapabilities
.codecs
.push(rCodec
);
1675 // determine common feedback mechanisms
1676 rCodec
.rtcpFeedback
= rCodec
.rtcpFeedback
.filter(function(fb
) {
1677 for (var j
= 0; j
< lCodec
.rtcpFeedback
.length
; j
++) {
1678 if (lCodec
.rtcpFeedback
[j
].type
=== fb
.type
&&
1679 lCodec
.rtcpFeedback
[j
].parameter
=== fb
.parameter
) {
1685 // FIXME: also need to determine .parameters
1686 // see https://github.com/openpeer/ortc/issues/569
1692 localCapabilities
.headerExtensions
.forEach(function(lHeaderExtension
) {
1693 for (var i
= 0; i
< remoteCapabilities
.headerExtensions
.length
;
1695 var rHeaderExtension
= remoteCapabilities
.headerExtensions
[i
];
1696 if (lHeaderExtension
.uri
=== rHeaderExtension
.uri
) {
1697 commonCapabilities
.headerExtensions
.push(rHeaderExtension
);
1703 // FIXME: fecMechanisms
1704 return commonCapabilities
;
1707 // is action=setLocalDescription with type allowed in signalingState
1708 function isActionAllowedInSignalingState(action
, type
, signalingState
) {
1711 setLocalDescription
: ['stable', 'have-local-offer'],
1712 setRemoteDescription
: ['stable', 'have-remote-offer']
1715 setLocalDescription
: ['have-remote-offer', 'have-local-pranswer'],
1716 setRemoteDescription
: ['have-local-offer', 'have-remote-pranswer']
1718 }[type
][action
].indexOf(signalingState
) !== -1;
1721 module
.exports = function(window
, edgeVersion
) {
1722 var RTCPeerConnection = function(config
) {
1725 var _eventTarget
= document
.createDocumentFragment();
1726 ['addEventListener', 'removeEventListener', 'dispatchEvent']
1727 .forEach(function(method
) {
1728 self
[method
] = _eventTarget
[method
].bind(_eventTarget
);
1731 this.needNegotiation
= false;
1733 this.onicecandidate
= null;
1734 this.onaddstream
= null;
1735 this.ontrack
= null;
1736 this.onremovestream
= null;
1737 this.onsignalingstatechange
= null;
1738 this.oniceconnectionstatechange
= null;
1739 this.onicegatheringstatechange
= null;
1740 this.onnegotiationneeded
= null;
1741 this.ondatachannel
= null;
1742 this.canTrickleIceCandidates
= null;
1744 this.localStreams
= [];
1745 this.remoteStreams
= [];
1746 this.getLocalStreams = function() {
1747 return self
.localStreams
;
1749 this.getRemoteStreams = function() {
1750 return self
.remoteStreams
;
1753 this.localDescription
= new window
.RTCSessionDescription({
1757 this.remoteDescription
= new window
.RTCSessionDescription({
1761 this.signalingState
= 'stable';
1762 this.iceConnectionState
= 'new';
1763 this.iceGatheringState
= 'new';
1766 gatherPolicy
: 'all',
1769 if (config
&& config
.iceTransportPolicy
) {
1770 switch (config
.iceTransportPolicy
) {
1773 this.iceOptions
.gatherPolicy
= config
.iceTransportPolicy
;
1776 // don't set iceTransportPolicy.
1780 this.usingBundle
= config
&& config
.bundlePolicy
=== 'max-bundle';
1782 if (config
&& config
.iceServers
) {
1783 this.iceOptions
.iceServers
= filterIceServers(config
.iceServers
,
1786 this._config
= config
|| {};
1788 // per-track iceGathers, iceTransports, dtlsTransports, rtpSenders, ...
1789 // everything that is needed to describe a SDP m-line.
1790 this.transceivers
= [];
1792 // since the iceGatherer is currently created in createOffer but we
1793 // must not emit candidates until after setLocalDescription we buffer
1794 // them in this array.
1795 this._localIceCandidatesBuffer
= [];
1797 this._sdpSessionId
= SDPUtils
.generateSessionId();
1800 RTCPeerConnection
.prototype._emitGatheringStateChange = function() {
1801 var event
= new Event('icegatheringstatechange');
1802 this.dispatchEvent(event
);
1803 if (this.onicegatheringstatechange
!== null) {
1804 this.onicegatheringstatechange(event
);
1808 RTCPeerConnection
.prototype._emitBufferedCandidates = function() {
1810 var sections
= SDPUtils
.splitSections(self
.localDescription
.sdp
);
1811 // FIXME: need to apply ice candidates in a way which is async but
1813 this._localIceCandidatesBuffer
.forEach(function(event
) {
1814 var end
= !event
.candidate
|| Object
.keys(event
.candidate
).length
=== 0;
1816 for (var j
= 1; j
< sections
.length
; j
++) {
1817 if (sections
[j
].indexOf('\r\na=end-of-candidates\r\n') === -1) {
1818 sections
[j
] += 'a=end-of-candidates\r\n';
1822 sections
[event
.candidate
.sdpMLineIndex
+ 1] +=
1823 'a=' + event
.candidate
.candidate
+ '\r\n';
1825 self
.localDescription
.sdp
= sections
.join('');
1826 self
.dispatchEvent(event
);
1827 if (self
.onicecandidate
!== null) {
1828 self
.onicecandidate(event
);
1830 if (!event
.candidate
&& self
.iceGatheringState
!== 'complete') {
1831 var complete
= self
.transceivers
.every(function(transceiver
) {
1832 return transceiver
.iceGatherer
&&
1833 transceiver
.iceGatherer
.state
=== 'completed';
1835 if (complete
&& self
.iceGatheringStateChange
!== 'complete') {
1836 self
.iceGatheringState
= 'complete';
1837 self
._emitGatheringStateChange();
1841 this._localIceCandidatesBuffer
= [];
1844 RTCPeerConnection
.prototype.getConfiguration = function() {
1845 return this._config
;
1848 // internal helper to create a transceiver object.
1849 // (whih is not yet the same as the WebRTC 1.0 transceiver)
1850 RTCPeerConnection
.prototype._createTransceiver = function(kind
) {
1851 var hasBundleTransport
= this.transceivers
.length
> 0;
1856 dtlsTransport
: null,
1857 localCapabilities
: null,
1858 remoteCapabilities
: null,
1863 sendEncodingParameters
: null,
1864 recvEncodingParameters
: null,
1868 if (this.usingBundle
&& hasBundleTransport
) {
1869 transceiver
.iceTransport
= this.transceivers
[0].iceTransport
;
1870 transceiver
.dtlsTransport
= this.transceivers
[0].dtlsTransport
;
1872 var transports
= this._createIceAndDtlsTransports();
1873 transceiver
.iceTransport
= transports
.iceTransport
;
1874 transceiver
.dtlsTransport
= transports
.dtlsTransport
;
1876 this.transceivers
.push(transceiver
);
1880 RTCPeerConnection
.prototype.addTrack = function(track
, stream
) {
1882 for (var i
= 0; i
< this.transceivers
.length
; i
++) {
1883 if (!this.transceivers
[i
].track
&&
1884 this.transceivers
[i
].kind
=== track
.kind
) {
1885 transceiver
= this.transceivers
[i
];
1889 transceiver
= this._createTransceiver(track
.kind
);
1892 transceiver
.track
= track
;
1893 transceiver
.stream
= stream
;
1894 transceiver
.rtpSender
= new window
.RTCRtpSender(track
,
1895 transceiver
.dtlsTransport
);
1897 this._maybeFireNegotiationNeeded();
1898 return transceiver
.rtpSender
;
1901 RTCPeerConnection
.prototype.addStream = function(stream
) {
1903 if (edgeVersion
>= 15025) {
1904 this.localStreams
.push(stream
);
1905 stream
.getTracks().forEach(function(track
) {
1906 self
.addTrack(track
, stream
);
1909 // Clone is necessary for local demos mostly, attaching directly
1910 // to two different senders does not work (build 10547).
1911 // Fixed in 15025 (or earlier)
1912 var clonedStream
= stream
.clone();
1913 stream
.getTracks().forEach(function(track
, idx
) {
1914 var clonedTrack
= clonedStream
.getTracks()[idx
];
1915 track
.addEventListener('enabled', function(event
) {
1916 clonedTrack
.enabled
= event
.enabled
;
1919 clonedStream
.getTracks().forEach(function(track
) {
1920 self
.addTrack(track
, clonedStream
);
1922 this.localStreams
.push(clonedStream
);
1924 this._maybeFireNegotiationNeeded();
1927 RTCPeerConnection
.prototype.removeStream = function(stream
) {
1928 var idx
= this.localStreams
.indexOf(stream
);
1930 this.localStreams
.splice(idx
, 1);
1931 this._maybeFireNegotiationNeeded();
1935 RTCPeerConnection
.prototype.getSenders = function() {
1936 return this.transceivers
.filter(function(transceiver
) {
1937 return !!transceiver
.rtpSender
;
1939 .map(function(transceiver
) {
1940 return transceiver
.rtpSender
;
1944 RTCPeerConnection
.prototype.getReceivers = function() {
1945 return this.transceivers
.filter(function(transceiver
) {
1946 return !!transceiver
.rtpReceiver
;
1948 .map(function(transceiver
) {
1949 return transceiver
.rtpReceiver
;
1953 // Create ICE gatherer and hook it up.
1954 RTCPeerConnection
.prototype._createIceGatherer = function(mid
,
1957 var iceGatherer
= new window
.RTCIceGatherer(self
.iceOptions
);
1958 iceGatherer
.onlocalcandidate = function(evt
) {
1959 var event
= new Event('icecandidate');
1960 event
.candidate
= {sdpMid
: mid
, sdpMLineIndex
: sdpMLineIndex
};
1962 var cand
= evt
.candidate
;
1963 var end
= !cand
|| Object
.keys(cand
).length
=== 0;
1964 // Edge emits an empty object for RTCIceCandidateComplete‥
1966 // polyfill since RTCIceGatherer.state is not implemented in
1968 if (iceGatherer
.state
=== undefined) {
1969 iceGatherer
.state
= 'completed';
1972 // RTCIceCandidate doesn't have a component, needs to be added
1974 event
.candidate
.candidate
= SDPUtils
.writeCandidate(cand
);
1977 // update local description.
1978 var sections
= SDPUtils
.splitSections(self
.localDescription
.sdp
);
1980 sections
[event
.candidate
.sdpMLineIndex
+ 1] +=
1981 'a=' + event
.candidate
.candidate
+ '\r\n';
1983 sections
[event
.candidate
.sdpMLineIndex
+ 1] +=
1984 'a=end-of-candidates\r\n';
1986 self
.localDescription
.sdp
= sections
.join('');
1987 var transceivers
= self
._pendingOffer
? self
._pendingOffer
:
1989 var complete
= transceivers
.every(function(transceiver
) {
1990 return transceiver
.iceGatherer
&&
1991 transceiver
.iceGatherer
.state
=== 'completed';
1994 // Emit candidate if localDescription is set.
1995 // Also emits null candidate when all gatherers are complete.
1996 switch (self
.iceGatheringState
) {
1999 self
._localIceCandidatesBuffer
.push(event
);
2001 if (end
&& complete
) {
2002 self
._localIceCandidatesBuffer
.push(
2003 new Event('icecandidate'));
2007 self
._emitBufferedCandidates();
2009 self
.dispatchEvent(event
);
2010 if (self
.onicecandidate
!== null) {
2011 self
.onicecandidate(event
);
2015 self
.dispatchEvent(new Event('icecandidate'));
2016 if (self
.onicecandidate
!== null) {
2017 self
.onicecandidate(new Event('icecandidate'));
2019 self
.iceGatheringState
= 'complete';
2020 self
._emitGatheringStateChange();
2024 // should not happen... currently!
2033 // Create ICE transport and DTLS transport.
2034 RTCPeerConnection
.prototype._createIceAndDtlsTransports = function() {
2036 var iceTransport
= new window
.RTCIceTransport(null);
2037 iceTransport
.onicestatechange = function() {
2038 self
._updateConnectionState();
2041 var dtlsTransport
= new window
.RTCDtlsTransport(iceTransport
);
2042 dtlsTransport
.ondtlsstatechange = function() {
2043 self
._updateConnectionState();
2045 dtlsTransport
.onerror = function() {
2046 // onerror does not set state to failed by itself.
2047 Object
.defineProperty(dtlsTransport
, 'state',
2048 {value
: 'failed', writable
: true});
2049 self
._updateConnectionState();
2053 iceTransport
: iceTransport
,
2054 dtlsTransport
: dtlsTransport
2058 // Destroy ICE gatherer, ICE transport and DTLS transport.
2059 // Without triggering the callbacks.
2060 RTCPeerConnection
.prototype._disposeIceAndDtlsTransports = function(
2062 var iceGatherer
= this.transceivers
[sdpMLineIndex
].iceGatherer
;
2064 delete iceGatherer
.onlocalcandidate
;
2065 delete this.transceivers
[sdpMLineIndex
].iceGatherer
;
2067 var iceTransport
= this.transceivers
[sdpMLineIndex
].iceTransport
;
2069 delete iceTransport
.onicestatechange
;
2070 delete this.transceivers
[sdpMLineIndex
].iceTransport
;
2072 var dtlsTransport
= this.transceivers
[sdpMLineIndex
].dtlsTransport
;
2073 if (dtlsTransport
) {
2074 delete dtlsTransport
.ondtlssttatechange
;
2075 delete dtlsTransport
.onerror
;
2076 delete this.transceivers
[sdpMLineIndex
].dtlsTransport
;
2080 // Start the RTP Sender and Receiver for a transceiver.
2081 RTCPeerConnection
.prototype._transceive = function(transceiver
,
2083 var params
= getCommonCapabilities(transceiver
.localCapabilities
,
2084 transceiver
.remoteCapabilities
);
2085 if (send
&& transceiver
.rtpSender
) {
2086 params
.encodings
= transceiver
.sendEncodingParameters
;
2088 cname
: SDPUtils
.localCName
,
2089 compound
: transceiver
.rtcpParameters
.compound
2091 if (transceiver
.recvEncodingParameters
.length
) {
2092 params
.rtcp
.ssrc
= transceiver
.recvEncodingParameters
[0].ssrc
;
2094 transceiver
.rtpSender
.send(params
);
2096 if (recv
&& transceiver
.rtpReceiver
) {
2097 // remove RTX field in Edge 14942
2098 if (transceiver
.kind
=== 'video'
2099 && transceiver
.recvEncodingParameters
2100 && edgeVersion
< 15019) {
2101 transceiver
.recvEncodingParameters
.forEach(function(p
) {
2105 params
.encodings
= transceiver
.recvEncodingParameters
;
2107 cname
: transceiver
.rtcpParameters
.cname
,
2108 compound
: transceiver
.rtcpParameters
.compound
2110 if (transceiver
.sendEncodingParameters
.length
) {
2111 params
.rtcp
.ssrc
= transceiver
.sendEncodingParameters
[0].ssrc
;
2113 transceiver
.rtpReceiver
.receive(params
);
2117 RTCPeerConnection
.prototype.setLocalDescription = function(description
) {
2120 if (!isActionAllowedInSignalingState('setLocalDescription',
2121 description
.type
, this.signalingState
)) {
2122 var e
= new Error('Can not set local ' + description
.type
+
2123 ' in state ' + this.signalingState
);
2124 e
.name
= 'InvalidStateError';
2125 if (arguments
.length
> 2 && typeof arguments
[2] === 'function') {
2126 window
.setTimeout(arguments
[2], 0, e
);
2128 return Promise
.reject(e
);
2133 if (description
.type
=== 'offer') {
2134 // FIXME: What was the purpose of this empty if statement?
2135 // if (!this._pendingOffer) {
2137 if (this._pendingOffer
) {
2138 // VERY limited support for SDP munging. Limited to:
2139 // * changing the order of codecs
2140 sections
= SDPUtils
.splitSections(description
.sdp
);
2141 sessionpart
= sections
.shift();
2142 sections
.forEach(function(mediaSection
, sdpMLineIndex
) {
2143 var caps
= SDPUtils
.parseRtpParameters(mediaSection
);
2144 self
._pendingOffer
[sdpMLineIndex
].localCapabilities
= caps
;
2146 this.transceivers
= this._pendingOffer
;
2147 delete this._pendingOffer
;
2149 } else if (description
.type
=== 'answer') {
2150 sections
= SDPUtils
.splitSections(self
.remoteDescription
.sdp
);
2151 sessionpart
= sections
.shift();
2152 var isIceLite
= SDPUtils
.matchPrefix(sessionpart
,
2153 'a=ice-lite').length
> 0;
2154 sections
.forEach(function(mediaSection
, sdpMLineIndex
) {
2155 var transceiver
= self
.transceivers
[sdpMLineIndex
];
2156 var iceGatherer
= transceiver
.iceGatherer
;
2157 var iceTransport
= transceiver
.iceTransport
;
2158 var dtlsTransport
= transceiver
.dtlsTransport
;
2159 var localCapabilities
= transceiver
.localCapabilities
;
2160 var remoteCapabilities
= transceiver
.remoteCapabilities
;
2162 var rejected
= SDPUtils
.isRejected(mediaSection
);
2164 if (!rejected
&& !transceiver
.isDatachannel
) {
2165 var remoteIceParameters
= SDPUtils
.getIceParameters(
2166 mediaSection
, sessionpart
);
2167 var remoteDtlsParameters
= SDPUtils
.getDtlsParameters(
2168 mediaSection
, sessionpart
);
2170 remoteDtlsParameters
.role
= 'server';
2173 if (!self
.usingBundle
|| sdpMLineIndex
=== 0) {
2174 iceTransport
.start(iceGatherer
, remoteIceParameters
,
2175 isIceLite
? 'controlling' : 'controlled');
2176 dtlsTransport
.start(remoteDtlsParameters
);
2179 // Calculate intersection of capabilities.
2180 var params
= getCommonCapabilities(localCapabilities
,
2181 remoteCapabilities
);
2183 // Start the RTCRtpSender. The RTCRtpReceiver for this
2184 // transceiver has already been started in setRemoteDescription.
2185 self
._transceive(transceiver
,
2186 params
.codecs
.length
> 0,
2192 this.localDescription
= {
2193 type
: description
.type
,
2194 sdp
: description
.sdp
2196 switch (description
.type
) {
2198 this._updateSignalingState('have-local-offer');
2201 this._updateSignalingState('stable');
2204 throw new TypeError('unsupported type "' + description
.type
+
2208 // If a success callback was provided, emit ICE candidates after it
2209 // has been executed. Otherwise, emit callback after the Promise is
2211 var hasCallback
= arguments
.length
> 1 &&
2212 typeof arguments
[1] === 'function';
2214 var cb
= arguments
[1];
2215 window
.setTimeout(function() {
2217 if (self
.iceGatheringState
=== 'new') {
2218 self
.iceGatheringState
= 'gathering';
2219 self
._emitGatheringStateChange();
2221 self
._emitBufferedCandidates();
2224 var p
= Promise
.resolve();
2227 if (self
.iceGatheringState
=== 'new') {
2228 self
.iceGatheringState
= 'gathering';
2229 self
._emitGatheringStateChange();
2231 // Usually candidates will be emitted earlier.
2232 window
.setTimeout(self
._emitBufferedCandidates
.bind(self
), 500);
2238 RTCPeerConnection
.prototype.setRemoteDescription = function(description
) {
2241 if (!isActionAllowedInSignalingState('setRemoteDescription',
2242 description
.type
, this.signalingState
)) {
2243 var e
= new Error('Can not set remote ' + description
.type
+
2244 ' in state ' + this.signalingState
);
2245 e
.name
= 'InvalidStateError';
2246 if (arguments
.length
> 2 && typeof arguments
[2] === 'function') {
2247 window
.setTimeout(arguments
[2], 0, e
);
2249 return Promise
.reject(e
);
2253 var receiverList
= [];
2254 var sections
= SDPUtils
.splitSections(description
.sdp
);
2255 var sessionpart
= sections
.shift();
2256 var isIceLite
= SDPUtils
.matchPrefix(sessionpart
,
2257 'a=ice-lite').length
> 0;
2258 var usingBundle
= SDPUtils
.matchPrefix(sessionpart
,
2259 'a=group:BUNDLE ').length
> 0;
2260 this.usingBundle
= usingBundle
;
2261 var iceOptions
= SDPUtils
.matchPrefix(sessionpart
,
2262 'a=ice-options:')[0];
2264 this.canTrickleIceCandidates
= iceOptions
.substr(14).split(' ')
2265 .indexOf('trickle') >= 0;
2267 this.canTrickleIceCandidates
= false;
2270 sections
.forEach(function(mediaSection
, sdpMLineIndex
) {
2271 var lines
= SDPUtils
.splitLines(mediaSection
);
2272 var kind
= SDPUtils
.getKind(mediaSection
);
2273 var rejected
= SDPUtils
.isRejected(mediaSection
);
2274 var protocol
= lines
[0].substr(2).split(' ')[2];
2276 var direction
= SDPUtils
.getDirection(mediaSection
, sessionpart
);
2277 var remoteMsid
= SDPUtils
.parseMsid(mediaSection
);
2279 var mid
= SDPUtils
.getMid(mediaSection
) || SDPUtils
.generateIdentifier();
2281 // Reject datachannels which are not implemented yet.
2282 if (kind
=== 'application' && protocol
=== 'DTLS/SCTP') {
2283 self
.transceivers
[sdpMLineIndex
] = {
2295 var sendEncodingParameters
;
2296 var recvEncodingParameters
;
2297 var localCapabilities
;
2300 // FIXME: ensure the mediaSection has rtcp-mux set.
2301 var remoteCapabilities
= SDPUtils
.parseRtpParameters(mediaSection
);
2302 var remoteIceParameters
;
2303 var remoteDtlsParameters
;
2305 remoteIceParameters
= SDPUtils
.getIceParameters(mediaSection
,
2307 remoteDtlsParameters
= SDPUtils
.getDtlsParameters(mediaSection
,
2309 remoteDtlsParameters
.role
= 'client';
2311 recvEncodingParameters
=
2312 SDPUtils
.parseRtpEncodingParameters(mediaSection
);
2314 var rtcpParameters
= SDPUtils
.parseRtcpParameters(mediaSection
);
2316 var isComplete
= SDPUtils
.matchPrefix(mediaSection
,
2317 'a=end-of-candidates', sessionpart
).length
> 0;
2318 var cands
= SDPUtils
.matchPrefix(mediaSection
, 'a=candidate:')
2319 .map(function(cand
) {
2320 return SDPUtils
.parseCandidate(cand
);
2322 .filter(function(cand
) {
2323 return cand
.component
=== '1' || cand
.component
=== 1;
2326 // Check if we can use BUNDLE and dispose transports.
2327 if ((description
.type
=== 'offer' || description
.type
=== 'answer') &&
2328 !rejected
&& usingBundle
&& sdpMLineIndex
> 0 &&
2329 self
.transceivers
[sdpMLineIndex
]) {
2330 self
._disposeIceAndDtlsTransports(sdpMLineIndex
);
2331 self
.transceivers
[sdpMLineIndex
].iceGatherer
=
2332 self
.transceivers
[0].iceGatherer
;
2333 self
.transceivers
[sdpMLineIndex
].iceTransport
=
2334 self
.transceivers
[0].iceTransport
;
2335 self
.transceivers
[sdpMLineIndex
].dtlsTransport
=
2336 self
.transceivers
[0].dtlsTransport
;
2337 if (self
.transceivers
[sdpMLineIndex
].rtpSender
) {
2338 self
.transceivers
[sdpMLineIndex
].rtpSender
.setTransport(
2339 self
.transceivers
[0].dtlsTransport
);
2341 if (self
.transceivers
[sdpMLineIndex
].rtpReceiver
) {
2342 self
.transceivers
[sdpMLineIndex
].rtpReceiver
.setTransport(
2343 self
.transceivers
[0].dtlsTransport
);
2346 if (description
.type
=== 'offer' && !rejected
) {
2347 transceiver
= self
.transceivers
[sdpMLineIndex
] ||
2348 self
._createTransceiver(kind
);
2349 transceiver
.mid
= mid
;
2351 if (!transceiver
.iceGatherer
) {
2352 transceiver
.iceGatherer
= usingBundle
&& sdpMLineIndex
> 0 ?
2353 self
.transceivers
[0].iceGatherer
:
2354 self
._createIceGatherer(mid
, sdpMLineIndex
);
2357 if (isComplete
&& (!usingBundle
|| sdpMLineIndex
=== 0)) {
2358 transceiver
.iceTransport
.setRemoteCandidates(cands
);
2361 localCapabilities
= window
.RTCRtpReceiver
.getCapabilities(kind
);
2363 // filter RTX until additional stuff needed for RTX is implemented
2365 if (edgeVersion
< 15019) {
2366 localCapabilities
.codecs
= localCapabilities
.codecs
.filter(
2368 return codec
.name
!== 'rtx';
2372 sendEncodingParameters
= [{
2373 ssrc
: (2 * sdpMLineIndex
+ 2) * 1001
2376 if (direction
=== 'sendrecv' || direction
=== 'sendonly') {
2377 rtpReceiver
= new window
.RTCRtpReceiver(transceiver
.dtlsTransport
,
2380 track
= rtpReceiver
.track
;
2381 // FIXME: does not work with Plan B.
2383 if (!streams
[remoteMsid
.stream
]) {
2384 streams
[remoteMsid
.stream
] = new window
.MediaStream();
2385 Object
.defineProperty(streams
[remoteMsid
.stream
], 'id', {
2387 return remoteMsid
.stream
;
2391 Object
.defineProperty(track
, 'id', {
2393 return remoteMsid
.track
;
2396 streams
[remoteMsid
.stream
].addTrack(track
);
2397 receiverList
.push([track
, rtpReceiver
,
2398 streams
[remoteMsid
.stream
]]);
2400 if (!streams
.default) {
2401 streams
.default = new window
.MediaStream();
2403 streams
.default.addTrack(track
);
2404 receiverList
.push([track
, rtpReceiver
, streams
.default]);
2408 transceiver
.localCapabilities
= localCapabilities
;
2409 transceiver
.remoteCapabilities
= remoteCapabilities
;
2410 transceiver
.rtpReceiver
= rtpReceiver
;
2411 transceiver
.rtcpParameters
= rtcpParameters
;
2412 transceiver
.sendEncodingParameters
= sendEncodingParameters
;
2413 transceiver
.recvEncodingParameters
= recvEncodingParameters
;
2415 // Start the RTCRtpReceiver now. The RTPSender is started in
2416 // setLocalDescription.
2417 self
._transceive(self
.transceivers
[sdpMLineIndex
],
2419 direction
=== 'sendrecv' || direction
=== 'sendonly');
2420 } else if (description
.type
=== 'answer' && !rejected
) {
2421 transceiver
= self
.transceivers
[sdpMLineIndex
];
2422 iceGatherer
= transceiver
.iceGatherer
;
2423 iceTransport
= transceiver
.iceTransport
;
2424 dtlsTransport
= transceiver
.dtlsTransport
;
2425 rtpReceiver
= transceiver
.rtpReceiver
;
2426 sendEncodingParameters
= transceiver
.sendEncodingParameters
;
2427 localCapabilities
= transceiver
.localCapabilities
;
2429 self
.transceivers
[sdpMLineIndex
].recvEncodingParameters
=
2430 recvEncodingParameters
;
2431 self
.transceivers
[sdpMLineIndex
].remoteCapabilities
=
2433 self
.transceivers
[sdpMLineIndex
].rtcpParameters
= rtcpParameters
;
2435 if ((isIceLite
|| isComplete
) && cands
.length
) {
2436 iceTransport
.setRemoteCandidates(cands
);
2438 if (!usingBundle
|| sdpMLineIndex
=== 0) {
2439 iceTransport
.start(iceGatherer
, remoteIceParameters
,
2441 dtlsTransport
.start(remoteDtlsParameters
);
2444 self
._transceive(transceiver
,
2445 direction
=== 'sendrecv' || direction
=== 'recvonly',
2446 direction
=== 'sendrecv' || direction
=== 'sendonly');
2449 (direction
=== 'sendrecv' || direction
=== 'sendonly')) {
2450 track
= rtpReceiver
.track
;
2452 if (!streams
[remoteMsid
.stream
]) {
2453 streams
[remoteMsid
.stream
] = new window
.MediaStream();
2455 streams
[remoteMsid
.stream
].addTrack(track
);
2456 receiverList
.push([track
, rtpReceiver
, streams
[remoteMsid
.stream
]]);
2458 if (!streams
.default) {
2459 streams
.default = new window
.MediaStream();
2461 streams
.default.addTrack(track
);
2462 receiverList
.push([track
, rtpReceiver
, streams
.default]);
2465 // FIXME: actually the receiver should be created later.
2466 delete transceiver
.rtpReceiver
;
2471 this.remoteDescription
= {
2472 type
: description
.type
,
2473 sdp
: description
.sdp
2475 switch (description
.type
) {
2477 this._updateSignalingState('have-remote-offer');
2480 this._updateSignalingState('stable');
2483 throw new TypeError('unsupported type "' + description
.type
+
2486 Object
.keys(streams
).forEach(function(sid
) {
2487 var stream
= streams
[sid
];
2488 if (stream
.getTracks().length
) {
2489 self
.remoteStreams
.push(stream
);
2490 var event
= new Event('addstream');
2491 event
.stream
= stream
;
2492 self
.dispatchEvent(event
);
2493 if (self
.onaddstream
!== null) {
2494 window
.setTimeout(function() {
2495 self
.onaddstream(event
);
2499 receiverList
.forEach(function(item
) {
2500 var track
= item
[0];
2501 var receiver
= item
[1];
2502 if (stream
.id
!== item
[2].id
) {
2505 var trackEvent
= new Event('track');
2506 trackEvent
.track
= track
;
2507 trackEvent
.receiver
= receiver
;
2508 trackEvent
.streams
= [stream
];
2509 self
.dispatchEvent(trackEvent
);
2510 if (self
.ontrack
!== null) {
2511 window
.setTimeout(function() {
2512 self
.ontrack(trackEvent
);
2519 // check whether addIceCandidate({}) was called within four seconds after
2520 // setRemoteDescription.
2521 window
.setTimeout(function() {
2522 if (!(self
&& self
.transceivers
)) {
2525 self
.transceivers
.forEach(function(transceiver
) {
2526 if (transceiver
.iceTransport
&&
2527 transceiver
.iceTransport
.state
=== 'new' &&
2528 transceiver
.iceTransport
.getRemoteCandidates().length
> 0) {
2529 console
.warn('Timeout for addRemoteCandidate. Consider sending ' +
2530 'an end-of-candidates notification');
2531 transceiver
.iceTransport
.addRemoteCandidate({});
2536 if (arguments
.length
> 1 && typeof arguments
[1] === 'function') {
2537 window
.setTimeout(arguments
[1], 0);
2539 return Promise
.resolve();
2542 RTCPeerConnection
.prototype.close = function() {
2543 this.transceivers
.forEach(function(transceiver
) {
2545 if (transceiver.iceGatherer) {
2546 transceiver.iceGatherer.close();
2549 if (transceiver
.iceTransport
) {
2550 transceiver
.iceTransport
.stop();
2552 if (transceiver
.dtlsTransport
) {
2553 transceiver
.dtlsTransport
.stop();
2555 if (transceiver
.rtpSender
) {
2556 transceiver
.rtpSender
.stop();
2558 if (transceiver
.rtpReceiver
) {
2559 transceiver
.rtpReceiver
.stop();
2562 // FIXME: clean up tracks, local streams, remote streams, etc
2563 this._updateSignalingState('closed');
2566 // Update the signaling state.
2567 RTCPeerConnection
.prototype._updateSignalingState = function(newState
) {
2568 this.signalingState
= newState
;
2569 var event
= new Event('signalingstatechange');
2570 this.dispatchEvent(event
);
2571 if (this.onsignalingstatechange
!== null) {
2572 this.onsignalingstatechange(event
);
2576 // Determine whether to fire the negotiationneeded event.
2577 RTCPeerConnection
.prototype._maybeFireNegotiationNeeded = function() {
2579 if (this.signalingState
!== 'stable' || this.needNegotiation
=== true) {
2582 this.needNegotiation
= true;
2583 window
.setTimeout(function() {
2584 if (self
.needNegotiation
=== false) {
2587 self
.needNegotiation
= false;
2588 var event
= new Event('negotiationneeded');
2589 self
.dispatchEvent(event
);
2590 if (self
.onnegotiationneeded
!== null) {
2591 self
.onnegotiationneeded(event
);
2596 // Update the connection state.
2597 RTCPeerConnection
.prototype._updateConnectionState = function() {
2610 this.transceivers
.forEach(function(transceiver
) {
2611 states
[transceiver
.iceTransport
.state
]++;
2612 states
[transceiver
.dtlsTransport
.state
]++;
2614 // ICETransport.completed and connected are the same for this purpose.
2615 states
.connected
+= states
.completed
;
2618 if (states
.failed
> 0) {
2619 newState
= 'failed';
2620 } else if (states
.connecting
> 0 || states
.checking
> 0) {
2621 newState
= 'connecting';
2622 } else if (states
.disconnected
> 0) {
2623 newState
= 'disconnected';
2624 } else if (states
.new > 0) {
2626 } else if (states
.connected
> 0 || states
.completed
> 0) {
2627 newState
= 'connected';
2630 if (newState
!== self
.iceConnectionState
) {
2631 self
.iceConnectionState
= newState
;
2632 var event
= new Event('iceconnectionstatechange');
2633 this.dispatchEvent(event
);
2634 if (this.oniceconnectionstatechange
!== null) {
2635 this.oniceconnectionstatechange(event
);
2640 RTCPeerConnection
.prototype.createOffer = function() {
2642 if (this._pendingOffer
) {
2643 throw new Error('createOffer called while there is a pending offer.');
2646 if (arguments
.length
=== 1 && typeof arguments
[0] !== 'function') {
2647 offerOptions
= arguments
[0];
2648 } else if (arguments
.length
=== 3) {
2649 offerOptions
= arguments
[2];
2652 var numAudioTracks
= this.transceivers
.filter(function(t
) {
2653 return t
.kind
=== 'audio';
2655 var numVideoTracks
= this.transceivers
.filter(function(t
) {
2656 return t
.kind
=== 'video';
2659 // Determine number of audio and video tracks we need to send/recv.
2661 // Reject Chrome legacy constraints.
2662 if (offerOptions
.mandatory
|| offerOptions
.optional
) {
2663 throw new TypeError(
2664 'Legacy mandatory/optional constraints not supported.');
2666 if (offerOptions
.offerToReceiveAudio
!== undefined) {
2667 if (offerOptions
.offerToReceiveAudio
=== true) {
2669 } else if (offerOptions
.offerToReceiveAudio
=== false) {
2672 numAudioTracks
= offerOptions
.offerToReceiveAudio
;
2675 if (offerOptions
.offerToReceiveVideo
!== undefined) {
2676 if (offerOptions
.offerToReceiveVideo
=== true) {
2678 } else if (offerOptions
.offerToReceiveVideo
=== false) {
2681 numVideoTracks
= offerOptions
.offerToReceiveVideo
;
2686 this.transceivers
.forEach(function(transceiver
) {
2687 if (transceiver
.kind
=== 'audio') {
2689 if (numAudioTracks
< 0) {
2690 transceiver
.wantReceive
= false;
2692 } else if (transceiver
.kind
=== 'video') {
2694 if (numVideoTracks
< 0) {
2695 transceiver
.wantReceive
= false;
2700 // Create M-lines for recvonly streams.
2701 while (numAudioTracks
> 0 || numVideoTracks
> 0) {
2702 if (numAudioTracks
> 0) {
2703 this._createTransceiver('audio');
2706 if (numVideoTracks
> 0) {
2707 this._createTransceiver('video');
2712 var transceivers
= sortTracks(this.transceivers
);
2714 var sdp
= SDPUtils
.writeSessionBoilerplate(this._sdpSessionId
);
2715 transceivers
.forEach(function(transceiver
, sdpMLineIndex
) {
2716 // For each track, create an ice gatherer, ice transport,
2717 // dtls transport, potentially rtpsender and rtpreceiver.
2718 var track
= transceiver
.track
;
2719 var kind
= transceiver
.kind
;
2720 var mid
= SDPUtils
.generateIdentifier();
2721 transceiver
.mid
= mid
;
2723 if (!transceiver
.iceGatherer
) {
2724 transceiver
.iceGatherer
= self
.usingBundle
&& sdpMLineIndex
> 0 ?
2725 transceivers
[0].iceGatherer
:
2726 self
._createIceGatherer(mid
, sdpMLineIndex
);
2729 var localCapabilities
= window
.RTCRtpSender
.getCapabilities(kind
);
2730 // filter RTX until additional stuff needed for RTX is implemented
2732 if (edgeVersion
< 15019) {
2733 localCapabilities
.codecs
= localCapabilities
.codecs
.filter(
2735 return codec
.name
!== 'rtx';
2738 localCapabilities
.codecs
.forEach(function(codec
) {
2739 // work around https://bugs.chromium.org/p/webrtc/issues/detail?id=6552
2740 // by adding level-asymmetry-allowed=1
2741 if (codec
.name
=== 'H264' &&
2742 codec
.parameters
['level-asymmetry-allowed'] === undefined) {
2743 codec
.parameters
['level-asymmetry-allowed'] = '1';
2747 // generate an ssrc now, to be used later in rtpSender.send
2748 var sendEncodingParameters
= [{
2749 ssrc
: (2 * sdpMLineIndex
+ 1) * 1001
2753 if (edgeVersion
>= 15019 && kind
=== 'video') {
2754 sendEncodingParameters
[0].rtx
= {
2755 ssrc
: (2 * sdpMLineIndex
+ 1) * 1001 + 1
2760 if (transceiver
.wantReceive
) {
2761 transceiver
.rtpReceiver
= new window
.RTCRtpReceiver(
2762 transceiver
.dtlsTransport
,
2767 transceiver
.localCapabilities
= localCapabilities
;
2768 transceiver
.sendEncodingParameters
= sendEncodingParameters
;
2771 // always offer BUNDLE and dispose on return if not supported.
2772 if (this._config
.bundlePolicy
!== 'max-compat') {
2773 sdp
+= 'a=group:BUNDLE ' + transceivers
.map(function(t
) {
2775 }).join(' ') + '\r\n';
2777 sdp
+= 'a=ice-options:trickle\r\n';
2779 transceivers
.forEach(function(transceiver
, sdpMLineIndex
) {
2780 sdp
+= SDPUtils
.writeMediaSection(transceiver
,
2781 transceiver
.localCapabilities
, 'offer', transceiver
.stream
);
2782 sdp
+= 'a=rtcp-rsize\r\n';
2785 this._pendingOffer
= transceivers
;
2786 var desc
= new window
.RTCSessionDescription({
2790 if (arguments
.length
&& typeof arguments
[0] === 'function') {
2791 window
.setTimeout(arguments
[0], 0, desc
);
2793 return Promise
.resolve(desc
);
2796 RTCPeerConnection
.prototype.createAnswer = function() {
2797 var sdp
= SDPUtils
.writeSessionBoilerplate(this._sdpSessionId
);
2798 if (this.usingBundle
) {
2799 sdp
+= 'a=group:BUNDLE ' + this.transceivers
.map(function(t
) {
2801 }).join(' ') + '\r\n';
2803 this.transceivers
.forEach(function(transceiver
, sdpMLineIndex
) {
2804 if (transceiver
.isDatachannel
) {
2805 sdp
+= 'm=application 0 DTLS/SCTP 5000\r\n' +
2806 'c=IN IP4 0.0.0.0\r\n' +
2807 'a=mid:' + transceiver
.mid
+ '\r\n';
2811 // FIXME: look at direction.
2812 if (transceiver
.stream
) {
2814 if (transceiver
.kind
=== 'audio') {
2815 localTrack
= transceiver
.stream
.getAudioTracks()[0];
2816 } else if (transceiver
.kind
=== 'video') {
2817 localTrack
= transceiver
.stream
.getVideoTracks()[0];
2821 if (edgeVersion
>= 15019 && transceiver
.kind
=== 'video') {
2822 transceiver
.sendEncodingParameters
[0].rtx
= {
2823 ssrc
: (2 * sdpMLineIndex
+ 2) * 1001 + 1
2829 // Calculate intersection of capabilities.
2830 var commonCapabilities
= getCommonCapabilities(
2831 transceiver
.localCapabilities
,
2832 transceiver
.remoteCapabilities
);
2834 var hasRtx
= commonCapabilities
.codecs
.filter(function(c
) {
2835 return c
.name
.toLowerCase() === 'rtx';
2837 if (!hasRtx
&& transceiver
.sendEncodingParameters
[0].rtx
) {
2838 delete transceiver
.sendEncodingParameters
[0].rtx
;
2841 sdp
+= SDPUtils
.writeMediaSection(transceiver
, commonCapabilities
,
2842 'answer', transceiver
.stream
);
2843 if (transceiver
.rtcpParameters
&&
2844 transceiver
.rtcpParameters
.reducedSize
) {
2845 sdp
+= 'a=rtcp-rsize\r\n';
2849 var desc
= new window
.RTCSessionDescription({
2853 if (arguments
.length
&& typeof arguments
[0] === 'function') {
2854 window
.setTimeout(arguments
[0], 0, desc
);
2856 return Promise
.resolve(desc
);
2859 RTCPeerConnection
.prototype.addIceCandidate = function(candidate
) {
2861 for (var j
= 0; j
< this.transceivers
.length
; j
++) {
2862 this.transceivers
[j
].iceTransport
.addRemoteCandidate({});
2863 if (this.usingBundle
) {
2864 return Promise
.resolve();
2868 var mLineIndex
= candidate
.sdpMLineIndex
;
2869 if (candidate
.sdpMid
) {
2870 for (var i
= 0; i
< this.transceivers
.length
; i
++) {
2871 if (this.transceivers
[i
].mid
=== candidate
.sdpMid
) {
2877 var transceiver
= this.transceivers
[mLineIndex
];
2879 var cand
= Object
.keys(candidate
.candidate
).length
> 0 ?
2880 SDPUtils
.parseCandidate(candidate
.candidate
) : {};
2881 // Ignore Chrome's invalid candidates since Edge does not like them.
2882 if (cand
.protocol
=== 'tcp' && (cand
.port
=== 0 || cand
.port
=== 9)) {
2883 return Promise
.resolve();
2885 // Ignore RTCP candidates, we assume RTCP-MUX.
2886 if (cand
.component
&&
2887 !(cand
.component
=== '1' || cand
.component
=== 1)) {
2888 return Promise
.resolve();
2890 transceiver
.iceTransport
.addRemoteCandidate(cand
);
2892 // update the remoteDescription.
2893 var sections
= SDPUtils
.splitSections(this.remoteDescription
.sdp
);
2894 sections
[mLineIndex
+ 1] += (cand
.type
? candidate
.candidate
.trim()
2895 : 'a=end-of-candidates') + '\r\n';
2896 this.remoteDescription
.sdp
= sections
.join('');
2899 if (arguments
.length
> 1 && typeof arguments
[1] === 'function') {
2900 window
.setTimeout(arguments
[1], 0);
2902 return Promise
.resolve();
2905 RTCPeerConnection
.prototype.getStats = function() {
2907 this.transceivers
.forEach(function(transceiver
) {
2908 ['rtpSender', 'rtpReceiver', 'iceGatherer', 'iceTransport',
2909 'dtlsTransport'].forEach(function(method
) {
2910 if (transceiver
[method
]) {
2911 promises
.push(transceiver
[method
].getStats());
2915 var cb
= arguments
.length
> 1 && typeof arguments
[1] === 'function' &&
2917 var fixStatsType = function(stat
) {
2919 inboundrtp
: 'inbound-rtp',
2920 outboundrtp
: 'outbound-rtp',
2921 candidatepair
: 'candidate-pair',
2922 localcandidate
: 'local-candidate',
2923 remotecandidate
: 'remote-candidate'
2924 }[stat
.type
] || stat
.type
;
2926 return new Promise(function(resolve
) {
2927 // shim getStats with maplike support
2928 var results
= new Map();
2929 Promise
.all(promises
).then(function(res
) {
2930 res
.forEach(function(result
) {
2931 Object
.keys(result
).forEach(function(id
) {
2932 result
[id
].type
= fixStatsType(result
[id
]);
2933 results
.set(id
, result
[id
]);
2937 window
.setTimeout(cb
, 0, results
);
2943 return RTCPeerConnection
;
2946 },{"sdp":1}],9:[function(require
,module
,exports
){
2948 * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
2950 * Use of this source code is governed by a BSD-style license
2951 * that can be found in the LICENSE file in the root of the source
2954 /* eslint-env node */
2957 var utils
= require('../utils');
2960 shimOnTrack: function(window
) {
2961 if (typeof window
=== 'object' && window
.RTCPeerConnection
&& !('ontrack' in
2962 window
.RTCPeerConnection
.prototype)) {
2963 Object
.defineProperty(window
.RTCPeerConnection
.prototype, 'ontrack', {
2965 return this._ontrack
;
2968 if (this._ontrack
) {
2969 this.removeEventListener('track', this._ontrack
);
2970 this.removeEventListener('addstream', this._ontrackpoly
);
2972 this.addEventListener('track', this._ontrack
= f
);
2973 this.addEventListener('addstream', this._ontrackpoly = function(e
) {
2974 e
.stream
.getTracks().forEach(function(track
) {
2975 var event
= new Event('track');
2976 event
.track
= track
;
2977 event
.receiver
= {track
: track
};
2978 event
.streams
= [e
.stream
];
2979 this.dispatchEvent(event
);
2987 shimSourceObject: function(window
) {
2988 // Firefox has supported mozSrcObject since FF22, unprefixed in 42.
2989 if (typeof window
=== 'object') {
2990 if (window
.HTMLMediaElement
&&
2991 !('srcObject' in window
.HTMLMediaElement
.prototype)) {
2992 // Shim the srcObject property, once, when HTMLMediaElement is found.
2993 Object
.defineProperty(window
.HTMLMediaElement
.prototype, 'srcObject', {
2995 return this.mozSrcObject
;
2997 set: function(stream
) {
2998 this.mozSrcObject
= stream
;
3005 shimPeerConnection: function(window
) {
3006 var browserDetails
= utils
.detectBrowser(window
);
3008 if (typeof window
!== 'object' || !(window
.RTCPeerConnection
||
3009 window
.mozRTCPeerConnection
)) {
3010 return; // probably media.peerconnection.enabled=false in about:config
3012 // The RTCPeerConnection object.
3013 if (!window
.RTCPeerConnection
) {
3014 window
.RTCPeerConnection = function(pcConfig
, pcConstraints
) {
3015 if (browserDetails
.version
< 38) {
3016 // .urls is not supported in FF < 38.
3017 // create RTCIceServers with a single url.
3018 if (pcConfig
&& pcConfig
.iceServers
) {
3019 var newIceServers
= [];
3020 for (var i
= 0; i
< pcConfig
.iceServers
.length
; i
++) {
3021 var server
= pcConfig
.iceServers
[i
];
3022 if (server
.hasOwnProperty('urls')) {
3023 for (var j
= 0; j
< server
.urls
.length
; j
++) {
3027 if (server
.urls
[j
].indexOf('turn') === 0) {
3028 newServer
.username
= server
.username
;
3029 newServer
.credential
= server
.credential
;
3031 newIceServers
.push(newServer
);
3034 newIceServers
.push(pcConfig
.iceServers
[i
]);
3037 pcConfig
.iceServers
= newIceServers
;
3040 return new window
.mozRTCPeerConnection(pcConfig
, pcConstraints
);
3042 window
.RTCPeerConnection
.prototype =
3043 window
.mozRTCPeerConnection
.prototype;
3045 // wrap static methods. Currently just generateCertificate.
3046 if (window
.mozRTCPeerConnection
.generateCertificate
) {
3047 Object
.defineProperty(window
.RTCPeerConnection
, 'generateCertificate', {
3049 return window
.mozRTCPeerConnection
.generateCertificate
;
3054 window
.RTCSessionDescription
= window
.mozRTCSessionDescription
;
3055 window
.RTCIceCandidate
= window
.mozRTCIceCandidate
;
3058 // shim away need for obsolete RTCIceCandidate/RTCSessionDescription.
3059 ['setLocalDescription', 'setRemoteDescription', 'addIceCandidate']
3060 .forEach(function(method
) {
3061 var nativeMethod
= window
.RTCPeerConnection
.prototype[method
];
3062 window
.RTCPeerConnection
.prototype[method
] = function() {
3063 arguments
[0] = new ((method
=== 'addIceCandidate') ?
3064 window
.RTCIceCandidate
:
3065 window
.RTCSessionDescription
)(arguments
[0]);
3066 return nativeMethod
.apply(this, arguments
);
3070 // support for addIceCandidate(null or undefined)
3071 var nativeAddIceCandidate
=
3072 window
.RTCPeerConnection
.prototype.addIceCandidate
;
3073 window
.RTCPeerConnection
.prototype.addIceCandidate = function() {
3074 if (!arguments
[0]) {
3076 arguments
[1].apply(null);
3078 return Promise
.resolve();
3080 return nativeAddIceCandidate
.apply(this, arguments
);
3083 // shim getStats with maplike support
3084 var makeMapStats = function(stats
) {
3085 var map
= new Map();
3086 Object
.keys(stats
).forEach(function(key
) {
3087 map
.set(key
, stats
[key
]);
3088 map
[key
] = stats
[key
];
3093 var modernStatsTypes
= {
3094 inboundrtp
: 'inbound-rtp',
3095 outboundrtp
: 'outbound-rtp',
3096 candidatepair
: 'candidate-pair',
3097 localcandidate
: 'local-candidate',
3098 remotecandidate
: 'remote-candidate'
3101 var nativeGetStats
= window
.RTCPeerConnection
.prototype.getStats
;
3102 window
.RTCPeerConnection
.prototype.getStats = function(
3107 return nativeGetStats
.apply(this, [selector
|| null])
3108 .then(function(stats
) {
3109 if (browserDetails
.version
< 48) {
3110 stats
= makeMapStats(stats
);
3112 if (browserDetails
.version
< 53 && !onSucc
) {
3113 // Shim only promise getStats with spec-hyphens in type names
3114 // Leave callback version alone; misc old uses of forEach before Map
3116 stats
.forEach(function(stat
) {
3117 stat
.type
= modernStatsTypes
[stat
.type
] || stat
.type
;
3120 if (e
.name
!== 'TypeError') {
3123 // Avoid TypeError: "type" is read-only, in old versions. 34-43ish
3124 stats
.forEach(function(stat
, i
) {
3125 stats
.set(i
, Object
.assign({}, stat
, {
3126 type
: modernStatsTypes
[stat
.type
] || stat
.type
3133 .then(onSucc
, onErr
);
3138 // Expose public methods.
3140 shimOnTrack
: firefoxShim
.shimOnTrack
,
3141 shimSourceObject
: firefoxShim
.shimSourceObject
,
3142 shimPeerConnection
: firefoxShim
.shimPeerConnection
,
3143 shimGetUserMedia
: require('./getusermedia')
3146 },{"../utils":12,"./getusermedia":10}],10:[function(require
,module
,exports
){
3148 * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
3150 * Use of this source code is governed by a BSD-style license
3151 * that can be found in the LICENSE file in the root of the source
3154 /* eslint-env node */
3157 var utils
= require('../utils');
3158 var logging
= utils
.log
;
3160 // Expose public methods.
3161 module
.exports = function(window
) {
3162 var browserDetails
= utils
.detectBrowser(window
);
3163 var navigator
= window
&& window
.navigator
;
3164 var MediaStreamTrack
= window
&& window
.MediaStreamTrack
;
3166 var shimError_ = function(e
) {
3169 InternalError
: 'NotReadableError',
3170 NotSupportedError
: 'TypeError',
3171 PermissionDeniedError
: 'NotAllowedError',
3172 SecurityError
: 'NotAllowedError'
3173 }[e
.name
] || e
.name
,
3175 'The operation is insecure.': 'The request is not allowed by the ' +
3176 'user agent or the platform in the current context.'
3177 }[e
.message
] || e
.message
,
3178 constraint
: e
.constraint
,
3179 toString: function() {
3180 return this.name
+ (this.message
&& ': ') + this.message
;
3185 // getUserMedia constraints shim.
3186 var getUserMedia_ = function(constraints
, onSuccess
, onError
) {
3187 var constraintsToFF37_ = function(c
) {
3188 if (typeof c
!== 'object' || c
.require
) {
3192 Object
.keys(c
).forEach(function(key
) {
3193 if (key
=== 'require' || key
=== 'advanced' || key
=== 'mediaSource') {
3196 var r
= c
[key
] = (typeof c
[key
] === 'object') ?
3197 c
[key
] : {ideal
: c
[key
]};
3198 if (r
.min
!== undefined ||
3199 r
.max
!== undefined || r
.exact
!== undefined) {
3202 if (r
.exact
!== undefined) {
3203 if (typeof r
.exact
=== 'number') {
3204 r
. min
= r
.max
= r
.exact
;
3210 if (r
.ideal
!== undefined) {
3211 c
.advanced
= c
.advanced
|| [];
3213 if (typeof r
.ideal
=== 'number') {
3214 oc
[key
] = {min
: r
.ideal
, max
: r
.ideal
};
3218 c
.advanced
.push(oc
);
3220 if (!Object
.keys(r
).length
) {
3225 if (require
.length
) {
3226 c
.require
= require
;
3230 constraints
= JSON
.parse(JSON
.stringify(constraints
));
3231 if (browserDetails
.version
< 38) {
3232 logging('spec: ' + JSON
.stringify(constraints
));
3233 if (constraints
.audio
) {
3234 constraints
.audio
= constraintsToFF37_(constraints
.audio
);
3236 if (constraints
.video
) {
3237 constraints
.video
= constraintsToFF37_(constraints
.video
);
3239 logging('ff37: ' + JSON
.stringify(constraints
));
3241 return navigator
.mozGetUserMedia(constraints
, onSuccess
, function(e
) {
3242 onError(shimError_(e
));
3246 // Returns the result of getUserMedia as a Promise.
3247 var getUserMediaPromise_ = function(constraints
) {
3248 return new Promise(function(resolve
, reject
) {
3249 getUserMedia_(constraints
, resolve
, reject
);
3253 // Shim for mediaDevices on older versions.
3254 if (!navigator
.mediaDevices
) {
3255 navigator
.mediaDevices
= {getUserMedia
: getUserMediaPromise_
,
3256 addEventListener: function() { },
3257 removeEventListener: function() { }
3260 navigator
.mediaDevices
.enumerateDevices
=
3261 navigator
.mediaDevices
.enumerateDevices
|| function() {
3262 return new Promise(function(resolve
) {
3264 {kind
: 'audioinput', deviceId
: 'default', label
: '', groupId
: ''},
3265 {kind
: 'videoinput', deviceId
: 'default', label
: '', groupId
: ''}
3271 if (browserDetails
.version
< 41) {
3272 // Work around http://bugzil.la/1169665
3273 var orgEnumerateDevices
=
3274 navigator
.mediaDevices
.enumerateDevices
.bind(navigator
.mediaDevices
);
3275 navigator
.mediaDevices
.enumerateDevices = function() {
3276 return orgEnumerateDevices().then(undefined, function(e
) {
3277 if (e
.name
=== 'NotFoundError') {
3284 if (browserDetails
.version
< 49) {
3285 var origGetUserMedia
= navigator
.mediaDevices
.getUserMedia
.
3286 bind(navigator
.mediaDevices
);
3287 navigator
.mediaDevices
.getUserMedia = function(c
) {
3288 return origGetUserMedia(c
).then(function(stream
) {
3289 // Work around https://bugzil.la/802326
3290 if (c
.audio
&& !stream
.getAudioTracks().length
||
3291 c
.video
&& !stream
.getVideoTracks().length
) {
3292 stream
.getTracks().forEach(function(track
) {
3295 throw new DOMException('The object can not be found here.',
3300 return Promise
.reject(shimError_(e
));
3304 if (!(browserDetails
.version
> 55 &&
3305 'autoGainControl' in navigator
.mediaDevices
.getSupportedConstraints())) {
3306 var remap = function(obj
, a
, b
) {
3307 if (a
in obj
&& !(b
in obj
)) {
3313 var nativeGetUserMedia
= navigator
.mediaDevices
.getUserMedia
.
3314 bind(navigator
.mediaDevices
);
3315 navigator
.mediaDevices
.getUserMedia = function(c
) {
3316 if (typeof c
=== 'object' && typeof c
.audio
=== 'object') {
3317 c
= JSON
.parse(JSON
.stringify(c
));
3318 remap(c
.audio
, 'autoGainControl', 'mozAutoGainControl');
3319 remap(c
.audio
, 'noiseSuppression', 'mozNoiseSuppression');
3321 return nativeGetUserMedia(c
);
3324 if (MediaStreamTrack
&& MediaStreamTrack
.prototype.getSettings
) {
3325 var nativeGetSettings
= MediaStreamTrack
.prototype.getSettings
;
3326 MediaStreamTrack
.prototype.getSettings = function() {
3327 var obj
= nativeGetSettings
.apply(this, arguments
);
3328 remap(obj
, 'mozAutoGainControl', 'autoGainControl');
3329 remap(obj
, 'mozNoiseSuppression', 'noiseSuppression');
3334 if (MediaStreamTrack
&& MediaStreamTrack
.prototype.applyConstraints
) {
3335 var nativeApplyConstraints
= MediaStreamTrack
.prototype.applyConstraints
;
3336 MediaStreamTrack
.prototype.applyConstraints = function(c
) {
3337 if (this.kind
=== 'audio' && typeof c
=== 'object') {
3338 c
= JSON
.parse(JSON
.stringify(c
));
3339 remap(c
, 'autoGainControl', 'mozAutoGainControl');
3340 remap(c
, 'noiseSuppression', 'mozNoiseSuppression');
3342 return nativeApplyConstraints
.apply(this, [c
]);
3346 navigator
.getUserMedia = function(constraints
, onSuccess
, onError
) {
3347 if (browserDetails
.version
< 44) {
3348 return getUserMedia_(constraints
, onSuccess
, onError
);
3350 // Replace Firefox 44+'s deprecation warning with unprefixed version.
3351 console
.warn('navigator.getUserMedia has been replaced by ' +
3352 'navigator.mediaDevices.getUserMedia');
3353 navigator
.mediaDevices
.getUserMedia(constraints
).then(onSuccess
, onError
);
3357 },{"../utils":12}],11:[function(require
,module
,exports
){
3359 * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
3361 * Use of this source code is governed by a BSD-style license
3362 * that can be found in the LICENSE file in the root of the source
3366 var utils
= require('../utils');
3369 // TODO: DrAlex, should be here, double check against LayoutTests
3371 // TODO: once the back-end for the mac port is done, add.
3372 // TODO: check for webkitGTK+
3373 // shimPeerConnection: function() { },
3375 shimLocalStreamsAPI: function(window
) {
3376 if (typeof window
!== 'object' || !window
.RTCPeerConnection
) {
3379 if (!('getLocalStreams' in window
.RTCPeerConnection
.prototype)) {
3380 window
.RTCPeerConnection
.prototype.getLocalStreams = function() {
3381 if (!this._localStreams
) {
3382 this._localStreams
= [];
3384 return this._localStreams
;
3387 if (!('getStreamById' in window
.RTCPeerConnection
.prototype)) {
3388 window
.RTCPeerConnection
.prototype.getStreamById = function(id
) {
3390 if (this._localStreams
) {
3391 this._localStreams
.forEach(function(stream
) {
3392 if (stream
.id
=== id
) {
3397 if (this._remoteStreams
) {
3398 this._remoteStreams
.forEach(function(stream
) {
3399 if (stream
.id
=== id
) {
3407 if (!('addStream' in window
.RTCPeerConnection
.prototype)) {
3408 var _addTrack
= window
.RTCPeerConnection
.prototype.addTrack
;
3409 window
.RTCPeerConnection
.prototype.addStream = function(stream
) {
3410 if (!this._localStreams
) {
3411 this._localStreams
= [];
3413 if (this._localStreams
.indexOf(stream
) === -1) {
3414 this._localStreams
.push(stream
);
3417 stream
.getTracks().forEach(function(track
) {
3418 _addTrack
.call(self
, track
, stream
);
3422 window
.RTCPeerConnection
.prototype.addTrack = function(track
, stream
) {
3424 if (!this._localStreams
) {
3425 this._localStreams
= [stream
];
3426 } else if (this._localStreams
.indexOf(stream
) === -1) {
3427 this._localStreams
.push(stream
);
3430 _addTrack
.call(this, track
, stream
);
3433 if (!('removeStream' in window
.RTCPeerConnection
.prototype)) {
3434 window
.RTCPeerConnection
.prototype.removeStream = function(stream
) {
3435 if (!this._localStreams
) {
3436 this._localStreams
= [];
3438 var index
= this._localStreams
.indexOf(stream
);
3442 this._localStreams
.splice(index
, 1);
3444 var tracks
= stream
.getTracks();
3445 this.getSenders().forEach(function(sender
) {
3446 if (tracks
.indexOf(sender
.track
) !== -1) {
3447 self
.removeTrack(sender
);
3453 shimRemoteStreamsAPI: function(window
) {
3454 if (typeof window
!== 'object' || !window
.RTCPeerConnection
) {
3457 if (!('getRemoteStreams' in window
.RTCPeerConnection
.prototype)) {
3458 window
.RTCPeerConnection
.prototype.getRemoteStreams = function() {
3459 return this._remoteStreams
? this._remoteStreams
: [];
3462 if (!('onaddstream' in window
.RTCPeerConnection
.prototype)) {
3463 Object
.defineProperty(window
.RTCPeerConnection
.prototype, 'onaddstream', {
3465 return this._onaddstream
;
3468 if (this._onaddstream
) {
3469 this.removeEventListener('addstream', this._onaddstream
);
3470 this.removeEventListener('track', this._onaddstreampoly
);
3472 this.addEventListener('addstream', this._onaddstream
= f
);
3473 this.addEventListener('track', this._onaddstreampoly = function(e
) {
3474 var stream
= e
.streams
[0];
3475 if (!this._remoteStreams
) {
3476 this._remoteStreams
= [];
3478 if (this._remoteStreams
.indexOf(stream
) >= 0) {
3481 this._remoteStreams
.push(stream
);
3482 var event
= new Event('addstream');
3483 event
.stream
= e
.streams
[0];
3484 this.dispatchEvent(event
);
3490 shimCallbacksAPI: function(window
) {
3491 if (typeof window
!== 'object' || !window
.RTCPeerConnection
) {
3494 var prototype = window
.RTCPeerConnection
.prototype;
3495 var createOffer
= prototype.createOffer
;
3496 var createAnswer
= prototype.createAnswer
;
3497 var setLocalDescription
= prototype.setLocalDescription
;
3498 var setRemoteDescription
= prototype.setRemoteDescription
;
3499 var addIceCandidate
= prototype.addIceCandidate
;
3501 prototype.createOffer = function(successCallback
, failureCallback
) {
3502 var options
= (arguments
.length
>= 2) ? arguments
[2] : arguments
[0];
3503 var promise
= createOffer
.apply(this, [options
]);
3504 if (!failureCallback
) {
3507 promise
.then(successCallback
, failureCallback
);
3508 return Promise
.resolve();
3511 prototype.createAnswer = function(successCallback
, failureCallback
) {
3512 var options
= (arguments
.length
>= 2) ? arguments
[2] : arguments
[0];
3513 var promise
= createAnswer
.apply(this, [options
]);
3514 if (!failureCallback
) {
3517 promise
.then(successCallback
, failureCallback
);
3518 return Promise
.resolve();
3521 var withCallback = function(description
, successCallback
, failureCallback
) {
3522 var promise
= setLocalDescription
.apply(this, [description
]);
3523 if (!failureCallback
) {
3526 promise
.then(successCallback
, failureCallback
);
3527 return Promise
.resolve();
3529 prototype.setLocalDescription
= withCallback
;
3531 withCallback = function(description
, successCallback
, failureCallback
) {
3532 var promise
= setRemoteDescription
.apply(this, [description
]);
3533 if (!failureCallback
) {
3536 promise
.then(successCallback
, failureCallback
);
3537 return Promise
.resolve();
3539 prototype.setRemoteDescription
= withCallback
;
3541 withCallback = function(candidate
, successCallback
, failureCallback
) {
3542 var promise
= addIceCandidate
.apply(this, [candidate
]);
3543 if (!failureCallback
) {
3546 promise
.then(successCallback
, failureCallback
);
3547 return Promise
.resolve();
3549 prototype.addIceCandidate
= withCallback
;
3551 shimGetUserMedia: function(window
) {
3552 var navigator
= window
&& window
.navigator
;
3554 if (!navigator
.getUserMedia
) {
3555 if (navigator
.webkitGetUserMedia
) {
3556 navigator
.getUserMedia
= navigator
.webkitGetUserMedia
.bind(navigator
);
3557 } else if (navigator
.mediaDevices
&&
3558 navigator
.mediaDevices
.getUserMedia
) {
3559 navigator
.getUserMedia = function(constraints
, cb
, errcb
) {
3560 navigator
.mediaDevices
.getUserMedia(constraints
)
3566 shimRTCIceServerUrls: function(window
) {
3567 // migrate from non-spec RTCIceServer.url to RTCIceServer.urls
3568 var OrigPeerConnection
= window
.RTCPeerConnection
;
3569 window
.RTCPeerConnection = function(pcConfig
, pcConstraints
) {
3570 if (pcConfig
&& pcConfig
.iceServers
) {
3571 var newIceServers
= [];
3572 for (var i
= 0; i
< pcConfig
.iceServers
.length
; i
++) {
3573 var server
= pcConfig
.iceServers
[i
];
3574 if (!server
.hasOwnProperty('urls') &&
3575 server
.hasOwnProperty('url')) {
3576 utils
.deprecated('RTCIceServer.url', 'RTCIceServer.urls');
3577 server
= JSON
.parse(JSON
.stringify(server
));
3578 server
.urls
= server
.url
;
3580 newIceServers
.push(server
);
3582 newIceServers
.push(pcConfig
.iceServers
[i
]);
3585 pcConfig
.iceServers
= newIceServers
;
3587 return new OrigPeerConnection(pcConfig
, pcConstraints
);
3589 window
.RTCPeerConnection
.prototype = OrigPeerConnection
.prototype;
3590 // wrap static methods. Currently just generateCertificate.
3591 Object
.defineProperty(window
.RTCPeerConnection
, 'generateCertificate', {
3593 return OrigPeerConnection
.generateCertificate
;
3599 // Expose public methods.
3601 shimCallbacksAPI
: safariShim
.shimCallbacksAPI
,
3602 shimLocalStreamsAPI
: safariShim
.shimLocalStreamsAPI
,
3603 shimRemoteStreamsAPI
: safariShim
.shimRemoteStreamsAPI
,
3604 shimGetUserMedia
: safariShim
.shimGetUserMedia
,
3605 shimRTCIceServerUrls
: safariShim
.shimRTCIceServerUrls
3607 // shimPeerConnection: safariShim.shimPeerConnection
3610 },{"../utils":12}],12:[function(require
,module
,exports
){
3612 * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
3614 * Use of this source code is governed by a BSD-style license
3615 * that can be found in the LICENSE file in the root of the source
3618 /* eslint-env node */
3621 var logDisabled_
= true;
3622 var deprecationWarnings_
= true;
3626 disableLog: function(bool
) {
3627 if (typeof bool
!== 'boolean') {
3628 return new Error('Argument type: ' + typeof bool
+
3629 '. Please use a boolean.');
3631 logDisabled_
= bool
;
3632 return (bool
) ? 'adapter.js logging disabled' :
3633 'adapter.js logging enabled';
3637 * Disable or enable deprecation warnings
3638 * @param {!boolean} bool set to true to disable warnings.
3640 disableWarnings: function(bool
) {
3641 if (typeof bool
!== 'boolean') {
3642 return new Error('Argument type: ' + typeof bool
+
3643 '. Please use a boolean.');
3645 deprecationWarnings_
= !bool
;
3646 return 'adapter.js deprecation warnings ' + (bool
? 'disabled' : 'enabled');
3650 if (typeof window
=== 'object') {
3654 if (typeof console
!== 'undefined' && typeof console
.log
=== 'function') {
3655 console
.log
.apply(console
, arguments
);
3661 * Shows a deprecation warning suggesting the modern and spec-compatible API.
3663 deprecated: function(oldMethod
, newMethod
) {
3664 if (!deprecationWarnings_
) {
3667 console
.warn(oldMethod
+ ' is deprecated, please use ' + newMethod
+
3672 * Extract browser version out of the provided user agent string.
3674 * @param {!string} uastring userAgent string.
3675 * @param {!string} expr Regular expression used as match criteria.
3676 * @param {!number} pos position in the version string to be returned.
3677 * @return {!number} browser version.
3679 extractVersion: function(uastring
, expr
, pos
) {
3680 var match
= uastring
.match(expr
);
3681 return match
&& match
.length
>= pos
&& parseInt(match
[pos
], 10);
3687 * @return {object} result containing browser and version
3690 detectBrowser: function(window
) {
3691 var navigator
= window
&& window
.navigator
;
3693 // Returned result object.
3695 result
.browser
= null;
3696 result
.version
= null;
3698 // Fail early if it's not a browser
3699 if (typeof window
=== 'undefined' || !window
.navigator
) {
3700 result
.browser
= 'Not a browser.';
3705 if (navigator
.mozGetUserMedia
) {
3706 result
.browser
= 'firefox';
3707 result
.version
= this.extractVersion(navigator
.userAgent
,
3708 /Firefox\/(\d+)\./, 1);
3709 } else if (navigator
.webkitGetUserMedia
) {
3710 // Chrome, Chromium, Webview, Opera, all use the chrome shim for now
3711 if (window
.webkitRTCPeerConnection
) {
3712 result
.browser
= 'chrome';
3713 result
.version
= this.extractVersion(navigator
.userAgent
,
3714 /Chrom(e|ium)\/(\d+)\./, 2);
3715 } else { // Safari (in an unpublished version) or unknown webkit-based.
3716 if (navigator
.userAgent
.match(/Version\/(\d+).(\d+)/)) {
3717 result
.browser
= 'safari';
3718 result
.version
= this.extractVersion(navigator
.userAgent
,
3719 /AppleWebKit\/(\d+)\./, 1);
3720 } else { // unknown webkit-based browser.
3721 result
.browser
= 'Unsupported webkit-based browser ' +
3722 'with GUM support but no WebRTC support.';
3726 } else if (navigator
.mediaDevices
&&
3727 navigator
.userAgent
.match(/Edge\/(\d+).(\d+)$/)) { // Edge.
3728 result
.browser
= 'edge';
3729 result
.version
= this.extractVersion(navigator
.userAgent
,
3730 /Edge\/(\d+).(\d+)$/, 2);
3731 } else if (navigator
.mediaDevices
&&
3732 navigator
.userAgent
.match(/AppleWebKit\/(\d+)\./)) {
3733 // Safari, with webkitGetUserMedia removed.
3734 result
.browser
= 'safari';
3735 result
.version
= this.extractVersion(navigator
.userAgent
,
3736 /AppleWebKit\/(\d+)\./, 1);
3737 } else { // Default fallthrough: not supported.
3738 result
.browser
= 'Not a supported browser.';
3745 // shimCreateObjectURL must be called before shimSourceObject to avoid loop.
3747 shimCreateObjectURL: function(window
) {
3748 var URL
= window
&& window
.URL
;
3750 if (!(typeof window
=== 'object' && window
.HTMLMediaElement
&&
3751 'srcObject' in window
.HTMLMediaElement
.prototype)) {
3752 // Only shim CreateObjectURL using srcObject if srcObject exists.
3756 var nativeCreateObjectURL
= URL
.createObjectURL
.bind(URL
);
3757 var nativeRevokeObjectURL
= URL
.revokeObjectURL
.bind(URL
);
3758 var streams
= new Map(), newId
= 0;
3760 URL
.createObjectURL = function(stream
) {
3761 if ('getTracks' in stream
) {
3762 var url
= 'polyblob:' + (++newId
);
3763 streams
.set(url
, stream
);
3764 utils
.deprecated('URL.createObjectURL(stream)',
3765 'elem.srcObject = stream');
3768 return nativeCreateObjectURL(stream
);
3770 URL
.revokeObjectURL = function(url
) {
3771 nativeRevokeObjectURL(url
);
3772 streams
.delete(url
);
3775 var dsc
= Object
.getOwnPropertyDescriptor(window
.HTMLMediaElement
.prototype,
3777 Object
.defineProperty(window
.HTMLMediaElement
.prototype, 'src', {
3779 return dsc
.get.apply(this);
3781 set: function(url
) {
3782 this.srcObject
= streams
.get(url
) || null;
3783 return dsc
.set.apply(this, [url
]);
3787 var nativeSetAttribute
= window
.HTMLMediaElement
.prototype.setAttribute
;
3788 window
.HTMLMediaElement
.prototype.setAttribute = function() {
3789 if (arguments
.length
=== 2 &&
3790 ('' + arguments
[0]).toLowerCase() === 'src') {
3791 this.srcObject
= streams
.get(arguments
[1]) || null;
3793 return nativeSetAttribute
.apply(this, arguments
);
3801 deprecated
: utils
.deprecated
,
3802 disableLog
: utils
.disableLog
,
3803 disableWarnings
: utils
.disableWarnings
,
3804 extractVersion
: utils
.extractVersion
,
3805 shimCreateObjectURL
: utils
.shimCreateObjectURL
,
3806 detectBrowser
: utils
.detectBrowser
.bind(utils
)