How to use iceTransport method in wpt

Best JavaScript code snippet using wpt

rtcpeerconnection.js

Source:rtcpeerconnection.js Github

copy

Full Screen

1/*2 * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.3 *4 * Use of this source code is governed by a BSD-style license5 * that can be found in the LICENSE file in the root of the source6 * tree.7 */8 /* eslint-env node */9'use strict';10var SDPUtils = require('sdp');11function fixStatsType(stat) {12 return {13 inboundrtp: 'inbound-rtp',14 outboundrtp: 'outbound-rtp',15 candidatepair: 'candidate-pair',16 localcandidate: 'local-candidate',17 remotecandidate: 'remote-candidate'18 }[stat.type] || stat.type;19}20function writeMediaSection(transceiver, caps, type, stream, dtlsRole) {21 var sdp = SDPUtils.writeRtpDescription(transceiver.kind, caps);22 // Map ICE parameters (ufrag, pwd) to SDP.23 sdp += SDPUtils.writeIceParameters(24 transceiver.iceGatherer.getLocalParameters());25 // Map DTLS parameters to SDP.26 sdp += SDPUtils.writeDtlsParameters(27 transceiver.dtlsTransport.getLocalParameters(),28 type === 'offer' ? 'actpass' : dtlsRole || 'active');29 sdp += 'a=mid:' + transceiver.mid + '\r\n';30 if (transceiver.rtpSender && transceiver.rtpReceiver) {31 sdp += 'a=sendrecv\r\n';32 } else if (transceiver.rtpSender) {33 sdp += 'a=sendonly\r\n';34 } else if (transceiver.rtpReceiver) {35 sdp += 'a=recvonly\r\n';36 } else {37 sdp += 'a=inactive\r\n';38 }39 if (transceiver.rtpSender) {40 var trackId = transceiver.rtpSender._initialTrackId ||41 transceiver.rtpSender.track.id;42 transceiver.rtpSender._initialTrackId = trackId;43 // spec.44 var msid = 'msid:' + (stream ? stream.id : '-') + ' ' +45 trackId + '\r\n';46 sdp += 'a=' + msid;47 // for Chrome. Legacy should no longer be required.48 sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].ssrc +49 ' ' + msid;50 // RTX51 if (transceiver.sendEncodingParameters[0].rtx) {52 sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].rtx.ssrc +53 ' ' + msid;54 sdp += 'a=ssrc-group:FID ' +55 transceiver.sendEncodingParameters[0].ssrc + ' ' +56 transceiver.sendEncodingParameters[0].rtx.ssrc +57 '\r\n';58 }59 }60 // FIXME: this should be written by writeRtpDescription.61 sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].ssrc +62 ' cname:' + SDPUtils.localCName + '\r\n';63 if (transceiver.rtpSender && transceiver.sendEncodingParameters[0].rtx) {64 sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].rtx.ssrc +65 ' cname:' + SDPUtils.localCName + '\r\n';66 }67 return sdp;68}69// Edge does not like70// 1) stun: filtered after 14393 unless ?transport=udp is present71// 2) turn: that does not have all of turn:host:port?transport=udp72// 3) turn: with ipv6 addresses73// 4) turn: occurring muliple times74function filterIceServers(iceServers, edgeVersion) {75 var hasTurn = false;76 iceServers = JSON.parse(JSON.stringify(iceServers));77 return iceServers.filter(function(server) {78 if (server && (server.urls || server.url)) {79 var urls = server.urls || server.url;80 if (server.url && !server.urls) {81 console.warn('RTCIceServer.url is deprecated! Use urls instead.');82 }83 var isString = typeof urls === 'string';84 if (isString) {85 urls = [urls];86 }87 urls = urls.filter(function(url) {88 var validTurn = url.indexOf('turn:') === 0 &&89 url.indexOf('transport=udp') !== -1 &&90 url.indexOf('turn:[') === -1 &&91 !hasTurn;92 if (validTurn) {93 hasTurn = true;94 return true;95 }96 return url.indexOf('stun:') === 0 && edgeVersion >= 14393 &&97 url.indexOf('?transport=udp') === -1;98 });99 delete server.url;100 server.urls = isString ? urls[0] : urls;101 return !!urls.length;102 }103 });104}105// Determines the intersection of local and remote capabilities.106function getCommonCapabilities(localCapabilities, remoteCapabilities) {107 var commonCapabilities = {108 codecs: [],109 headerExtensions: [],110 fecMechanisms: []111 };112 var findCodecByPayloadType = function(pt, codecs) {113 pt = parseInt(pt, 10);114 for (var i = 0; i < codecs.length; i++) {115 if (codecs[i].payloadType === pt ||116 codecs[i].preferredPayloadType === pt) {117 return codecs[i];118 }119 }120 };121 var rtxCapabilityMatches = function(lRtx, rRtx, lCodecs, rCodecs) {122 var lCodec = findCodecByPayloadType(lRtx.parameters.apt, lCodecs);123 var rCodec = findCodecByPayloadType(rRtx.parameters.apt, rCodecs);124 return lCodec && rCodec &&125 lCodec.name.toLowerCase() === rCodec.name.toLowerCase();126 };127 localCapabilities.codecs.forEach(function(lCodec) {128 for (var i = 0; i < remoteCapabilities.codecs.length; i++) {129 var rCodec = remoteCapabilities.codecs[i];130 if (lCodec.name.toLowerCase() === rCodec.name.toLowerCase() &&131 lCodec.clockRate === rCodec.clockRate) {132 if (lCodec.name.toLowerCase() === 'rtx' &&133 lCodec.parameters && rCodec.parameters.apt) {134 // for RTX we need to find the local rtx that has a apt135 // which points to the same local codec as the remote one.136 if (!rtxCapabilityMatches(lCodec, rCodec,137 localCapabilities.codecs, remoteCapabilities.codecs)) {138 continue;139 }140 }141 rCodec = JSON.parse(JSON.stringify(rCodec)); // deepcopy142 // number of channels is the highest common number of channels143 rCodec.numChannels = Math.min(lCodec.numChannels,144 rCodec.numChannels);145 // push rCodec so we reply with offerer payload type146 commonCapabilities.codecs.push(rCodec);147 // determine common feedback mechanisms148 rCodec.rtcpFeedback = rCodec.rtcpFeedback.filter(function(fb) {149 for (var j = 0; j < lCodec.rtcpFeedback.length; j++) {150 if (lCodec.rtcpFeedback[j].type === fb.type &&151 lCodec.rtcpFeedback[j].parameter === fb.parameter) {152 return true;153 }154 }155 return false;156 });157 // FIXME: also need to determine .parameters158 // see https://github.com/openpeer/ortc/issues/569159 break;160 }161 }162 });163 localCapabilities.headerExtensions.forEach(function(lHeaderExtension) {164 for (var i = 0; i < remoteCapabilities.headerExtensions.length;165 i++) {166 var rHeaderExtension = remoteCapabilities.headerExtensions[i];167 if (lHeaderExtension.uri === rHeaderExtension.uri) {168 commonCapabilities.headerExtensions.push(rHeaderExtension);169 break;170 }171 }172 });173 // FIXME: fecMechanisms174 return commonCapabilities;175}176// is action=setLocalDescription with type allowed in signalingState177function isActionAllowedInSignalingState(action, type, signalingState) {178 return {179 offer: {180 setLocalDescription: ['stable', 'have-local-offer'],181 setRemoteDescription: ['stable', 'have-remote-offer']182 },183 answer: {184 setLocalDescription: ['have-remote-offer', 'have-local-pranswer'],185 setRemoteDescription: ['have-local-offer', 'have-remote-pranswer']186 }187 }[type][action].indexOf(signalingState) !== -1;188}189function maybeAddCandidate(iceTransport, candidate) {190 // Edge's internal representation adds some fields therefore191 // not all fieldѕ are taken into account.192 var alreadyAdded = iceTransport.getRemoteCandidates()193 .find(function(remoteCandidate) {194 return candidate.foundation === remoteCandidate.foundation &&195 candidate.ip === remoteCandidate.ip &&196 candidate.port === remoteCandidate.port &&197 candidate.priority === remoteCandidate.priority &&198 candidate.protocol === remoteCandidate.protocol &&199 candidate.type === remoteCandidate.type;200 });201 if (!alreadyAdded) {202 iceTransport.addRemoteCandidate(candidate);203 }204 return !alreadyAdded;205}206function makeError(name, description) {207 var e = new Error(description);208 e.name = name;209 // legacy error codes from https://heycam.github.io/webidl/#idl-DOMException-error-names210 e.code = {211 NotSupportedError: 9,212 InvalidStateError: 11,213 InvalidAccessError: 15,214 TypeError: undefined,215 OperationError: undefined216 }[name];217 return e;218}219module.exports = function(window, edgeVersion) {220 // https://w3c.github.io/mediacapture-main/#mediastream221 // Helper function to add the track to the stream and222 // dispatch the event ourselves.223 function addTrackToStreamAndFireEvent(track, stream) {224 stream.addTrack(track);225 stream.dispatchEvent(new window.MediaStreamTrackEvent('addtrack',226 {track: track}));227 }228 function removeTrackFromStreamAndFireEvent(track, stream) {229 stream.removeTrack(track);230 stream.dispatchEvent(new window.MediaStreamTrackEvent('removetrack',231 {track: track}));232 }233 function fireAddTrack(pc, track, receiver, streams) {234 var trackEvent = new Event('track');235 trackEvent.track = track;236 trackEvent.receiver = receiver;237 trackEvent.transceiver = {receiver: receiver};238 trackEvent.streams = streams;239 window.setTimeout(function() {240 pc._dispatchEvent('track', trackEvent);241 });242 }243 var RTCPeerConnection = function(config) {244 var pc = this;245 var _eventTarget = document.createDocumentFragment();246 ['addEventListener', 'removeEventListener', 'dispatchEvent']247 .forEach(function(method) {248 pc[method] = _eventTarget[method].bind(_eventTarget);249 });250 this.canTrickleIceCandidates = null;251 this.needNegotiation = false;252 this.localStreams = [];253 this.remoteStreams = [];254 this._localDescription = null;255 this._remoteDescription = null;256 this.signalingState = 'stable';257 this.iceConnectionState = 'new';258 this.connectionState = 'new';259 this.iceGatheringState = 'new';260 config = JSON.parse(JSON.stringify(config || {}));261 this.usingBundle = config.bundlePolicy === 'max-bundle';262 if (config.rtcpMuxPolicy === 'negotiate') {263 throw(makeError('NotSupportedError',264 'rtcpMuxPolicy \'negotiate\' is not supported'));265 } else if (!config.rtcpMuxPolicy) {266 config.rtcpMuxPolicy = 'require';267 }268 switch (config.iceTransportPolicy) {269 case 'all':270 case 'relay':271 break;272 default:273 config.iceTransportPolicy = 'all';274 break;275 }276 switch (config.bundlePolicy) {277 case 'balanced':278 case 'max-compat':279 case 'max-bundle':280 break;281 default:282 config.bundlePolicy = 'balanced';283 break;284 }285 config.iceServers = filterIceServers(config.iceServers || [], edgeVersion);286 this._iceGatherers = [];287 if (config.iceCandidatePoolSize) {288 for (var i = config.iceCandidatePoolSize; i > 0; i--) {289 this._iceGatherers.push(new window.RTCIceGatherer({290 iceServers: config.iceServers,291 gatherPolicy: config.iceTransportPolicy292 }));293 }294 } else {295 config.iceCandidatePoolSize = 0;296 }297 this._config = config;298 // per-track iceGathers, iceTransports, dtlsTransports, rtpSenders, ...299 // everything that is needed to describe a SDP m-line.300 this.transceivers = [];301 this._sdpSessionId = SDPUtils.generateSessionId();302 this._sdpSessionVersion = 0;303 this._dtlsRole = undefined; // role for a=setup to use in answers.304 this._isClosed = false;305 };306 Object.defineProperty(RTCPeerConnection.prototype, 'localDescription', {307 configurable: true,308 get: function() {309 return this._localDescription;310 }311 });312 Object.defineProperty(RTCPeerConnection.prototype, 'remoteDescription', {313 configurable: true,314 get: function() {315 return this._remoteDescription;316 }317 });318 // set up event handlers on prototype319 RTCPeerConnection.prototype.onicecandidate = null;320 RTCPeerConnection.prototype.onaddstream = null;321 RTCPeerConnection.prototype.ontrack = null;322 RTCPeerConnection.prototype.onremovestream = null;323 RTCPeerConnection.prototype.onsignalingstatechange = null;324 RTCPeerConnection.prototype.oniceconnectionstatechange = null;325 RTCPeerConnection.prototype.onconnectionstatechange = null;326 RTCPeerConnection.prototype.onicegatheringstatechange = null;327 RTCPeerConnection.prototype.onnegotiationneeded = null;328 RTCPeerConnection.prototype.ondatachannel = null;329 RTCPeerConnection.prototype._dispatchEvent = function(name, event) {330 if (this._isClosed) {331 return;332 }333 this.dispatchEvent(event);334 if (typeof this['on' + name] === 'function') {335 this['on' + name](event);336 }337 };338 RTCPeerConnection.prototype._emitGatheringStateChange = function() {339 var event = new Event('icegatheringstatechange');340 this._dispatchEvent('icegatheringstatechange', event);341 };342 RTCPeerConnection.prototype.getConfiguration = function() {343 return this._config;344 };345 RTCPeerConnection.prototype.getLocalStreams = function() {346 return this.localStreams;347 };348 RTCPeerConnection.prototype.getRemoteStreams = function() {349 return this.remoteStreams;350 };351 // internal helper to create a transceiver object.352 // (which is not yet the same as the WebRTC 1.0 transceiver)353 RTCPeerConnection.prototype._createTransceiver = function(kind, doNotAdd) {354 var hasBundleTransport = this.transceivers.length > 0;355 var transceiver = {356 track: null,357 iceGatherer: null,358 iceTransport: null,359 dtlsTransport: null,360 localCapabilities: null,361 remoteCapabilities: null,362 rtpSender: null,363 rtpReceiver: null,364 kind: kind,365 mid: null,366 sendEncodingParameters: null,367 recvEncodingParameters: null,368 stream: null,369 associatedRemoteMediaStreams: [],370 wantReceive: true371 };372 if (this.usingBundle && hasBundleTransport) {373 transceiver.iceTransport = this.transceivers[0].iceTransport;374 transceiver.dtlsTransport = this.transceivers[0].dtlsTransport;375 } else {376 var transports = this._createIceAndDtlsTransports();377 transceiver.iceTransport = transports.iceTransport;378 transceiver.dtlsTransport = transports.dtlsTransport;379 }380 if (!doNotAdd) {381 this.transceivers.push(transceiver);382 }383 return transceiver;384 };385 RTCPeerConnection.prototype.addTrack = function(track, stream) {386 if (this._isClosed) {387 throw makeError('InvalidStateError',388 'Attempted to call addTrack on a closed peerconnection.');389 }390 var alreadyExists = this.transceivers.find(function(s) {391 return s.track === track;392 });393 if (alreadyExists) {394 throw makeError('InvalidAccessError', 'Track already exists.');395 }396 var transceiver;397 for (var i = 0; i < this.transceivers.length; i++) {398 if (!this.transceivers[i].track &&399 this.transceivers[i].kind === track.kind) {400 transceiver = this.transceivers[i];401 }402 }403 if (!transceiver) {404 transceiver = this._createTransceiver(track.kind);405 }406 this._maybeFireNegotiationNeeded();407 if (this.localStreams.indexOf(stream) === -1) {408 this.localStreams.push(stream);409 }410 transceiver.track = track;411 transceiver.stream = stream;412 transceiver.rtpSender = new window.RTCRtpSender(track,413 transceiver.dtlsTransport);414 return transceiver.rtpSender;415 };416 RTCPeerConnection.prototype.addStream = function(stream) {417 var pc = this;418 if (edgeVersion >= 15025) {419 stream.getTracks().forEach(function(track) {420 pc.addTrack(track, stream);421 });422 } else {423 // Clone is necessary for local demos mostly, attaching directly424 // to two different senders does not work (build 10547).425 // Fixed in 15025 (or earlier)426 var clonedStream = stream.clone();427 stream.getTracks().forEach(function(track, idx) {428 var clonedTrack = clonedStream.getTracks()[idx];429 track.addEventListener('enabled', function(event) {430 clonedTrack.enabled = event.enabled;431 });432 });433 clonedStream.getTracks().forEach(function(track) {434 pc.addTrack(track, clonedStream);435 });436 }437 };438 RTCPeerConnection.prototype.removeTrack = function(sender) {439 if (this._isClosed) {440 throw makeError('InvalidStateError',441 'Attempted to call removeTrack on a closed peerconnection.');442 }443 if (!(sender instanceof window.RTCRtpSender)) {444 throw new TypeError('Argument 1 of RTCPeerConnection.removeTrack ' +445 'does not implement interface RTCRtpSender.');446 }447 var transceiver = this.transceivers.find(function(t) {448 return t.rtpSender === sender;449 });450 if (!transceiver) {451 throw makeError('InvalidAccessError',452 'Sender was not created by this connection.');453 }454 var stream = transceiver.stream;455 transceiver.rtpSender.stop();456 transceiver.rtpSender = null;457 transceiver.track = null;458 transceiver.stream = null;459 // remove the stream from the set of local streams460 var localStreams = this.transceivers.map(function(t) {461 return t.stream;462 });463 if (localStreams.indexOf(stream) === -1 &&464 this.localStreams.indexOf(stream) > -1) {465 this.localStreams.splice(this.localStreams.indexOf(stream), 1);466 }467 this._maybeFireNegotiationNeeded();468 };469 RTCPeerConnection.prototype.removeStream = function(stream) {470 var pc = this;471 stream.getTracks().forEach(function(track) {472 var sender = pc.getSenders().find(function(s) {473 return s.track === track;474 });475 if (sender) {476 pc.removeTrack(sender);477 }478 });479 };480 RTCPeerConnection.prototype.getSenders = function() {481 return this.transceivers.filter(function(transceiver) {482 return !!transceiver.rtpSender;483 })484 .map(function(transceiver) {485 return transceiver.rtpSender;486 });487 };488 RTCPeerConnection.prototype.getReceivers = function() {489 return this.transceivers.filter(function(transceiver) {490 return !!transceiver.rtpReceiver;491 })492 .map(function(transceiver) {493 return transceiver.rtpReceiver;494 });495 };496 RTCPeerConnection.prototype._createIceGatherer = function(sdpMLineIndex,497 usingBundle) {498 var pc = this;499 if (usingBundle && sdpMLineIndex > 0) {500 return this.transceivers[0].iceGatherer;501 } else if (this._iceGatherers.length) {502 return this._iceGatherers.shift();503 }504 var iceGatherer = new window.RTCIceGatherer({505 iceServers: this._config.iceServers,506 gatherPolicy: this._config.iceTransportPolicy507 });508 Object.defineProperty(iceGatherer, 'state',509 {value: 'new', writable: true}510 );511 this.transceivers[sdpMLineIndex].bufferedCandidateEvents = [];512 this.transceivers[sdpMLineIndex].bufferCandidates = function(event) {513 var end = !event.candidate || Object.keys(event.candidate).length === 0;514 // polyfill since RTCIceGatherer.state is not implemented in515 // Edge 10547 yet.516 iceGatherer.state = end ? 'completed' : 'gathering';517 if (pc.transceivers[sdpMLineIndex].bufferedCandidateEvents !== null) {518 pc.transceivers[sdpMLineIndex].bufferedCandidateEvents.push(event);519 }520 };521 iceGatherer.addEventListener('localcandidate',522 this.transceivers[sdpMLineIndex].bufferCandidates);523 return iceGatherer;524 };525 // start gathering from an RTCIceGatherer.526 RTCPeerConnection.prototype._gather = function(mid, sdpMLineIndex) {527 var pc = this;528 var iceGatherer = this.transceivers[sdpMLineIndex].iceGatherer;529 if (iceGatherer.onlocalcandidate) {530 return;531 }532 var bufferedCandidateEvents =533 this.transceivers[sdpMLineIndex].bufferedCandidateEvents;534 this.transceivers[sdpMLineIndex].bufferedCandidateEvents = null;535 iceGatherer.removeEventListener('localcandidate',536 this.transceivers[sdpMLineIndex].bufferCandidates);537 iceGatherer.onlocalcandidate = function(evt) {538 if (pc.usingBundle && sdpMLineIndex > 0) {539 // if we know that we use bundle we can drop candidates with540 // ѕdpMLineIndex > 0. If we don't do this then our state gets541 // confused since we dispose the extra ice gatherer.542 return;543 }544 var event = new Event('icecandidate');545 event.candidate = {sdpMid: mid, sdpMLineIndex: sdpMLineIndex};546 var cand = evt.candidate;547 // Edge emits an empty object for RTCIceCandidateComplete‥548 var end = !cand || Object.keys(cand).length === 0;549 if (end) {550 // polyfill since RTCIceGatherer.state is not implemented in551 // Edge 10547 yet.552 if (iceGatherer.state === 'new' || iceGatherer.state === 'gathering') {553 iceGatherer.state = 'completed';554 }555 } else {556 if (iceGatherer.state === 'new') {557 iceGatherer.state = 'gathering';558 }559 // RTCIceCandidate doesn't have a component, needs to be added560 cand.component = 1;561 // also the usernameFragment. TODO: update SDP to take both variants.562 cand.ufrag = iceGatherer.getLocalParameters().usernameFragment;563 var serializedCandidate = SDPUtils.writeCandidate(cand);564 event.candidate = Object.assign(event.candidate,565 SDPUtils.parseCandidate(serializedCandidate));566 event.candidate.candidate = serializedCandidate;567 event.candidate.toJSON = function() {568 return {569 candidate: event.candidate.candidate,570 sdpMid: event.candidate.sdpMid,571 sdpMLineIndex: event.candidate.sdpMLineIndex,572 usernameFragment: event.candidate.usernameFragment573 };574 };575 }576 // update local description.577 var sections = SDPUtils.getMediaSections(pc._localDescription.sdp);578 if (!end) {579 sections[event.candidate.sdpMLineIndex] +=580 'a=' + event.candidate.candidate + '\r\n';581 } else {582 sections[event.candidate.sdpMLineIndex] +=583 'a=end-of-candidates\r\n';584 }585 pc._localDescription.sdp =586 SDPUtils.getDescription(pc._localDescription.sdp) +587 sections.join('');588 var complete = pc.transceivers.every(function(transceiver) {589 return transceiver.iceGatherer &&590 transceiver.iceGatherer.state === 'completed';591 });592 if (pc.iceGatheringState !== 'gathering') {593 pc.iceGatheringState = 'gathering';594 pc._emitGatheringStateChange();595 }596 // Emit candidate. Also emit null candidate when all gatherers are597 // complete.598 if (!end) {599 pc._dispatchEvent('icecandidate', event);600 }601 if (complete) {602 pc._dispatchEvent('icecandidate', new Event('icecandidate'));603 pc.iceGatheringState = 'complete';604 pc._emitGatheringStateChange();605 }606 };607 // emit already gathered candidates.608 window.setTimeout(function() {609 bufferedCandidateEvents.forEach(function(e) {610 iceGatherer.onlocalcandidate(e);611 });612 }, 0);613 };614 // Create ICE transport and DTLS transport.615 RTCPeerConnection.prototype._createIceAndDtlsTransports = function() {616 var pc = this;617 var iceTransport = new window.RTCIceTransport(null);618 iceTransport.onicestatechange = function() {619 pc._updateIceConnectionState();620 pc._updateConnectionState();621 };622 var dtlsTransport = new window.RTCDtlsTransport(iceTransport);623 dtlsTransport.ondtlsstatechange = function() {624 pc._updateConnectionState();625 };626 dtlsTransport.onerror = function() {627 // onerror does not set state to failed by itself.628 Object.defineProperty(dtlsTransport, 'state',629 {value: 'failed', writable: true});630 pc._updateConnectionState();631 };632 return {633 iceTransport: iceTransport,634 dtlsTransport: dtlsTransport635 };636 };637 // Destroy ICE gatherer, ICE transport and DTLS transport.638 // Without triggering the callbacks.639 RTCPeerConnection.prototype._disposeIceAndDtlsTransports = function(640 sdpMLineIndex) {641 var iceGatherer = this.transceivers[sdpMLineIndex].iceGatherer;642 if (iceGatherer) {643 delete iceGatherer.onlocalcandidate;644 delete this.transceivers[sdpMLineIndex].iceGatherer;645 }646 var iceTransport = this.transceivers[sdpMLineIndex].iceTransport;647 if (iceTransport) {648 delete iceTransport.onicestatechange;649 delete this.transceivers[sdpMLineIndex].iceTransport;650 }651 var dtlsTransport = this.transceivers[sdpMLineIndex].dtlsTransport;652 if (dtlsTransport) {653 delete dtlsTransport.ondtlsstatechange;654 delete dtlsTransport.onerror;655 delete this.transceivers[sdpMLineIndex].dtlsTransport;656 }657 };658 // Start the RTP Sender and Receiver for a transceiver.659 RTCPeerConnection.prototype._transceive = function(transceiver,660 send, recv) {661 var params = getCommonCapabilities(transceiver.localCapabilities,662 transceiver.remoteCapabilities);663 if (send && transceiver.rtpSender) {664 params.encodings = transceiver.sendEncodingParameters;665 params.rtcp = {666 cname: SDPUtils.localCName,667 compound: transceiver.rtcpParameters.compound668 };669 if (transceiver.recvEncodingParameters.length) {670 params.rtcp.ssrc = transceiver.recvEncodingParameters[0].ssrc;671 }672 transceiver.rtpSender.send(params);673 }674 if (recv && transceiver.rtpReceiver && params.codecs.length > 0) {675 // remove RTX field in Edge 14942676 if (transceiver.kind === 'video'677 && transceiver.recvEncodingParameters678 && edgeVersion < 15019) {679 transceiver.recvEncodingParameters.forEach(function(p) {680 delete p.rtx;681 });682 }683 if (transceiver.recvEncodingParameters.length) {684 params.encodings = transceiver.recvEncodingParameters;685 } else {686 params.encodings = [{}];687 }688 params.rtcp = {689 compound: transceiver.rtcpParameters.compound690 };691 if (transceiver.rtcpParameters.cname) {692 params.rtcp.cname = transceiver.rtcpParameters.cname;693 }694 if (transceiver.sendEncodingParameters.length) {695 params.rtcp.ssrc = transceiver.sendEncodingParameters[0].ssrc;696 }697 transceiver.rtpReceiver.receive(params);698 }699 };700 RTCPeerConnection.prototype.setLocalDescription = function(description) {701 var pc = this;702 // Note: pranswer is not supported.703 if (['offer', 'answer'].indexOf(description.type) === -1) {704 return Promise.reject(makeError('TypeError',705 'Unsupported type "' + description.type + '"'));706 }707 if (!isActionAllowedInSignalingState('setLocalDescription',708 description.type, pc.signalingState) || pc._isClosed) {709 return Promise.reject(makeError('InvalidStateError',710 'Can not set local ' + description.type +711 ' in state ' + pc.signalingState));712 }713 var sections;714 var sessionpart;715 if (description.type === 'offer') {716 // VERY limited support for SDP munging. Limited to:717 // * changing the order of codecs718 sections = SDPUtils.splitSections(description.sdp);719 sessionpart = sections.shift();720 sections.forEach(function(mediaSection, sdpMLineIndex) {721 var caps = SDPUtils.parseRtpParameters(mediaSection);722 pc.transceivers[sdpMLineIndex].localCapabilities = caps;723 });724 pc.transceivers.forEach(function(transceiver, sdpMLineIndex) {725 pc._gather(transceiver.mid, sdpMLineIndex);726 });727 } else if (description.type === 'answer') {728 sections = SDPUtils.splitSections(pc._remoteDescription.sdp);729 sessionpart = sections.shift();730 var isIceLite = SDPUtils.matchPrefix(sessionpart,731 'a=ice-lite').length > 0;732 sections.forEach(function(mediaSection, sdpMLineIndex) {733 var transceiver = pc.transceivers[sdpMLineIndex];734 var iceGatherer = transceiver.iceGatherer;735 var iceTransport = transceiver.iceTransport;736 var dtlsTransport = transceiver.dtlsTransport;737 var localCapabilities = transceiver.localCapabilities;738 var remoteCapabilities = transceiver.remoteCapabilities;739 // treat bundle-only as not-rejected.740 var rejected = SDPUtils.isRejected(mediaSection) &&741 SDPUtils.matchPrefix(mediaSection, 'a=bundle-only').length === 0;742 if (!rejected && !transceiver.rejected) {743 var remoteIceParameters = SDPUtils.getIceParameters(744 mediaSection, sessionpart);745 var remoteDtlsParameters = SDPUtils.getDtlsParameters(746 mediaSection, sessionpart);747 if (isIceLite) {748 remoteDtlsParameters.role = 'server';749 }750 if (!pc.usingBundle || sdpMLineIndex === 0) {751 pc._gather(transceiver.mid, sdpMLineIndex);752 if (iceTransport.state === 'new') {753 iceTransport.start(iceGatherer, remoteIceParameters,754 isIceLite ? 'controlling' : 'controlled');755 }756 if (dtlsTransport.state === 'new') {757 dtlsTransport.start(remoteDtlsParameters);758 }759 }760 // Calculate intersection of capabilities.761 var params = getCommonCapabilities(localCapabilities,762 remoteCapabilities);763 // Start the RTCRtpSender. The RTCRtpReceiver for this764 // transceiver has already been started in setRemoteDescription.765 pc._transceive(transceiver,766 params.codecs.length > 0,767 false);768 }769 });770 }771 pc._localDescription = {772 type: description.type,773 sdp: description.sdp774 };775 if (description.type === 'offer') {776 pc._updateSignalingState('have-local-offer');777 } else {778 pc._updateSignalingState('stable');779 }780 return Promise.resolve();781 };782 RTCPeerConnection.prototype.setRemoteDescription = function(description) {783 var pc = this;784 // Note: pranswer is not supported.785 if (['offer', 'answer'].indexOf(description.type) === -1) {786 return Promise.reject(makeError('TypeError',787 'Unsupported type "' + description.type + '"'));788 }789 if (!isActionAllowedInSignalingState('setRemoteDescription',790 description.type, pc.signalingState) || pc._isClosed) {791 return Promise.reject(makeError('InvalidStateError',792 'Can not set remote ' + description.type +793 ' in state ' + pc.signalingState));794 }795 var streams = {};796 pc.remoteStreams.forEach(function(stream) {797 streams[stream.id] = stream;798 });799 var receiverList = [];800 var sections = SDPUtils.splitSections(description.sdp);801 var sessionpart = sections.shift();802 var isIceLite = SDPUtils.matchPrefix(sessionpart,803 'a=ice-lite').length > 0;804 var usingBundle = SDPUtils.matchPrefix(sessionpart,805 'a=group:BUNDLE ').length > 0;806 pc.usingBundle = usingBundle;807 var iceOptions = SDPUtils.matchPrefix(sessionpart,808 'a=ice-options:')[0];809 if (iceOptions) {810 pc.canTrickleIceCandidates = iceOptions.substr(14).split(' ')811 .indexOf('trickle') >= 0;812 } else {813 pc.canTrickleIceCandidates = false;814 }815 sections.forEach(function(mediaSection, sdpMLineIndex) {816 var lines = SDPUtils.splitLines(mediaSection);817 var kind = SDPUtils.getKind(mediaSection);818 // treat bundle-only as not-rejected.819 var rejected = SDPUtils.isRejected(mediaSection) &&820 SDPUtils.matchPrefix(mediaSection, 'a=bundle-only').length === 0;821 var protocol = lines[0].substr(2).split(' ')[2];822 var direction = SDPUtils.getDirection(mediaSection, sessionpart);823 var remoteMsid = SDPUtils.parseMsid(mediaSection);824 var mid = SDPUtils.getMid(mediaSection) || SDPUtils.generateIdentifier();825 // Reject datachannels which are not implemented yet.826 if (rejected || (kind === 'application' && (protocol === 'DTLS/SCTP' ||827 protocol === 'UDP/DTLS/SCTP'))) {828 // TODO: this is dangerous in the case where a non-rejected m-line829 // becomes rejected.830 pc.transceivers[sdpMLineIndex] = {831 mid: mid,832 kind: kind,833 protocol: protocol,834 rejected: true835 };836 return;837 }838 if (!rejected && pc.transceivers[sdpMLineIndex] &&839 pc.transceivers[sdpMLineIndex].rejected) {840 // recycle a rejected transceiver.841 pc.transceivers[sdpMLineIndex] = pc._createTransceiver(kind, true);842 }843 var transceiver;844 var iceGatherer;845 var iceTransport;846 var dtlsTransport;847 var rtpReceiver;848 var sendEncodingParameters;849 var recvEncodingParameters;850 var localCapabilities;851 var track;852 // FIXME: ensure the mediaSection has rtcp-mux set.853 var remoteCapabilities = SDPUtils.parseRtpParameters(mediaSection);854 var remoteIceParameters;855 var remoteDtlsParameters;856 if (!rejected) {857 remoteIceParameters = SDPUtils.getIceParameters(mediaSection,858 sessionpart);859 remoteDtlsParameters = SDPUtils.getDtlsParameters(mediaSection,860 sessionpart);861 remoteDtlsParameters.role = 'client';862 }863 recvEncodingParameters =864 SDPUtils.parseRtpEncodingParameters(mediaSection);865 var rtcpParameters = SDPUtils.parseRtcpParameters(mediaSection);866 var isComplete = SDPUtils.matchPrefix(mediaSection,867 'a=end-of-candidates', sessionpart).length > 0;868 var cands = SDPUtils.matchPrefix(mediaSection, 'a=candidate:')869 .map(function(cand) {870 return SDPUtils.parseCandidate(cand);871 })872 .filter(function(cand) {873 return cand.component === 1;874 });875 // Check if we can use BUNDLE and dispose transports.876 if ((description.type === 'offer' || description.type === 'answer') &&877 !rejected && usingBundle && sdpMLineIndex > 0 &&878 pc.transceivers[sdpMLineIndex]) {879 pc._disposeIceAndDtlsTransports(sdpMLineIndex);880 pc.transceivers[sdpMLineIndex].iceGatherer =881 pc.transceivers[0].iceGatherer;882 pc.transceivers[sdpMLineIndex].iceTransport =883 pc.transceivers[0].iceTransport;884 pc.transceivers[sdpMLineIndex].dtlsTransport =885 pc.transceivers[0].dtlsTransport;886 if (pc.transceivers[sdpMLineIndex].rtpSender) {887 pc.transceivers[sdpMLineIndex].rtpSender.setTransport(888 pc.transceivers[0].dtlsTransport);889 }890 if (pc.transceivers[sdpMLineIndex].rtpReceiver) {891 pc.transceivers[sdpMLineIndex].rtpReceiver.setTransport(892 pc.transceivers[0].dtlsTransport);893 }894 }895 if (description.type === 'offer' && !rejected) {896 transceiver = pc.transceivers[sdpMLineIndex] ||897 pc._createTransceiver(kind);898 transceiver.mid = mid;899 if (!transceiver.iceGatherer) {900 transceiver.iceGatherer = pc._createIceGatherer(sdpMLineIndex,901 usingBundle);902 }903 if (cands.length && transceiver.iceTransport.state === 'new') {904 if (isComplete && (!usingBundle || sdpMLineIndex === 0)) {905 transceiver.iceTransport.setRemoteCandidates(cands);906 } else {907 cands.forEach(function(candidate) {908 maybeAddCandidate(transceiver.iceTransport, candidate);909 });910 }911 }912 localCapabilities = window.RTCRtpReceiver.getCapabilities(kind);913 // filter RTX until additional stuff needed for RTX is implemented914 // in adapter.js915 if (edgeVersion < 15019) {916 localCapabilities.codecs = localCapabilities.codecs.filter(917 function(codec) {918 return codec.name !== 'rtx';919 });920 }921 sendEncodingParameters = transceiver.sendEncodingParameters || [{922 ssrc: (2 * sdpMLineIndex + 2) * 1001923 }];924 // TODO: rewrite to use http://w3c.github.io/webrtc-pc/#set-associated-remote-streams925 var isNewTrack = false;926 if (direction === 'sendrecv' || direction === 'sendonly') {927 isNewTrack = !transceiver.rtpReceiver;928 rtpReceiver = transceiver.rtpReceiver ||929 new window.RTCRtpReceiver(transceiver.dtlsTransport, kind);930 if (isNewTrack) {931 var stream;932 track = rtpReceiver.track;933 // FIXME: does not work with Plan B.934 if (remoteMsid && remoteMsid.stream === '-') {935 // no-op. a stream id of '-' means: no associated stream.936 } else if (remoteMsid) {937 if (!streams[remoteMsid.stream]) {938 streams[remoteMsid.stream] = new window.MediaStream();939 Object.defineProperty(streams[remoteMsid.stream], 'id', {940 get: function() {941 return remoteMsid.stream;942 }943 });944 }945 Object.defineProperty(track, 'id', {946 get: function() {947 return remoteMsid.track;948 }949 });950 stream = streams[remoteMsid.stream];951 } else {952 if (!streams.default) {953 streams.default = new window.MediaStream();954 }955 stream = streams.default;956 }957 if (stream) {958 addTrackToStreamAndFireEvent(track, stream);959 transceiver.associatedRemoteMediaStreams.push(stream);960 }961 receiverList.push([track, rtpReceiver, stream]);962 }963 } else if (transceiver.rtpReceiver && transceiver.rtpReceiver.track) {964 transceiver.associatedRemoteMediaStreams.forEach(function(s) {965 var nativeTrack = s.getTracks().find(function(t) {966 return t.id === transceiver.rtpReceiver.track.id;967 });968 if (nativeTrack) {969 removeTrackFromStreamAndFireEvent(nativeTrack, s);970 }971 });972 transceiver.associatedRemoteMediaStreams = [];973 }974 transceiver.localCapabilities = localCapabilities;975 transceiver.remoteCapabilities = remoteCapabilities;976 transceiver.rtpReceiver = rtpReceiver;977 transceiver.rtcpParameters = rtcpParameters;978 transceiver.sendEncodingParameters = sendEncodingParameters;979 transceiver.recvEncodingParameters = recvEncodingParameters;980 // Start the RTCRtpReceiver now. The RTPSender is started in981 // setLocalDescription.982 pc._transceive(pc.transceivers[sdpMLineIndex],983 false,984 isNewTrack);985 } else if (description.type === 'answer' && !rejected) {986 transceiver = pc.transceivers[sdpMLineIndex];987 iceGatherer = transceiver.iceGatherer;988 iceTransport = transceiver.iceTransport;989 dtlsTransport = transceiver.dtlsTransport;990 rtpReceiver = transceiver.rtpReceiver;991 sendEncodingParameters = transceiver.sendEncodingParameters;992 localCapabilities = transceiver.localCapabilities;993 pc.transceivers[sdpMLineIndex].recvEncodingParameters =994 recvEncodingParameters;995 pc.transceivers[sdpMLineIndex].remoteCapabilities =996 remoteCapabilities;997 pc.transceivers[sdpMLineIndex].rtcpParameters = rtcpParameters;998 if (cands.length && iceTransport.state === 'new') {999 if ((isIceLite || isComplete) &&1000 (!usingBundle || sdpMLineIndex === 0)) {1001 iceTransport.setRemoteCandidates(cands);1002 } else {1003 cands.forEach(function(candidate) {1004 maybeAddCandidate(transceiver.iceTransport, candidate);1005 });1006 }1007 }1008 if (!usingBundle || sdpMLineIndex === 0) {1009 if (iceTransport.state === 'new') {1010 iceTransport.start(iceGatherer, remoteIceParameters,1011 'controlling');1012 }1013 if (dtlsTransport.state === 'new') {1014 dtlsTransport.start(remoteDtlsParameters);1015 }1016 }1017 // If the offer contained RTX but the answer did not,1018 // remove RTX from sendEncodingParameters.1019 var commonCapabilities = getCommonCapabilities(1020 transceiver.localCapabilities,1021 transceiver.remoteCapabilities);1022 var hasRtx = commonCapabilities.codecs.filter(function(c) {1023 return c.name.toLowerCase() === 'rtx';1024 }).length;1025 if (!hasRtx && transceiver.sendEncodingParameters[0].rtx) {1026 delete transceiver.sendEncodingParameters[0].rtx;1027 }1028 pc._transceive(transceiver,1029 direction === 'sendrecv' || direction === 'recvonly',1030 direction === 'sendrecv' || direction === 'sendonly');1031 // TODO: rewrite to use http://w3c.github.io/webrtc-pc/#set-associated-remote-streams1032 if (rtpReceiver &&1033 (direction === 'sendrecv' || direction === 'sendonly')) {1034 track = rtpReceiver.track;1035 if (remoteMsid) {1036 if (!streams[remoteMsid.stream]) {1037 streams[remoteMsid.stream] = new window.MediaStream();1038 }1039 addTrackToStreamAndFireEvent(track, streams[remoteMsid.stream]);1040 receiverList.push([track, rtpReceiver, streams[remoteMsid.stream]]);1041 } else {1042 if (!streams.default) {1043 streams.default = new window.MediaStream();1044 }1045 addTrackToStreamAndFireEvent(track, streams.default);1046 receiverList.push([track, rtpReceiver, streams.default]);1047 }1048 } else {1049 // FIXME: actually the receiver should be created later.1050 delete transceiver.rtpReceiver;1051 }1052 }1053 });1054 if (pc._dtlsRole === undefined) {1055 pc._dtlsRole = description.type === 'offer' ? 'active' : 'passive';1056 }1057 pc._remoteDescription = {1058 type: description.type,1059 sdp: description.sdp1060 };1061 if (description.type === 'offer') {1062 pc._updateSignalingState('have-remote-offer');1063 } else {1064 pc._updateSignalingState('stable');1065 }1066 Object.keys(streams).forEach(function(sid) {1067 var stream = streams[sid];1068 if (stream.getTracks().length) {1069 if (pc.remoteStreams.indexOf(stream) === -1) {1070 pc.remoteStreams.push(stream);1071 var event = new Event('addstream');1072 event.stream = stream;1073 window.setTimeout(function() {1074 pc._dispatchEvent('addstream', event);1075 });1076 }1077 receiverList.forEach(function(item) {1078 var track = item[0];1079 var receiver = item[1];1080 if (stream.id !== item[2].id) {1081 return;1082 }1083 fireAddTrack(pc, track, receiver, [stream]);1084 });1085 }1086 });1087 receiverList.forEach(function(item) {1088 if (item[2]) {1089 return;1090 }1091 fireAddTrack(pc, item[0], item[1], []);1092 });1093 // check whether addIceCandidate({}) was called within four seconds after1094 // setRemoteDescription.1095 window.setTimeout(function() {1096 if (!(pc && pc.transceivers)) {1097 return;1098 }1099 pc.transceivers.forEach(function(transceiver) {1100 if (transceiver.iceTransport &&1101 transceiver.iceTransport.state === 'new' &&1102 transceiver.iceTransport.getRemoteCandidates().length > 0) {1103 console.warn('Timeout for addRemoteCandidate. Consider sending ' +1104 'an end-of-candidates notification');1105 transceiver.iceTransport.addRemoteCandidate({});1106 }1107 });1108 }, 4000);1109 return Promise.resolve();1110 };1111 RTCPeerConnection.prototype.close = function() {1112 this.transceivers.forEach(function(transceiver) {1113 /* not yet1114 if (transceiver.iceGatherer) {1115 transceiver.iceGatherer.close();1116 }1117 */1118 if (transceiver.iceTransport) {1119 transceiver.iceTransport.stop();1120 }1121 if (transceiver.dtlsTransport) {1122 transceiver.dtlsTransport.stop();1123 }1124 if (transceiver.rtpSender) {1125 transceiver.rtpSender.stop();1126 }1127 if (transceiver.rtpReceiver) {1128 transceiver.rtpReceiver.stop();1129 }1130 });1131 // FIXME: clean up tracks, local streams, remote streams, etc1132 this._isClosed = true;1133 this._updateSignalingState('closed');1134 };1135 // Update the signaling state.1136 RTCPeerConnection.prototype._updateSignalingState = function(newState) {1137 this.signalingState = newState;1138 var event = new Event('signalingstatechange');1139 this._dispatchEvent('signalingstatechange', event);1140 };1141 // Determine whether to fire the negotiationneeded event.1142 RTCPeerConnection.prototype._maybeFireNegotiationNeeded = function() {1143 var pc = this;1144 if (this.signalingState !== 'stable' || this.needNegotiation === true) {1145 return;1146 }1147 this.needNegotiation = true;1148 window.setTimeout(function() {1149 if (pc.needNegotiation) {1150 pc.needNegotiation = false;1151 var event = new Event('negotiationneeded');1152 pc._dispatchEvent('negotiationneeded', event);1153 }1154 }, 0);1155 };1156 // Update the ice connection state.1157 RTCPeerConnection.prototype._updateIceConnectionState = function() {1158 var newState;1159 var states = {1160 'new': 0,1161 closed: 0,1162 checking: 0,1163 connected: 0,1164 completed: 0,1165 disconnected: 0,1166 failed: 01167 };1168 this.transceivers.forEach(function(transceiver) {1169 if (transceiver.iceTransport && !transceiver.rejected) {1170 states[transceiver.iceTransport.state]++;1171 }1172 });1173 newState = 'new';1174 if (states.failed > 0) {1175 newState = 'failed';1176 } else if (states.checking > 0) {1177 newState = 'checking';1178 } else if (states.disconnected > 0) {1179 newState = 'disconnected';1180 } else if (states.new > 0) {1181 newState = 'new';1182 } else if (states.connected > 0) {1183 newState = 'connected';1184 } else if (states.completed > 0) {1185 newState = 'completed';1186 }1187 if (newState !== this.iceConnectionState) {1188 this.iceConnectionState = newState;1189 var event = new Event('iceconnectionstatechange');1190 this._dispatchEvent('iceconnectionstatechange', event);1191 }1192 };1193 // Update the connection state.1194 RTCPeerConnection.prototype._updateConnectionState = function() {1195 var newState;1196 var states = {1197 'new': 0,1198 closed: 0,1199 connecting: 0,1200 connected: 0,1201 completed: 0,1202 disconnected: 0,1203 failed: 01204 };1205 this.transceivers.forEach(function(transceiver) {1206 if (transceiver.iceTransport && transceiver.dtlsTransport &&1207 !transceiver.rejected) {1208 states[transceiver.iceTransport.state]++;1209 states[transceiver.dtlsTransport.state]++;1210 }1211 });1212 // ICETransport.completed and connected are the same for this purpose.1213 states.connected += states.completed;1214 newState = 'new';1215 if (states.failed > 0) {1216 newState = 'failed';1217 } else if (states.connecting > 0) {1218 newState = 'connecting';1219 } else if (states.disconnected > 0) {1220 newState = 'disconnected';1221 } else if (states.new > 0) {1222 newState = 'new';1223 } else if (states.connected > 0) {1224 newState = 'connected';1225 }1226 if (newState !== this.connectionState) {1227 this.connectionState = newState;1228 var event = new Event('connectionstatechange');1229 this._dispatchEvent('connectionstatechange', event);1230 }1231 };1232 RTCPeerConnection.prototype.createOffer = function() {1233 var pc = this;1234 if (pc._isClosed) {1235 return Promise.reject(makeError('InvalidStateError',1236 'Can not call createOffer after close'));1237 }1238 var numAudioTracks = pc.transceivers.filter(function(t) {1239 return t.kind === 'audio';1240 }).length;1241 var numVideoTracks = pc.transceivers.filter(function(t) {1242 return t.kind === 'video';1243 }).length;1244 // Determine number of audio and video tracks we need to send/recv.1245 var offerOptions = arguments[0];1246 if (offerOptions) {1247 // Reject Chrome legacy constraints.1248 if (offerOptions.mandatory || offerOptions.optional) {1249 throw new TypeError(1250 'Legacy mandatory/optional constraints not supported.');1251 }1252 if (offerOptions.offerToReceiveAudio !== undefined) {1253 if (offerOptions.offerToReceiveAudio === true) {1254 numAudioTracks = 1;1255 } else if (offerOptions.offerToReceiveAudio === false) {1256 numAudioTracks = 0;1257 } else {1258 numAudioTracks = offerOptions.offerToReceiveAudio;1259 }1260 }1261 if (offerOptions.offerToReceiveVideo !== undefined) {1262 if (offerOptions.offerToReceiveVideo === true) {1263 numVideoTracks = 1;1264 } else if (offerOptions.offerToReceiveVideo === false) {1265 numVideoTracks = 0;1266 } else {1267 numVideoTracks = offerOptions.offerToReceiveVideo;1268 }1269 }1270 }1271 pc.transceivers.forEach(function(transceiver) {1272 if (transceiver.kind === 'audio') {1273 numAudioTracks--;1274 if (numAudioTracks < 0) {1275 transceiver.wantReceive = false;1276 }1277 } else if (transceiver.kind === 'video') {1278 numVideoTracks--;1279 if (numVideoTracks < 0) {1280 transceiver.wantReceive = false;1281 }1282 }1283 });1284 // Create M-lines for recvonly streams.1285 while (numAudioTracks > 0 || numVideoTracks > 0) {1286 if (numAudioTracks > 0) {1287 pc._createTransceiver('audio');1288 numAudioTracks--;1289 }1290 if (numVideoTracks > 0) {1291 pc._createTransceiver('video');1292 numVideoTracks--;1293 }1294 }1295 var sdp = SDPUtils.writeSessionBoilerplate(pc._sdpSessionId,1296 pc._sdpSessionVersion++);1297 pc.transceivers.forEach(function(transceiver, sdpMLineIndex) {1298 // For each track, create an ice gatherer, ice transport,1299 // dtls transport, potentially rtpsender and rtpreceiver.1300 var track = transceiver.track;1301 var kind = transceiver.kind;1302 var mid = transceiver.mid || SDPUtils.generateIdentifier();1303 transceiver.mid = mid;1304 if (!transceiver.iceGatherer) {1305 transceiver.iceGatherer = pc._createIceGatherer(sdpMLineIndex,1306 pc.usingBundle);1307 }1308 var localCapabilities = window.RTCRtpSender.getCapabilities(kind);1309 // filter RTX until additional stuff needed for RTX is implemented1310 // in adapter.js1311 if (edgeVersion < 15019) {1312 localCapabilities.codecs = localCapabilities.codecs.filter(1313 function(codec) {1314 return codec.name !== 'rtx';1315 });1316 }1317 localCapabilities.codecs.forEach(function(codec) {1318 // work around https://bugs.chromium.org/p/webrtc/issues/detail?id=65521319 // by adding level-asymmetry-allowed=11320 if (codec.name === 'H264' &&1321 codec.parameters['level-asymmetry-allowed'] === undefined) {1322 codec.parameters['level-asymmetry-allowed'] = '1';1323 }1324 // for subsequent offers, we might have to re-use the payload1325 // type of the last offer.1326 if (transceiver.remoteCapabilities &&1327 transceiver.remoteCapabilities.codecs) {1328 transceiver.remoteCapabilities.codecs.forEach(function(remoteCodec) {1329 if (codec.name.toLowerCase() === remoteCodec.name.toLowerCase() &&1330 codec.clockRate === remoteCodec.clockRate) {1331 codec.preferredPayloadType = remoteCodec.payloadType;1332 }1333 });1334 }1335 });1336 localCapabilities.headerExtensions.forEach(function(hdrExt) {1337 var remoteExtensions = transceiver.remoteCapabilities &&1338 transceiver.remoteCapabilities.headerExtensions || [];1339 remoteExtensions.forEach(function(rHdrExt) {1340 if (hdrExt.uri === rHdrExt.uri) {1341 hdrExt.id = rHdrExt.id;1342 }1343 });1344 });1345 // generate an ssrc now, to be used later in rtpSender.send1346 var sendEncodingParameters = transceiver.sendEncodingParameters || [{1347 ssrc: (2 * sdpMLineIndex + 1) * 10011348 }];1349 if (track) {1350 // add RTX1351 if (edgeVersion >= 15019 && kind === 'video' &&1352 !sendEncodingParameters[0].rtx) {1353 sendEncodingParameters[0].rtx = {1354 ssrc: sendEncodingParameters[0].ssrc + 11355 };1356 }1357 }1358 if (transceiver.wantReceive) {1359 transceiver.rtpReceiver = new window.RTCRtpReceiver(1360 transceiver.dtlsTransport, kind);1361 }1362 transceiver.localCapabilities = localCapabilities;1363 transceiver.sendEncodingParameters = sendEncodingParameters;1364 });1365 // always offer BUNDLE and dispose on return if not supported.1366 if (pc._config.bundlePolicy !== 'max-compat') {1367 sdp += 'a=group:BUNDLE ' + pc.transceivers.map(function(t) {1368 return t.mid;1369 }).join(' ') + '\r\n';1370 }1371 sdp += 'a=ice-options:trickle\r\n';1372 pc.transceivers.forEach(function(transceiver, sdpMLineIndex) {1373 sdp += writeMediaSection(transceiver, transceiver.localCapabilities,1374 'offer', transceiver.stream, pc._dtlsRole);1375 sdp += 'a=rtcp-rsize\r\n';1376 if (transceiver.iceGatherer && pc.iceGatheringState !== 'new' &&1377 (sdpMLineIndex === 0 || !pc.usingBundle)) {1378 transceiver.iceGatherer.getLocalCandidates().forEach(function(cand) {1379 cand.component = 1;1380 sdp += 'a=' + SDPUtils.writeCandidate(cand) + '\r\n';1381 });1382 if (transceiver.iceGatherer.state === 'completed') {1383 sdp += 'a=end-of-candidates\r\n';1384 }1385 }1386 });1387 var desc = new window.RTCSessionDescription({1388 type: 'offer',1389 sdp: sdp1390 });1391 return Promise.resolve(desc);1392 };1393 RTCPeerConnection.prototype.createAnswer = function() {1394 var pc = this;1395 if (pc._isClosed) {1396 return Promise.reject(makeError('InvalidStateError',1397 'Can not call createAnswer after close'));1398 }1399 if (!(pc.signalingState === 'have-remote-offer' ||1400 pc.signalingState === 'have-local-pranswer')) {1401 return Promise.reject(makeError('InvalidStateError',1402 'Can not call createAnswer in signalingState ' + pc.signalingState));1403 }1404 var sdp = SDPUtils.writeSessionBoilerplate(pc._sdpSessionId,1405 pc._sdpSessionVersion++);1406 if (pc.usingBundle) {1407 sdp += 'a=group:BUNDLE ' + pc.transceivers.map(function(t) {1408 return t.mid;1409 }).join(' ') + '\r\n';1410 }1411 sdp += 'a=ice-options:trickle\r\n';1412 var mediaSectionsInOffer = SDPUtils.getMediaSections(1413 pc._remoteDescription.sdp).length;1414 pc.transceivers.forEach(function(transceiver, sdpMLineIndex) {1415 if (sdpMLineIndex + 1 > mediaSectionsInOffer) {1416 return;1417 }1418 if (transceiver.rejected) {1419 if (transceiver.kind === 'application') {1420 if (transceiver.protocol === 'DTLS/SCTP') { // legacy fmt1421 sdp += 'm=application 0 DTLS/SCTP 5000\r\n';1422 } else {1423 sdp += 'm=application 0 ' + transceiver.protocol +1424 ' webrtc-datachannel\r\n';1425 }1426 } else if (transceiver.kind === 'audio') {1427 sdp += 'm=audio 0 UDP/TLS/RTP/SAVPF 0\r\n' +1428 'a=rtpmap:0 PCMU/8000\r\n';1429 } else if (transceiver.kind === 'video') {1430 sdp += 'm=video 0 UDP/TLS/RTP/SAVPF 120\r\n' +1431 'a=rtpmap:120 VP8/90000\r\n';1432 }1433 sdp += 'c=IN IP4 0.0.0.0\r\n' +1434 'a=inactive\r\n' +1435 'a=mid:' + transceiver.mid + '\r\n';1436 return;1437 }1438 // FIXME: look at direction.1439 if (transceiver.stream) {1440 var localTrack;1441 if (transceiver.kind === 'audio') {1442 localTrack = transceiver.stream.getAudioTracks()[0];1443 } else if (transceiver.kind === 'video') {1444 localTrack = transceiver.stream.getVideoTracks()[0];1445 }1446 if (localTrack) {1447 // add RTX1448 if (edgeVersion >= 15019 && transceiver.kind === 'video' &&1449 !transceiver.sendEncodingParameters[0].rtx) {1450 transceiver.sendEncodingParameters[0].rtx = {1451 ssrc: transceiver.sendEncodingParameters[0].ssrc + 11452 };1453 }1454 }1455 }1456 // Calculate intersection of capabilities.1457 var commonCapabilities = getCommonCapabilities(1458 transceiver.localCapabilities,1459 transceiver.remoteCapabilities);1460 var hasRtx = commonCapabilities.codecs.filter(function(c) {1461 return c.name.toLowerCase() === 'rtx';1462 }).length;1463 if (!hasRtx && transceiver.sendEncodingParameters[0].rtx) {1464 delete transceiver.sendEncodingParameters[0].rtx;1465 }1466 sdp += writeMediaSection(transceiver, commonCapabilities,1467 'answer', transceiver.stream, pc._dtlsRole);1468 if (transceiver.rtcpParameters &&1469 transceiver.rtcpParameters.reducedSize) {1470 sdp += 'a=rtcp-rsize\r\n';1471 }1472 });1473 var desc = new window.RTCSessionDescription({1474 type: 'answer',1475 sdp: sdp1476 });1477 return Promise.resolve(desc);1478 };1479 RTCPeerConnection.prototype.addIceCandidate = function(candidate) {1480 var pc = this;1481 var sections;1482 if (candidate && !(candidate.sdpMLineIndex !== undefined ||1483 candidate.sdpMid)) {1484 return Promise.reject(new TypeError('sdpMLineIndex or sdpMid required'));1485 }1486 // TODO: needs to go into ops queue.1487 return new Promise(function(resolve, reject) {1488 if (!pc._remoteDescription) {1489 return reject(makeError('InvalidStateError',1490 'Can not add ICE candidate without a remote description'));1491 } else if (!candidate || candidate.candidate === '') {1492 for (var j = 0; j < pc.transceivers.length; j++) {1493 if (pc.transceivers[j].rejected) {1494 continue;1495 }1496 pc.transceivers[j].iceTransport.addRemoteCandidate({});1497 sections = SDPUtils.getMediaSections(pc._remoteDescription.sdp);1498 sections[j] += 'a=end-of-candidates\r\n';1499 pc._remoteDescription.sdp =1500 SDPUtils.getDescription(pc._remoteDescription.sdp) +1501 sections.join('');1502 if (pc.usingBundle) {1503 break;1504 }1505 }1506 } else {1507 var sdpMLineIndex = candidate.sdpMLineIndex;1508 if (candidate.sdpMid) {1509 for (var i = 0; i < pc.transceivers.length; i++) {1510 if (pc.transceivers[i].mid === candidate.sdpMid) {1511 sdpMLineIndex = i;1512 break;1513 }1514 }1515 }1516 var transceiver = pc.transceivers[sdpMLineIndex];1517 if (transceiver) {1518 if (transceiver.rejected) {1519 return resolve();1520 }1521 var cand = Object.keys(candidate.candidate).length > 0 ?1522 SDPUtils.parseCandidate(candidate.candidate) : {};1523 // Ignore Chrome's invalid candidates since Edge does not like them.1524 if (cand.protocol === 'tcp' && (cand.port === 0 || cand.port === 9)) {1525 return resolve();1526 }1527 // Ignore RTCP candidates, we assume RTCP-MUX.1528 if (cand.component && cand.component !== 1) {1529 return resolve();1530 }1531 // when using bundle, avoid adding candidates to the wrong1532 // ice transport. And avoid adding candidates added in the SDP.1533 if (sdpMLineIndex === 0 || (sdpMLineIndex > 0 &&1534 transceiver.iceTransport !== pc.transceivers[0].iceTransport)) {1535 if (!maybeAddCandidate(transceiver.iceTransport, cand)) {1536 return reject(makeError('OperationError',1537 'Can not add ICE candidate'));1538 }1539 }1540 // update the remoteDescription.1541 var candidateString = candidate.candidate.trim();1542 if (candidateString.indexOf('a=') === 0) {1543 candidateString = candidateString.substr(2);1544 }1545 sections = SDPUtils.getMediaSections(pc._remoteDescription.sdp);1546 sections[sdpMLineIndex] += 'a=' +1547 (cand.type ? candidateString : 'end-of-candidates')1548 + '\r\n';1549 pc._remoteDescription.sdp =1550 SDPUtils.getDescription(pc._remoteDescription.sdp) +1551 sections.join('');1552 } else {1553 return reject(makeError('OperationError',1554 'Can not add ICE candidate'));1555 }1556 }1557 resolve();1558 });1559 };1560 RTCPeerConnection.prototype.getStats = function(selector) {1561 if (selector && selector instanceof window.MediaStreamTrack) {1562 var senderOrReceiver = null;1563 this.transceivers.forEach(function(transceiver) {1564 if (transceiver.rtpSender &&1565 transceiver.rtpSender.track === selector) {1566 senderOrReceiver = transceiver.rtpSender;1567 } else if (transceiver.rtpReceiver &&1568 transceiver.rtpReceiver.track === selector) {1569 senderOrReceiver = transceiver.rtpReceiver;1570 }1571 });1572 if (!senderOrReceiver) {1573 throw makeError('InvalidAccessError', 'Invalid selector.');1574 }1575 return senderOrReceiver.getStats();1576 }1577 var promises = [];1578 this.transceivers.forEach(function(transceiver) {1579 ['rtpSender', 'rtpReceiver', 'iceGatherer', 'iceTransport',1580 'dtlsTransport'].forEach(function(method) {1581 if (transceiver[method]) {1582 promises.push(transceiver[method].getStats());1583 }1584 });1585 });1586 return Promise.all(promises).then(function(allStats) {1587 var results = new Map();1588 allStats.forEach(function(stats) {1589 stats.forEach(function(stat) {1590 results.set(stat.id, stat);1591 });1592 });1593 return results;1594 });1595 };1596 // fix low-level stat names and return Map instead of object.1597 var ortcObjects = ['RTCRtpSender', 'RTCRtpReceiver', 'RTCIceGatherer',1598 'RTCIceTransport', 'RTCDtlsTransport'];1599 ortcObjects.forEach(function(ortcObjectName) {1600 var obj = window[ortcObjectName];1601 if (obj && obj.prototype && obj.prototype.getStats) {1602 var nativeGetstats = obj.prototype.getStats;1603 obj.prototype.getStats = function() {1604 return nativeGetstats.apply(this)1605 .then(function(nativeStats) {1606 var mapStats = new Map();1607 Object.keys(nativeStats).forEach(function(id) {1608 nativeStats[id].type = fixStatsType(nativeStats[id]);1609 mapStats.set(id, nativeStats[id]);1610 });1611 return mapStats;1612 });1613 };1614 }1615 });1616 // legacy callback shims. Should be moved to adapter.js some days.1617 var methods = ['createOffer', 'createAnswer'];1618 methods.forEach(function(method) {1619 var nativeMethod = RTCPeerConnection.prototype[method];1620 RTCPeerConnection.prototype[method] = function() {1621 var args = arguments;1622 if (typeof args[0] === 'function' ||1623 typeof args[1] === 'function') { // legacy1624 return nativeMethod.apply(this, [arguments[2]])1625 .then(function(description) {1626 if (typeof args[0] === 'function') {1627 args[0].apply(null, [description]);1628 }1629 }, function(error) {1630 if (typeof args[1] === 'function') {1631 args[1].apply(null, [error]);1632 }1633 });1634 }1635 return nativeMethod.apply(this, arguments);1636 };1637 });1638 methods = ['setLocalDescription', 'setRemoteDescription', 'addIceCandidate'];1639 methods.forEach(function(method) {1640 var nativeMethod = RTCPeerConnection.prototype[method];1641 RTCPeerConnection.prototype[method] = function() {1642 var args = arguments;1643 if (typeof args[1] === 'function' ||1644 typeof args[2] === 'function') { // legacy1645 return nativeMethod.apply(this, arguments)1646 .then(function() {1647 if (typeof args[1] === 'function') {1648 args[1].apply(null);1649 }1650 }, function(error) {1651 if (typeof args[2] === 'function') {1652 args[2].apply(null, [error]);1653 }1654 });1655 }1656 return nativeMethod.apply(this, arguments);1657 };1658 });1659 // getStats is special. It doesn't have a spec legacy method yet we support1660 // getStats(something, cb) without error callbacks.1661 ['getStats'].forEach(function(method) {1662 var nativeMethod = RTCPeerConnection.prototype[method];1663 RTCPeerConnection.prototype[method] = function() {1664 var args = arguments;1665 if (typeof args[1] === 'function') {1666 return nativeMethod.apply(this, arguments)1667 .then(function() {1668 if (typeof args[1] === 'function') {1669 args[1].apply(null);1670 }1671 });1672 }1673 return nativeMethod.apply(this, arguments);1674 };1675 });1676 return RTCPeerConnection;...

Full Screen

Full Screen

ice.extension.js

Source:ice.extension.js Github

copy

Full Screen

1'use strict';2const ICE_UFRAG = 'u'.repeat(4);3const ICE_PWD = 'p'.repeat(22);4test(() => {5 const iceTransport = new RTCIceTransport();6}, 'RTCIceTransport constructor does not throw');7test(() => {8 const iceTransport = new RTCIceTransport();9 assert_equals(iceTransport.role, null, 'Expect role to be null');10 assert_equals(iceTransport.state, 'new', `Expect state to be 'new'`);11 assert_equals(iceTransport.gatheringState, 'new',12 `Expect gatheringState to be 'new'`);13 assert_array_equals(iceTransport.getLocalCandidates(), [],14 'Expect no local candidates');15 assert_array_equals(iceTransport.getRemoteCandidates(), [],16 'Expect no remote candidates');17 assert_equals(iceTransport.getSelectedCandidatePair(), null,18 'Expect no selected candidate pair');19 assert_not_equals(iceTransport.getLocalParameters(), null,20 'Expect local parameters generated');21 assert_equals(iceTransport.getRemoteParameters(), null,22 'Expect no remote parameters');23}, 'RTCIceTransport initial properties are set');24test(t => {25 const iceTransport = makeIceTransport(t);26 assert_throws_js(TypeError, () =>27 iceTransport.gather({ iceServers: null }));28}, 'gather() with { iceServers: null } should throw TypeError');29test(t => {30 const iceTransport = makeIceTransport(t);31 iceTransport.gather({ iceServers: undefined });32}, 'gather() with { iceServers: undefined } should succeed');33test(t => {34 const iceTransport = makeIceTransport(t);35 iceTransport.gather({ iceServers: [{36 urls: ['turns:turn.example.org', 'turn:turn.example.net'],37 username: 'user',38 credential: 'cred',39 }] });40}, 'gather() with one turns server, one turn server, username, credential' +41 ' should succeed');42test(t => {43 const iceTransport = makeIceTransport(t);44 iceTransport.gather({ iceServers: [{45 urls: ['stun:stun1.example.net', 'stun:stun2.example.net'],46 }] });47}, 'gather() with 2 stun servers should succeed');48test(t => {49 const iceTransport = makeIceTransport(t);50 iceTransport.stop();51 assert_throws_dom('InvalidStateError', () => iceTransport.gather({}));52}, 'gather() throws if closed');53test(t => {54 const iceTransport = makeIceTransport(t);55 iceTransport.gather({});56 assert_equals(iceTransport.gatheringState, 'gathering');57}, `gather() transitions gatheringState to 'gathering'`);58test(t => {59 const iceTransport = makeIceTransport(t);60 iceTransport.gather({});61 assert_throws_dom('InvalidStateError', () => iceTransport.gather({}));62}, 'gather() throws if called twice');63promise_test(async t => {64 const iceTransport = makeIceTransport(t);65 const watcher = new EventWatcher(t, iceTransport, 'gatheringstatechange');66 iceTransport.gather({});67 await watcher.wait_for('gatheringstatechange');68 assert_equals(iceTransport.gatheringState, 'complete');69}, `eventually transition gatheringState to 'complete'`);70promise_test(async t => {71 const iceTransport = makeIceTransport(t);72 const watcher = new EventWatcher(t, iceTransport,73 [ 'icecandidate', 'gatheringstatechange' ]);74 iceTransport.gather({});75 let candidate;76 do {77 (({ candidate } = await watcher.wait_for('icecandidate')));78 } while (candidate !== null);79 assert_equals(iceTransport.gatheringState, 'gathering');80 await watcher.wait_for('gatheringstatechange');81 assert_equals(iceTransport.gatheringState, 'complete');82}, 'onicecandidate fires with null candidate before gatheringState' +83 ` transitions to 'complete'`);84promise_test(async t => {85 const iceTransport = makeIceTransport(t);86 const watcher = new EventWatcher(t, iceTransport, 'icecandidate');87 iceTransport.gather({});88 const { candidate } = await watcher.wait_for('icecandidate');89 assert_not_equals(candidate.candidate, '');90 assert_array_equals(iceTransport.getLocalCandidates(), [candidate]);91}, 'gather() returns at least one host candidate');92promise_test(async t => {93 const iceTransport = makeIceTransport(t);94 const watcher = new EventWatcher(t, iceTransport, 'icecandidate');95 iceTransport.gather({ gatherPolicy: 'relay' });96 const { candidate } = await watcher.wait_for('icecandidate');97 assert_equals(candidate, null);98 assert_array_equals(iceTransport.getLocalCandidates(), []);99}, `gather() returns no candidates with { gatherPolicy: 'relay'} and no turn` +100 ' servers');101const dummyRemoteParameters = {102 usernameFragment: ICE_UFRAG,103 password: ICE_PWD,104};105test(() => {106 const iceTransport = new RTCIceTransport();107 iceTransport.stop();108 assert_throws_dom('InvalidStateError',109 () => iceTransport.start(dummyRemoteParameters));110 assert_equals(iceTransport.getRemoteParameters(), null);111}, `start() throws if closed`);112test(() => {113 const iceTransport = new RTCIceTransport();114 assert_throws_js(TypeError, () => iceTransport.start({}));115 assert_throws_js(TypeError,116 () => iceTransport.start({ usernameFragment: ICE_UFRAG }));117 assert_throws_js(TypeError,118 () => iceTransport.start({ password: ICE_PWD }));119 assert_equals(iceTransport.getRemoteParameters(), null);120}, 'start() throws if usernameFragment or password not set');121test(() => {122 const TEST_CASES = [123 {usernameFragment: '2sh', description: 'less than 4 characters long'},124 {125 usernameFragment: 'x'.repeat(257),126 description: 'greater than 256 characters long',127 },128 {usernameFragment: '123\n', description: 'illegal character'},129 ];130 for (const {usernameFragment, description} of TEST_CASES) {131 const iceTransport = new RTCIceTransport();132 assert_throws_dom(133 'SyntaxError',134 () => iceTransport.start({ usernameFragment, password: ICE_PWD }),135 `illegal usernameFragment (${description}) should throw a SyntaxError`);136 }137}, 'start() throws if usernameFragment does not conform to syntax');138test(() => {139 const TEST_CASES = [140 {password: 'x'.repeat(21), description: 'less than 22 characters long'},141 {142 password: 'x'.repeat(257),143 description: 'greater than 256 characters long',144 },145 {password: ('x'.repeat(21) + '\n'), description: 'illegal character'},146 ];147 for (const {password, description} of TEST_CASES) {148 const iceTransport = new RTCIceTransport();149 assert_throws_dom(150 'SyntaxError',151 () => iceTransport.start({ usernameFragment: ICE_UFRAG, password }),152 `illegal password (${description}) should throw a SyntaxError`);153 }154}, 'start() throws if password does not conform to syntax');155const assert_ice_parameters_equals = (a, b) => {156 assert_equals(a.usernameFragment, b.usernameFragment,157 'usernameFragments are equal');158 assert_equals(a.password, b.password, 'passwords are equal');159};160test(t => {161 const iceTransport = makeIceTransport(t);162 iceTransport.start(dummyRemoteParameters);163 assert_equals(iceTransport.state, 'new');164 assert_ice_parameters_equals(iceTransport.getRemoteParameters(),165 dummyRemoteParameters);166}, `start() does not transition state to 'checking' if no remote candidates ` +167 'added');168test(t => {169 const iceTransport = makeIceTransport(t);170 iceTransport.start(dummyRemoteParameters);171 assert_equals(iceTransport.role, 'controlled');172}, `start() with default role sets role attribute to 'controlled'`);173test(t => {174 const iceTransport = makeIceTransport(t);175 iceTransport.start(dummyRemoteParameters, 'controlling');176 assert_equals(iceTransport.role, 'controlling');177}, `start() sets role attribute to 'controlling'`);178const candidate1 = new RTCIceCandidate({179 candidate: 'candidate:1 1 udp 2113929471 203.0.113.100 10100 typ host',180 sdpMid: '',181});182test(() => {183 const iceTransport = new RTCIceTransport();184 iceTransport.stop();185 assert_throws_dom('InvalidStateError',186 () => iceTransport.addRemoteCandidate(candidate1));187 assert_array_equals(iceTransport.getRemoteCandidates(), []);188}, 'addRemoteCandidate() throws if closed');189test(() => {190 const iceTransport = new RTCIceTransport();191 assert_throws_dom('OperationError',192 () => iceTransport.addRemoteCandidate(193 new RTCIceCandidate({ candidate: 'invalid', sdpMid: '' })));194 assert_array_equals(iceTransport.getRemoteCandidates(), []);195}, 'addRemoteCandidate() throws on invalid candidate');196test(t => {197 const iceTransport = makeIceTransport(t);198 iceTransport.addRemoteCandidate(candidate1);199 iceTransport.start(dummyRemoteParameters);200 assert_equals(iceTransport.state, 'checking');201 assert_array_equals(iceTransport.getRemoteCandidates(), [candidate1]);202}, `start() transitions state to 'checking' if one remote candidate had been ` +203 'added');204test(t => {205 const iceTransport = makeIceTransport(t);206 iceTransport.start(dummyRemoteParameters);207 iceTransport.addRemoteCandidate(candidate1);208 assert_equals(iceTransport.state, 'checking');209 assert_array_equals(iceTransport.getRemoteCandidates(), [candidate1]);210}, `addRemoteCandidate() transitions state to 'checking' if start() had been ` +211 'called before');212test(t => {213 const iceTransport = makeIceTransport(t);214 iceTransport.start(dummyRemoteParameters);215 assert_throws_dom('InvalidStateError',216 () => iceTransport.start(dummyRemoteParameters, 'controlling'));217}, 'start() throws if later called with a different role');218test(t => {219 const iceTransport = makeIceTransport(t);220 iceTransport.start({221 usernameFragment: '1'.repeat(4),222 password: '1'.repeat(22),223 });224 iceTransport.addRemoteCandidate(candidate1);225 const changedRemoteParameters = {226 usernameFragment: '2'.repeat(4),227 password: '2'.repeat(22),228 };229 iceTransport.start(changedRemoteParameters);230 assert_equals(iceTransport.state, 'new');231 assert_array_equals(iceTransport.getRemoteCandidates(), []);232 assert_ice_parameters_equals(iceTransport.getRemoteParameters(),233 changedRemoteParameters);234}, `start() flushes remote candidates and transitions state to 'new' if ` +235 'later called with different remote parameters');236promise_test(async t => {237 const [ localTransport, remoteTransport ] =238 makeGatherAndStartTwoIceTransports(t);239 const localWatcher = new EventWatcher(t, localTransport, 'statechange');240 const remoteWatcher = new EventWatcher(t, remoteTransport, 'statechange');241 await Promise.all([242 localWatcher.wait_for('statechange').then(() => {243 assert_equals(localTransport.state, 'connected');244 }),245 remoteWatcher.wait_for('statechange').then(() => {246 assert_equals(remoteTransport.state, 'connected');247 }),248 ]);249}, 'Two RTCIceTransports connect to each other');250['controlling', 'controlled'].forEach(role => {251 promise_test(async t => {252 const [ localTransport, remoteTransport ] =253 makeAndGatherTwoIceTransports(t);254 localTransport.start(remoteTransport.getLocalParameters(), role);255 remoteTransport.start(localTransport.getLocalParameters(), role);256 const localWatcher = new EventWatcher(t, localTransport, 'statechange');257 const remoteWatcher = new EventWatcher(t, remoteTransport, 'statechange');258 await Promise.all([259 localWatcher.wait_for('statechange').then(() => {260 assert_equals(localTransport.state, 'connected');261 }),262 remoteWatcher.wait_for('statechange').then(() => {263 assert_equals(remoteTransport.state, 'connected');264 }),265 ]);266 }, `Two RTCIceTransports configured with the ${role} role resolve the ` +267 'conflict in band and still connect.');268});269promise_test(async t => {270 async function waitForSelectedCandidatePairChangeThenConnected(t, transport,271 transportName) {272 const watcher = new EventWatcher(t, transport,273 [ 'statechange', 'selectedcandidatepairchange' ]);274 await watcher.wait_for('selectedcandidatepairchange');275 const selectedCandidatePair = transport.getSelectedCandidatePair();276 assert_not_equals(selectedCandidatePair, null,277 `${transportName} selected candidate pair should not be null once ` +278 'the selectedcandidatepairchange event fires');279 assert_true(280 transport.getLocalCandidates().some(281 ({ candidate }) =>282 candidate === selectedCandidatePair.local.candidate),283 `${transportName} selected candidate pair local should be in the ` +284 'list of local candidates');285 assert_true(286 transport.getRemoteCandidates().some(287 ({ candidate }) =>288 candidate === selectedCandidatePair.remote.candidate),289 `${transportName} selected candidate pair local should be in the ` +290 'list of remote candidates');291 await watcher.wait_for('statechange');292 assert_equals(transport.state, 'connected',293 `${transportName} state should be 'connected'`);294 }295 const [ localTransport, remoteTransport ] =296 makeGatherAndStartTwoIceTransports(t);297 await Promise.all([298 waitForSelectedCandidatePairChangeThenConnected(t, localTransport,299 'local transport'),300 waitForSelectedCandidatePairChangeThenConnected(t, remoteTransport,301 'remote transport'),302 ]);303}, 'Selected candidate pair changes once the RTCIceTransports connect.');304promise_test(async t => {305 const [ transport, ] = makeGatherAndStartTwoIceTransports(t);306 const watcher = new EventWatcher(t, transport, 'selectedcandidatepairchange');307 await watcher.wait_for('selectedcandidatepairchange');308 transport.stop();309 assert_equals(transport.getSelectedCandidatePair(), null);310}, 'getSelectedCandidatePair() returns null once the RTCIceTransport is ' +...

Full Screen

Full Screen

RTCPeerConnection.ts

Source:RTCPeerConnection.ts Github

copy

Full Screen

1let defaultIceServers: RTCIceServer[] = window.RTCPeerConnection.defaultIceServers;2if (defaultIceServers.length > 0) {3 const urls = defaultIceServers[0].urls;4}5// Create a peer connection6let ice1: RTCIceServer = {7 urls: 'stun:stun.l.google.com:19302',8 username: 'john',9 credential: '1234',10 credentialType: 'password',11};12let ice2: RTCIceServer = { urls: ['stun:stunserver.org', 'stun:stun.example.com'] };13let pc: RTCPeerConnection = new RTCPeerConnection({});14let pc2: RTCPeerConnection = new RTCPeerConnection({15 iceServers: [ice1, ice2],16});17window.RTCPeerConnection.generateCertificate("sha-256").then((cert: RTCCertificate) => {18 new RTCPeerConnection({19 iceServers: [ice1],20 iceTransportPolicy: 'relay',21 bundlePolicy: 'max-compat',22 rtcpMuxPolicy: 'require',23 peerIdentity: 'dude',24 certificates: [cert],25 iceCandidatePoolSize: 5,26 });27});28// Get/set the configuration29let conf: RTCConfiguration = pc2.getConfiguration();30pc.setConfiguration(conf);31// Close peer connection32pc2.close();33// Offer/answer flow34pc.createOffer({iceRestart: true})35 .then((offer: RTCSessionDescriptionInit) => {36 pc.setLocalDescription(offer);37 pc2.setRemoteDescription(offer);38 pc2.createAnswer().then((answer: RTCSessionDescriptionInit) => {39 pc2.setLocalDescription(answer);40 pc.setRemoteDescription(answer);41 });42});43// Event handlers44pc.onnegotiationneeded = ev => console.log(ev.type);45pc.onicecandidate = ev => console.log(ev.candidate);46pc.onicecandidateerror = ev => console.log(ev.type);47pc.onsignalingstatechange = ev => console.log(ev.type);48pc.oniceconnectionstatechange = ev => console.log(ev.type);49pc.onicegatheringstatechange = ev => console.log(ev.type);50pc.onconnectionstatechange = ev => console.log(ev.type);51pc.ontrack = ev => console.log(ev.receiver);52pc.ondatachannel = ev => console.log(ev.channel);53// State properties54console.log(pc.signalingState);55console.log(pc.iceGatheringState);56console.log(pc.iceConnectionState);57console.log(pc.connectionState);58// Legacy interface extensions59pc.createOffer(60 (sdp: RTCSessionDescription) => console.log(sdp.sdp),61 (error: DOMException) => console.log(error.message),62 {iceRestart: true}63).then(() => console.log('createOffer complete'));64pc.setLocalDescription(65 {type: 'offer', sdp: 'foobar'},66 () => console.log('local description set'),67 (error: DOMException) => console.log(error.message)68).then(() => console.log('setLocalDescription complete'));69pc.createAnswer(70 (sdp: RTCSessionDescription) => console.log(sdp.sdp),71 (error: DOMException) => console.log(error.message)72).then(() => console.log('createAnswer complete'));73pc.setRemoteDescription(74 {type: 'answer', sdp: 'foobar'},75 () => console.log('remote description set'),76 (error: DOMException) => console.log(error.message)77).then(() => console.log('setRemoteDescription complete'));78pc.addIceCandidate(79 {candidate: 'candidate', sdpMid: 'foo', sdpMLineIndex: 1},80 () => console.log('candidate added'),81 (error: DOMException) => console.log(error.message)82).then(() => console.log('addIceCandidate complete'));83pc.getStats(84 null,85 (report: RTCStatsReport) => console.log('got report'),86 (error: DOMException) => console.log(error.message)87).then(() => console.log('getStats complete'));88// RTCError89const error = new RTCError({90 errorDetail: 'dtls-failure',91 httpRequestStatusCode: 400,92 receivedAlert: 1,93 sctpCauseCode: 1,94 sdpLineNumber: 1,95 sentAlert: 1,96});97// RPCDtlsTransport98const dtlsTransport = pc.sctp!.transport;99dtlsTransport.onerror = ev => console.log(ev.error.errorDetail);100dtlsTransport.onstatechange = ev => console.log(ev.type);101dtlsTransport.addEventListener('error', ev => console.log(ev.error.errorDetail));102dtlsTransport.addEventListener('statechange', ev => console.log(ev.type));103console.log(dtlsTransport.state);104// RPCIceTransport105const iceTransport = dtlsTransport.iceTransport;106iceTransport.onstatechange = ev => console.log(ev.type);107iceTransport.ongatheringstatechange = ev => console.log(ev.type);108iceTransport.onselectedcandidatepairchange = ev => console.log(ev.type);109iceTransport.addEventListener('statechange', ev => console.log(ev.type));110iceTransport.addEventListener('ongatheringstatechange', ev => console.log(ev.type));111iceTransport.addEventListener('onselectedcandidatepairchange', ev => console.log(ev.type));112console.log(iceTransport.role);113console.log(iceTransport.gatheringState);114console.log(iceTransport.getSelectedCandidatePair()!.local);...

Full Screen

Full Screen

Using AI Code Generation

copy

Full Screen

1var wptoolkit = require('wptoolkit');2 if(err) {3 console.log(err);4 }5 else {6 console.log(data);7 }8});9var wptoolkit = require('wptoolkit');10 if(err) {11 console.log(err);12 }13 else {14 console.log(data);15 }16});17var wptoolkit = require('wptoolkit');18 if(err) {19 console.log(err);20 }21 else {22 console.log(data);23 }24});25var wptoolkit = require('wptoolkit');26 if(err) {27 console.log(err);28 }29 else {30 console.log(data);31 }32});33var wptoolkit = require('wptoolkit');34 if(err) {35 console.log(err);36 }37 else {38 console.log(data);39 }40});41var wptoolkit = require('wptoolkit');42 if(err) {43 console.log(err);44 }45 else {46 console.log(data);47 }48});49var wptoolkit = require('wptoolkit');50 if(err) {51 console.log(err);52 }

Full Screen

Using AI Code Generation

copy

Full Screen

1wpt.iceTransport = 'tcp';2var wpt = require('webpagetest');3var options = {4};5wpt.runTest(options, function(err, data) {6 if (err) return console.log(err);7 console.log(data);8});9wpt.iceTransport = 'udp';10var wpt = require('webpagetest');11var options = {12};13wpt.runTest(options, function(err, data) {14 if (err) return console.log(err);15 console.log(data);16});17wpt.iceTransport = 'any';18var wpt = require('webpagetest');19var options = {20};21wpt.runTest(options, function(err, data) {22 if (err) return console.log(err);23 console.log(data);24});25wpt.iceTransport = 'all';26var wpt = require('webpagetest');27var options = {28};29wpt.runTest(options, function(err, data) {30 if (err) return console.log(err);31 console.log(data);32});33wpt.iceTransport = 'none';34var wpt = require('webpagetest');35var options = {36};37wpt.runTest(options, function(err, data) {38 if (err) return console.log(err);39 console.log(data);40});41wpt.iceTransport = 'default';42var wpt = require('webpagetest

Full Screen

Using AI Code Generation

copy

Full Screen

1var iceTransport = require('wptools').iceTransport;2var ice = new iceTransport();3ice.setIceServers([4 {url: 'stun:stun1.l.google.com:19302'},5 {url: 'stun:stun2.l.google.com:19302'}6]);7ice.setIceTransportPolicy('relay');8ice.setIceCandidateFilter('host');9ice.setIceGatheringTimeout(10000);

Full Screen

Using AI Code Generation

copy

Full Screen

1var iceTransport = new IceTransport();2iceTransport.send('Hello world!');3var iceTransport = new IceTransport();4iceTransport.onmessage = function(msg) {5 console.log('Received: ' + msg);6};7var iceTransport = new IceTransport();8iceTransport.onopen = function() {9 console.log('Connection opened');10};11iceTransport.onclose = function() {12 console.log('Connection closed');13};14var iceTransport = new IceTransport();15iceTransport.onmessage = function(msg) {16 console.log('Received: ' + msg);17};18iceTransport.onerror = function(err) {19 console.log('Error: ' + err);20};21var iceTransport = new IceTransport();22iceTransport.onmessage = function(msg) {23 console.log('Received: ' + msg);24};25iceTransport.onstatechange = function(state) {26 console.log('State changed: ' + state);27};28var iceTransport = new IceTransport();29iceTransport.onmessage = function(msg) {30 console.log('Received: ' + msg);31};32iceTransport.onopen = function() {33 console.log('Connection opened');34};35iceTransport.onclose = function() {36 console.log('Connection closed');37};38iceTransport.onerror = function(err) {39 console.log('Error: ' + err);40};41iceTransport.onstatechange = function(state) {42 console.log('State changed: ' + state);43};44var iceTransport = new IceTransport();45iceTransport.send('Hello world!');

Full Screen

Using AI Code Generation

copy

Full Screen

1var wptools = require('wptools');2wptools.iceTransport('en.wikipedia.org', 'Barack Obama', function(data){3 console.log(data);4});5var wptools = require('wptools');6wptools.iceTransport('en.wikipedia.org', 'Barack Obama', function(data){7 console.log(data);8});9var wptools = require('wptools');10wptools.iceTransport('en.wikipedia.org', 'Barack Obama', function(data){11 console.log(data);12});13var wptools = require('wptools');14wptools.iceTransport('en.wikipedia.org', 'Barack Obama', function(data){15 console.log(data);16});17var wptools = require('wptools');18wptools.iceTransport('en.wikipedia.org', 'Barack Obama', function(data){19 console.log(data);20});21var wptools = require('wptools');22wptools.iceTransport('en.wikipedia.org', 'Barack Obama', function(data){23 console.log(data);24});25var wptools = require('wptools');26wptools.iceTransport('en

Full Screen

Using AI Code Generation

copy

Full Screen

1function runTest()2{3 var test = async_test("Test if iceTransport is set correctly");4 var wpt = new WebPerfTest();5 wpt.start();6 wpt.iceTransport = "relay";7 test.step(function()8 {9 assert_equals(wpt.iceTransport, "relay");10 test.done();11 });12}13runTest();

Full Screen

Automation Testing Tutorials

Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.

LambdaTest Learning Hubs:

YouTube

You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.

Run wpt automation tests on LambdaTest cloud grid

Perform automation testing on 3000+ real desktop and mobile devices online.

Try LambdaTest Now !!

Get 100 minutes of automation test minutes FREE!!

Next-Gen App & Browser Testing Cloud

Was this article helpful?

Helpful

NotHelpful