UNPKG

@videosdk.live/js-sdk

Version:

<h1 align="center"> <img src="https://static.videosdk.live/videosdk_logo_website_black.png"/><br/> <p align="center"> Video SDK for JavaScript<br/> <a href="https://videosdk.live/">videosdk.live</a> </p> </h1>

1,404 lines (1,293 loc) 2.08 MB
(function webpackUniversalModuleDefinition(root, factory) { if(typeof exports === 'object' && typeof module === 'object') module.exports = factory(); else if(typeof define === 'function' && define.amd) define([], factory); else { var a = factory(); for(var i in a) (typeof exports === 'object' ? exports : root)[i] = a[i]; } })(self, () => { return /******/ (() => { // webpackBootstrap /******/ var __webpack_modules__ = ({ /***/ 11: /***/ ((__unused_webpack_module, exports, __webpack_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.Chrome74 = void 0; const sdpTransform = __webpack_require__(7363); const Logger_1 = __webpack_require__(2994); const enhancedEvents_1 = __webpack_require__(3953); const ortc = __webpack_require__(8046); const errors_1 = __webpack_require__(4893); const scalabilityModes_1 = __webpack_require__(3303); const sdpCommonUtils = __webpack_require__(5544); const sdpUnifiedPlanUtils = __webpack_require__(5938); const ortcUtils = __webpack_require__(4256); const RemoteSdp_1 = __webpack_require__(1305); const logger = new Logger_1.Logger('Chrome74'); const NAME = 'Chrome74'; const SCTP_NUM_STREAMS = { OS: 1024, MIS: 1024 }; class Chrome74 extends enhancedEvents_1.EnhancedEventEmitter { // Closed flag. _closed = false; // Handler direction. _direction; // Remote SDP handler. _remoteSdp; // Callback to request sending extended RTP capabilities on demand. _getSendExtendedRtpCapabilities; // Initial server side DTLS role. If not 'auto', it will force the opposite // value in client side. _forcedLocalDtlsRole; // RTCPeerConnection instance. _pc; // Map of RTCTransceivers indexed by MID. _mapMidTransceiver = new Map(); // Local stream for sending. _sendStream = new MediaStream(); // Whether a DataChannel m=application section has been created. _hasDataChannelMediaSection = false; // Sending DataChannel id value counter. Incremented for each new DataChannel. _nextSendSctpStreamId = 0; // Got transport local and remote parameters. _transportReady = false; /** * Creates a factory function. */ static createFactory() { return { name: NAME, factory: (options) => new Chrome74(options), getNativeRtpCapabilities: async () => { logger.debug('getNativeRtpCapabilities()'); let pc = new RTCPeerConnection({ iceServers: [], iceTransportPolicy: 'all', bundlePolicy: 'max-bundle', rtcpMuxPolicy: 'require', }); try { pc.addTransceiver('audio'); pc.addTransceiver('video'); const offer = await pc.createOffer(); try { pc.close(); } catch (error) { } pc = undefined; const sdpObject = sdpTransform.parse(offer.sdp); const nativeRtpCapabilities = Chrome74.getLocalRtpCapabilities(sdpObject); return nativeRtpCapabilities; } catch (error) { try { pc?.close(); } catch (error2) { } pc = undefined; throw error; } }, getNativeSctpCapabilities: async () => { logger.debug('getNativeSctpCapabilities()'); return { numStreams: SCTP_NUM_STREAMS, }; }, }; } static getLocalRtpCapabilities(localSdpObject) { const nativeRtpCapabilities = sdpCommonUtils.extractRtpCapabilities({ sdpObject: localSdpObject, }); // Need to validate and normalize native RTP capabilities. ortc.validateAndNormalizeRtpCapabilities(nativeRtpCapabilities); // libwebrtc supports NACK for OPUS but doesn't announce it. ortcUtils.addNackSupportForOpus(nativeRtpCapabilities); return nativeRtpCapabilities; } constructor({ direction, iceParameters, iceCandidates, dtlsParameters, sctpParameters, iceServers, iceTransportPolicy, additionalSettings, getSendExtendedRtpCapabilities, }) { super(); logger.debug('constructor()'); this._direction = direction; this._remoteSdp = new RemoteSdp_1.RemoteSdp({ iceParameters, iceCandidates, dtlsParameters, sctpParameters, }); this._getSendExtendedRtpCapabilities = getSendExtendedRtpCapabilities; if (dtlsParameters.role && dtlsParameters.role !== 'auto') { this._forcedLocalDtlsRole = dtlsParameters.role === 'server' ? 'client' : 'server'; } this._pc = new RTCPeerConnection({ iceServers: iceServers ?? [], iceTransportPolicy: iceTransportPolicy ?? 'all', bundlePolicy: 'max-bundle', rtcpMuxPolicy: 'require', ...additionalSettings, }); this._pc.addEventListener('icegatheringstatechange', this.onIceGatheringStateChange); this._pc.addEventListener('icecandidateerror', this.onIceCandidateError); if (this._pc.connectionState) { this._pc.addEventListener('connectionstatechange', this.onConnectionStateChange); } else { logger.warn('run() | pc.connectionState not supported, using pc.iceConnectionState'); this._pc.addEventListener('iceconnectionstatechange', this.onIceConnectionStateChange); } } get name() { return NAME; } close() { logger.debug('close()'); if (this._closed) { return; } this._closed = true; // Close RTCPeerConnection. try { this._pc.close(); } catch (error) { } this._pc.removeEventListener('icegatheringstatechange', this.onIceGatheringStateChange); this._pc.removeEventListener('icecandidateerror', this.onIceCandidateError); this._pc.removeEventListener('connectionstatechange', this.onConnectionStateChange); this._pc.removeEventListener('iceconnectionstatechange', this.onIceConnectionStateChange); this.emit('@close'); // Invoke close() in EnhancedEventEmitter classes. super.close(); } async updateIceServers(iceServers) { this.assertNotClosed(); logger.debug('updateIceServers()'); const configuration = this._pc.getConfiguration(); configuration.iceServers = iceServers; this._pc.setConfiguration(configuration); } async restartIce(iceParameters) { this.assertNotClosed(); logger.debug('restartIce()'); // Provide the remote SDP handler with new remote ICE parameters. this._remoteSdp.updateIceParameters(iceParameters); if (!this._transportReady) { return; } if (this._direction === 'send') { const offer = await this._pc.createOffer({ iceRestart: true }); logger.debug('restartIce() | calling pc.setLocalDescription() [offer:%o]', offer); await this._pc.setLocalDescription(offer); const answer = { type: 'answer', sdp: this._remoteSdp.getSdp(), }; logger.debug('restartIce() | calling pc.setRemoteDescription() [answer:%o]', answer); await this._pc.setRemoteDescription(answer); } else { const offer = { type: 'offer', sdp: this._remoteSdp.getSdp(), }; logger.debug('restartIce() | calling pc.setRemoteDescription() [offer:%o]', offer); await this._pc.setRemoteDescription(offer); const answer = await this._pc.createAnswer(); logger.debug('restartIce() | calling pc.setLocalDescription() [answer:%o]', answer); await this._pc.setLocalDescription(answer); } } async getTransportStats() { this.assertNotClosed(); return this._pc.getStats(); } async send({ track, encodings, codecOptions, codec, }) { this.assertNotClosed(); this.assertSendDirection(); logger.debug('send() [kind:%s, track.id:%s]', track.kind, track.id); if (encodings && encodings.length > 1) { encodings.forEach((encoding, idx) => { encoding.rid = `r${idx}`; }); } const mediaSectionIdx = this._remoteSdp.getNextMediaSectionIdx(); const transceiver = this._pc.addTransceiver(track, { direction: 'sendonly', streams: [this._sendStream], sendEncodings: encodings, }); let offer = await this._pc.createOffer(); let localSdpObject = sdpTransform.parse(offer.sdp); if (localSdpObject.extmapAllowMixed) { this._remoteSdp.setSessionExtmapAllowMixed(); } const nativeRtpCapabilities = Chrome74.getLocalRtpCapabilities(localSdpObject); const sendExtendedRtpCapabilities = this._getSendExtendedRtpCapabilities(nativeRtpCapabilities); // Generic sending RTP parameters. const sendingRtpParameters = ortc.getSendingRtpParameters(track.kind, sendExtendedRtpCapabilities); // This may throw. sendingRtpParameters.codecs = ortc.reduceCodecs(sendingRtpParameters.codecs, codec); // Generic sending RTP parameters suitable for the SDP remote answer. const sendingRemoteRtpParameters = ortc.getSendingRemoteRtpParameters(track.kind, sendExtendedRtpCapabilities); // This may throw. sendingRemoteRtpParameters.codecs = ortc.reduceCodecs(sendingRemoteRtpParameters.codecs, codec); if (!this._transportReady) { await this.setupTransport({ localDtlsRole: this._forcedLocalDtlsRole ?? 'client', localSdpObject, }); } // Special case for VP9 with SVC. let hackVp9Svc = false; const layers = (0, scalabilityModes_1.parse)((encodings ?? [{}])[0].scalabilityMode); let offerMediaObject; if (encodings && encodings.length === 1 && layers.spatialLayers > 1 && sendingRtpParameters.codecs[0].mimeType.toLowerCase() === 'video/vp9') { logger.debug('send() | enabling legacy simulcast for VP9 SVC'); hackVp9Svc = true; localSdpObject = sdpTransform.parse(offer.sdp); offerMediaObject = localSdpObject.media[mediaSectionIdx.idx]; sdpUnifiedPlanUtils.addLegacySimulcast({ offerMediaObject, numStreams: layers.spatialLayers, }); offer = { type: 'offer', sdp: sdpTransform.write(localSdpObject), }; } logger.debug('send() | calling pc.setLocalDescription() [offer:%o]', offer); await this._pc.setLocalDescription(offer); // We can now get the transceiver.mid. const localId = transceiver.mid; // Set MID. sendingRtpParameters.mid = localId; localSdpObject = sdpTransform.parse(this._pc.localDescription.sdp); offerMediaObject = localSdpObject.media[mediaSectionIdx.idx]; // Set RTCP CNAME. sendingRtpParameters.rtcp.cname = sdpCommonUtils.getCname({ offerMediaObject, }); // Set RTP encodings by parsing the SDP offer if no encodings are given. if (!encodings) { sendingRtpParameters.encodings = sdpUnifiedPlanUtils.getRtpEncodings({ offerMediaObject, }); } // Set RTP encodings by parsing the SDP offer and complete them with given // one if just a single encoding has been given. else if (encodings.length === 1) { let newEncodings = sdpUnifiedPlanUtils.getRtpEncodings({ offerMediaObject, }); Object.assign(newEncodings[0], encodings[0]); // Hack for VP9 SVC. if (hackVp9Svc) { newEncodings = [newEncodings[0]]; } sendingRtpParameters.encodings = newEncodings; } // Otherwise if more than 1 encoding are given use them verbatim. else { sendingRtpParameters.encodings = encodings; } // If VP8 or H264 and there is effective simulcast, add scalabilityMode to // each encoding. if (sendingRtpParameters.encodings.length > 1 && (sendingRtpParameters.codecs[0].mimeType.toLowerCase() === 'video/vp8' || sendingRtpParameters.codecs[0].mimeType.toLowerCase() === 'video/h264')) { for (const encoding of sendingRtpParameters.encodings) { if (encoding.scalabilityMode) { encoding.scalabilityMode = `L1T${layers.temporalLayers}`; } else { encoding.scalabilityMode = 'L1T3'; } } } this._remoteSdp.send({ offerMediaObject, reuseMid: mediaSectionIdx.reuseMid, offerRtpParameters: sendingRtpParameters, answerRtpParameters: sendingRemoteRtpParameters, codecOptions, }); const answer = { type: 'answer', sdp: this._remoteSdp.getSdp(), }; logger.debug('send() | calling pc.setRemoteDescription() [answer:%o]', answer); await this._pc.setRemoteDescription(answer); // Store in the map. this._mapMidTransceiver.set(localId, transceiver); return { localId, rtpParameters: sendingRtpParameters, rtpSender: transceiver.sender, }; } async stopSending(localId) { this.assertSendDirection(); logger.debug('stopSending() [localId:%s]', localId); if (this._closed) { return; } const transceiver = this._mapMidTransceiver.get(localId); if (!transceiver) { throw new Error('associated RTCRtpTransceiver not found'); } void transceiver.sender.replaceTrack(null); this._pc.removeTrack(transceiver.sender); const mediaSectionClosed = this._remoteSdp.closeMediaSection(transceiver.mid); if (mediaSectionClosed) { try { transceiver.stop(); } catch (error) { } } const offer = await this._pc.createOffer(); logger.debug('stopSending() | calling pc.setLocalDescription() [offer:%o]', offer); await this._pc.setLocalDescription(offer); const answer = { type: 'answer', sdp: this._remoteSdp.getSdp(), }; logger.debug('stopSending() | calling pc.setRemoteDescription() [answer:%o]', answer); await this._pc.setRemoteDescription(answer); this._mapMidTransceiver.delete(localId); } async pauseSending(localId) { this.assertNotClosed(); this.assertSendDirection(); logger.debug('pauseSending() [localId:%s]', localId); const transceiver = this._mapMidTransceiver.get(localId); if (!transceiver) { throw new Error('associated RTCRtpTransceiver not found'); } transceiver.direction = 'inactive'; this._remoteSdp.pauseMediaSection(localId); const offer = await this._pc.createOffer(); logger.debug('pauseSending() | calling pc.setLocalDescription() [offer:%o]', offer); await this._pc.setLocalDescription(offer); const answer = { type: 'answer', sdp: this._remoteSdp.getSdp(), }; logger.debug('pauseSending() | calling pc.setRemoteDescription() [answer:%o]', answer); await this._pc.setRemoteDescription(answer); } async resumeSending(localId) { this.assertNotClosed(); this.assertSendDirection(); logger.debug('resumeSending() [localId:%s]', localId); const transceiver = this._mapMidTransceiver.get(localId); this._remoteSdp.resumeSendingMediaSection(localId); if (!transceiver) { throw new Error('associated RTCRtpTransceiver not found'); } transceiver.direction = 'sendonly'; const offer = await this._pc.createOffer(); logger.debug('resumeSending() | calling pc.setLocalDescription() [offer:%o]', offer); await this._pc.setLocalDescription(offer); const answer = { type: 'answer', sdp: this._remoteSdp.getSdp(), }; logger.debug('resumeSending() | calling pc.setRemoteDescription() [answer:%o]', answer); await this._pc.setRemoteDescription(answer); } async replaceTrack(localId, track) { this.assertNotClosed(); this.assertSendDirection(); if (track) { logger.debug('replaceTrack() [localId:%s, track.id:%s]', localId, track.id); } else { logger.debug('replaceTrack() [localId:%s, no track]', localId); } const transceiver = this._mapMidTransceiver.get(localId); if (!transceiver) { throw new Error('associated RTCRtpTransceiver not found'); } await transceiver.sender.replaceTrack(track); } async setMaxSpatialLayer(localId, spatialLayer) { this.assertNotClosed(); this.assertSendDirection(); logger.debug('setMaxSpatialLayer() [localId:%s, spatialLayer:%s]', localId, spatialLayer); const transceiver = this._mapMidTransceiver.get(localId); if (!transceiver) { throw new Error('associated RTCRtpTransceiver not found'); } const parameters = transceiver.sender.getParameters(); parameters.encodings.forEach((encoding, idx) => { if (idx <= spatialLayer) { encoding.active = true; } else { encoding.active = false; } }); await transceiver.sender.setParameters(parameters); this._remoteSdp.muxMediaSectionSimulcast(localId, parameters.encodings); const offer = await this._pc.createOffer(); logger.debug('setMaxSpatialLayer() | calling pc.setLocalDescription() [offer:%o]', offer); await this._pc.setLocalDescription(offer); const answer = { type: 'answer', sdp: this._remoteSdp.getSdp(), }; logger.debug('setMaxSpatialLayer() | calling pc.setRemoteDescription() [answer:%o]', answer); await this._pc.setRemoteDescription(answer); } async setRtpEncodingParameters(localId, params) { this.assertNotClosed(); this.assertSendDirection(); logger.debug('setRtpEncodingParameters() [localId:%s, params:%o]', localId, params); const transceiver = this._mapMidTransceiver.get(localId); if (!transceiver) { throw new Error('associated RTCRtpTransceiver not found'); } const parameters = transceiver.sender.getParameters(); parameters.encodings.forEach((encoding, idx) => { parameters.encodings[idx] = { ...encoding, ...params }; }); await transceiver.sender.setParameters(parameters); this._remoteSdp.muxMediaSectionSimulcast(localId, parameters.encodings); const offer = await this._pc.createOffer(); logger.debug('setRtpEncodingParameters() | calling pc.setLocalDescription() [offer:%o]', offer); await this._pc.setLocalDescription(offer); const answer = { type: 'answer', sdp: this._remoteSdp.getSdp(), }; logger.debug('setRtpEncodingParameters() | calling pc.setRemoteDescription() [answer:%o]', answer); await this._pc.setRemoteDescription(answer); } async getSenderStats(localId) { this.assertNotClosed(); this.assertSendDirection(); const transceiver = this._mapMidTransceiver.get(localId); if (!transceiver) { throw new Error('associated RTCRtpTransceiver not found'); } return transceiver.sender.getStats(); } async sendDataChannel({ ordered, maxPacketLifeTime, maxRetransmits, label, protocol, }) { this.assertNotClosed(); this.assertSendDirection(); const options = { negotiated: true, id: this._nextSendSctpStreamId, ordered, maxPacketLifeTime, maxRetransmits, protocol, }; logger.debug('sendDataChannel() [options:%o]', options); const dataChannel = this._pc.createDataChannel(label, options); // Increase next id. this._nextSendSctpStreamId = ++this._nextSendSctpStreamId % SCTP_NUM_STREAMS.MIS; // If this is the first DataChannel we need to create the SDP answer with // m=application section. if (!this._hasDataChannelMediaSection) { const offer = await this._pc.createOffer(); const localSdpObject = sdpTransform.parse(offer.sdp); const offerMediaObject = localSdpObject.media.find(m => m.type === 'application'); if (!this._transportReady) { await this.setupTransport({ localDtlsRole: this._forcedLocalDtlsRole ?? 'client', localSdpObject, }); } logger.debug('sendDataChannel() | calling pc.setLocalDescription() [offer:%o]', offer); await this._pc.setLocalDescription(offer); this._remoteSdp.sendSctpAssociation({ offerMediaObject }); const answer = { type: 'answer', sdp: this._remoteSdp.getSdp(), }; logger.debug('sendDataChannel() | calling pc.setRemoteDescription() [answer:%o]', answer); await this._pc.setRemoteDescription(answer); this._hasDataChannelMediaSection = true; } const sctpStreamParameters = { streamId: options.id, ordered: options.ordered, maxPacketLifeTime: options.maxPacketLifeTime, maxRetransmits: options.maxRetransmits, }; return { dataChannel, sctpStreamParameters }; } async receive(optionsList) { this.assertNotClosed(); this.assertRecvDirection(); const results = []; const mapLocalId = new Map(); for (const options of optionsList) { const { trackId, kind, rtpParameters, streamId } = options; logger.debug('receive() [trackId:%s, kind:%s]', trackId, kind); const localId = rtpParameters.mid ?? String(this._mapMidTransceiver.size); mapLocalId.set(trackId, localId); this._remoteSdp.receive({ mid: localId, kind, offerRtpParameters: rtpParameters, streamId: streamId ?? rtpParameters.rtcp.cname, trackId, }); } const offer = { type: 'offer', sdp: this._remoteSdp.getSdp(), }; logger.debug('receive() | calling pc.setRemoteDescription() [offer:%o]', offer); await this._pc.setRemoteDescription(offer); let answer = await this._pc.createAnswer(); const localSdpObject = sdpTransform.parse(answer.sdp); for (const options of optionsList) { const { trackId, rtpParameters } = options; const localId = mapLocalId.get(trackId); const answerMediaObject = localSdpObject.media.find(m => String(m.mid) === localId); // May need to modify codec parameters in the answer based on codec // parameters in the offer. sdpCommonUtils.applyCodecParameters({ offerRtpParameters: rtpParameters, answerMediaObject, }); } answer = { type: 'answer', sdp: sdpTransform.write(localSdpObject), }; if (!this._transportReady) { await this.setupTransport({ localDtlsRole: this._forcedLocalDtlsRole ?? 'client', localSdpObject, }); } logger.debug('receive() | calling pc.setLocalDescription() [answer:%o]', answer); await this._pc.setLocalDescription(answer); for (const options of optionsList) { const { trackId } = options; const localId = mapLocalId.get(trackId); const transceiver = this._pc .getTransceivers() .find((t) => t.mid === localId); if (!transceiver) { throw new Error('new RTCRtpTransceiver not found'); } else { // Store in the map. this._mapMidTransceiver.set(localId, transceiver); results.push({ localId, track: transceiver.receiver.track, rtpReceiver: transceiver.receiver, }); } } return results; } async stopReceiving(localIds) { this.assertRecvDirection(); if (this._closed) { return; } for (const localId of localIds) { logger.debug('stopReceiving() [localId:%s]', localId); const transceiver = this._mapMidTransceiver.get(localId); if (!transceiver) { throw new Error('associated RTCRtpTransceiver not found'); } this._remoteSdp.closeMediaSection(transceiver.mid); } const offer = { type: 'offer', sdp: this._remoteSdp.getSdp(), }; logger.debug('stopReceiving() | calling pc.setRemoteDescription() [offer:%o]', offer); await this._pc.setRemoteDescription(offer); const answer = await this._pc.createAnswer(); logger.debug('stopReceiving() | calling pc.setLocalDescription() [answer:%o]', answer); await this._pc.setLocalDescription(answer); for (const localId of localIds) { this._mapMidTransceiver.delete(localId); } } async pauseReceiving(localIds) { this.assertNotClosed(); this.assertRecvDirection(); for (const localId of localIds) { logger.debug('pauseReceiving() [localId:%s]', localId); const transceiver = this._mapMidTransceiver.get(localId); if (!transceiver) { throw new Error('associated RTCRtpTransceiver not found'); } transceiver.direction = 'inactive'; this._remoteSdp.pauseMediaSection(localId); } const offer = { type: 'offer', sdp: this._remoteSdp.getSdp(), }; logger.debug('pauseReceiving() | calling pc.setRemoteDescription() [offer:%o]', offer); await this._pc.setRemoteDescription(offer); const answer = await this._pc.createAnswer(); logger.debug('pauseReceiving() | calling pc.setLocalDescription() [answer:%o]', answer); await this._pc.setLocalDescription(answer); } async resumeReceiving(localIds) { this.assertNotClosed(); this.assertRecvDirection(); for (const localId of localIds) { logger.debug('resumeReceiving() [localId:%s]', localId); const transceiver = this._mapMidTransceiver.get(localId); if (!transceiver) { throw new Error('associated RTCRtpTransceiver not found'); } transceiver.direction = 'recvonly'; this._remoteSdp.resumeReceivingMediaSection(localId); } const offer = { type: 'offer', sdp: this._remoteSdp.getSdp(), }; logger.debug('resumeReceiving() | calling pc.setRemoteDescription() [offer:%o]', offer); await this._pc.setRemoteDescription(offer); const answer = await this._pc.createAnswer(); logger.debug('resumeReceiving() | calling pc.setLocalDescription() [answer:%o]', answer); await this._pc.setLocalDescription(answer); } async getReceiverStats(localId) { this.assertNotClosed(); this.assertRecvDirection(); const transceiver = this._mapMidTransceiver.get(localId); if (!transceiver) { throw new Error('associated RTCRtpTransceiver not found'); } return transceiver.receiver.getStats(); } async receiveDataChannel({ sctpStreamParameters, label, protocol, }) { this.assertNotClosed(); this.assertRecvDirection(); const { streamId, ordered, maxPacketLifeTime, maxRetransmits, } = sctpStreamParameters; const options = { negotiated: true, id: streamId, ordered, maxPacketLifeTime, maxRetransmits, protocol, }; logger.debug('receiveDataChannel() [options:%o]', options); const dataChannel = this._pc.createDataChannel(label, options); // If this is the first DataChannel we need to create the SDP offer with // m=application section. if (!this._hasDataChannelMediaSection) { this._remoteSdp.receiveSctpAssociation(); const offer = { type: 'offer', sdp: this._remoteSdp.getSdp(), }; logger.debug('receiveDataChannel() | calling pc.setRemoteDescription() [offer:%o]', offer); await this._pc.setRemoteDescription(offer); const answer = await this._pc.createAnswer(); if (!this._transportReady) { const localSdpObject = sdpTransform.parse(answer.sdp); await this.setupTransport({ localDtlsRole: this._forcedLocalDtlsRole ?? 'client', localSdpObject, }); } logger.debug('receiveDataChannel() | calling pc.setRemoteDescription() [answer:%o]', answer); await this._pc.setLocalDescription(answer); this._hasDataChannelMediaSection = true; } return { dataChannel }; } async setupTransport({ localDtlsRole, localSdpObject, }) { if (!localSdpObject) { localSdpObject = sdpTransform.parse(this._pc.localDescription.sdp); } // Get our local DTLS parameters. const dtlsParameters = sdpCommonUtils.extractDtlsParameters({ sdpObject: localSdpObject, }); // Set our DTLS role. dtlsParameters.role = localDtlsRole; // Update the remote DTLS role in the SDP. this._remoteSdp.updateDtlsRole(localDtlsRole === 'client' ? 'server' : 'client'); // Need to tell the remote transport about our parameters. await new Promise((resolve, reject) => { this.safeEmit('@connect', { dtlsParameters }, resolve, reject); }); this._transportReady = true; } onIceGatheringStateChange = () => { this.emit('@icegatheringstatechange', this._pc.iceGatheringState); }; onIceCandidateError = (event) => { this.emit('@icecandidateerror', event); }; onConnectionStateChange = () => { this.emit('@connectionstatechange', this._pc.connectionState); }; onIceConnectionStateChange = () => { switch (this._pc.iceConnectionState) { case 'checking': { this.emit('@connectionstatechange', 'connecting'); break; } case 'connected': case 'completed': { this.emit('@connectionstatechange', 'connected'); break; } case 'failed': { this.emit('@connectionstatechange', 'failed'); break; } case 'disconnected': { this.emit('@connectionstatechange', 'disconnected'); break; } case 'closed': { this.emit('@connectionstatechange', 'closed'); break; } } }; assertNotClosed() { if (this._closed) { throw new errors_1.InvalidStateError('method called in a closed handler'); } } assertSendDirection() { if (this._direction !== 'send') { throw new Error('method can just be called for handlers with "send" direction'); } } assertRecvDirection() { if (this._direction !== 'recv') { throw new Error('method can just be called for handlers with "recv" direction'); } } } exports.Chrome74 = Chrome74; /***/ }), /***/ 19: /***/ (function(module, exports, __webpack_require__) { ;(function (root, factory, undef) { if (true) { // CommonJS module.exports = exports = factory(__webpack_require__(9021), __webpack_require__(3009), __webpack_require__(1025)); } else {} }(this, function (CryptoJS) { (function () { // Shortcuts var C = CryptoJS; var C_lib = C.lib; var Base = C_lib.Base; var WordArray = C_lib.WordArray; var C_algo = C.algo; var SHA256 = C_algo.SHA256; var HMAC = C_algo.HMAC; /** * Password-Based Key Derivation Function 2 algorithm. */ var PBKDF2 = C_algo.PBKDF2 = Base.extend({ /** * Configuration options. * * @property {number} keySize The key size in words to generate. Default: 4 (128 bits) * @property {Hasher} hasher The hasher to use. Default: SHA256 * @property {number} iterations The number of iterations to perform. Default: 250000 */ cfg: Base.extend({ keySize: 128/32, hasher: SHA256, iterations: 250000 }), /** * Initializes a newly created key derivation function. * * @param {Object} cfg (Optional) The configuration options to use for the derivation. * * @example * * var kdf = CryptoJS.algo.PBKDF2.create(); * var kdf = CryptoJS.algo.PBKDF2.create({ keySize: 8 }); * var kdf = CryptoJS.algo.PBKDF2.create({ keySize: 8, iterations: 1000 }); */ init: function (cfg) { this.cfg = this.cfg.extend(cfg); }, /** * Computes the Password-Based Key Derivation Function 2. * * @param {WordArray|string} password The password. * @param {WordArray|string} salt A salt. * * @return {WordArray} The derived key. * * @example * * var key = kdf.compute(password, salt); */ compute: function (password, salt) { // Shortcut var cfg = this.cfg; // Init HMAC var hmac = HMAC.create(cfg.hasher, password); // Initial values var derivedKey = WordArray.create(); var blockIndex = WordArray.create([0x00000001]); // Shortcuts var derivedKeyWords = derivedKey.words; var blockIndexWords = blockIndex.words; var keySize = cfg.keySize; var iterations = cfg.iterations; // Generate key while (derivedKeyWords.length < keySize) { var block = hmac.update(salt).finalize(blockIndex); hmac.reset(); // Shortcuts var blockWords = block.words; var blockWordsLength = blockWords.length; // Iterations var intermediate = block; for (var i = 1; i < iterations; i++) { intermediate = hmac.finalize(intermediate); hmac.reset(); // Shortcut var intermediateWords = intermediate.words; // XOR intermediate with block for (var j = 0; j < blockWordsLength; j++) { blockWords[j] ^= intermediateWords[j]; } } derivedKey.concat(block); blockIndexWords[0]++; } derivedKey.sigBytes = keySize * 4; return derivedKey; } }); /** * Computes the Password-Based Key Derivation Function 2. * * @param {WordArray|string} password The password. * @param {WordArray|string} salt A salt. * @param {Object} cfg (Optional) The configuration options to use for this computation. * * @return {WordArray} The derived key. * * @static * * @example * * var key = CryptoJS.PBKDF2(password, salt); * var key = CryptoJS.PBKDF2(password, salt, { keySize: 8 }); * var key = CryptoJS.PBKDF2(password, salt, { keySize: 8, iterations: 1000 }); */ C.PBKDF2 = function (password, salt, cfg) { return PBKDF2.create(cfg).compute(password, salt); }; }()); return CryptoJS.PBKDF2; })); /***/ }), /***/ 25: /***/ (function(module, exports, __webpack_require__) { ;(function (root, factory, undef) { if (true) { // CommonJS module.exports = exports = factory(__webpack_require__(9021), __webpack_require__(7165)); } else {} }(this, function (CryptoJS) { (function (undefined) { // Shortcuts var C = CryptoJS; var C_lib = C.lib; var CipherParams = C_lib.CipherParams; var C_enc = C.enc; var Hex = C_enc.Hex; var C_format = C.format; var HexFormatter = C_format.Hex = { /** * Converts the ciphertext of a cipher params object to a hexadecimally encoded string. * * @param {CipherParams} cipherParams The cipher params object. * * @return {string} The hexadecimally encoded string. * * @static * * @example * * var hexString = CryptoJS.format.Hex.stringify(cipherParams); */ stringify: function (cipherParams) { return cipherParams.ciphertext.toString(Hex); }, /** * Converts a hexadecimally encoded ciphertext string to a cipher params object. * * @param {string} input The hexadecimally encoded string. * * @return {CipherParams} The cipher params object. * * @static * * @example * * var cipherParams = CryptoJS.format.Hex.parse(hexString); */ parse: function (input) { var ciphertext = Hex.parse(input); return CipherParams.create({ ciphertext: ciphertext }); } }; }()); return CryptoJS.format.Hex; })); /***/ }), /***/ 76: /***/ ((__unused_webpack_module, exports, __webpack_require__) => { "use strict"; var __webpack_unused_export__; __webpack_unused_export__ = ({ value: true }); __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = __webpack_unused_export__ = exports.s$ = __webpack_unused_export__ = __webpack_unused_export__ = exports.pF = __webpack_unused_export__ = __webpack_unused_export__ = void 0; const debug_1 = __webpack_require__(7833); __webpack_unused_export__ = debug_1.default; /** * Expose all types. */ /* unused reexport */ __webpack_require__(8057); /** * Expose mediasoup-client version. */ __webpack_unused_export__ = '3.15.5'; /** * Expose Device class and device detector helpers. */ var Device_1 = __webpack_require__(6004); Object.defineProperty(exports, "pF", ({ enumerable: true, get: function () { return Device_1.Device; } })); __webpack_unused_export__ = ({ enumerable: true, get: function () { return Device_1.detectDevice; } }); __webpack_unused_export__ = ({ enumerable: true, get: function () { return Device_1.detectDeviceAsync; } }); /** * Expose parseScalabilityMode() function. */ var scalabilityModes_1 = __webpack_require__(3303); Object.defineProperty(exports, "s$", ({ enumerable: true, get: function () { return scalabilityModes_1.parse; } })); /** * Expose all ORTC functions. */ /* unused reexport */ __webpack_require__(8046); /** * Expose enhanced events. */ /* unused reexport */ __webpack_require__(3953); /** * Expose FakeHandler. */ var FakeHandler_1 = __webpack_require__(2731); __webpack_unused_export__ = ({ enumerable: true, get: function () { return FakeHandler_1.FakeHandler; } }); /** * Expose test/fakeParameters utils. */ /* unused reexport */ __webpack_require__(5248); /***/ }), /***/ 296: /***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { "use strict"; // EXPORTS __webpack_require__.d(__webpack_exports__, { A: () => (/* binding */ _slicedToArray) }); ;// ./node_modules/@babel/runtime/helpers/esm/arrayWithHoles.js function _arrayWithHoles(r) { if (Array.isArray(r)) return r; } ;// ./node_modules/@babel/runtime/helpers/esm/iterableToArrayLimit.js function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t["return"] && (u = t["return"](), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } } // EXTERNAL MODULE: ./node_modules/@babel/runtime/helpers/esm/unsupportedIterableToArray.js var unsupportedIterableToArray = __webpack_require__(7800); ;// ./node_modules/@babel/runtime/helpers/esm/nonIterableRest.js function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } ;// ./node_modules/@babel/runtime/helpers/esm/slicedToArray.js function _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || (0,unsupportedIterableToArray/* default */.A)(r, e) || _nonIterableRest(); } /***/ }), /***/ 328: /***/ ((module, __unused_webpack_exports, __webpack_require__) => { // the whatwg-fetch polyfill installs the fetch() function // on the global object (window or self) // // Return that as the export for use in Webpack, Browserify etc. __webpack_require__(8624); module.exports = self.fetch.bind(self); /***/ }), /***/ 388: /***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { "use strict"; // EXPORTS __webpack_require__.d(__webpack_exports__, { A: () => (/* binding */ _possibleConstructorReturn) }); // EXTERNAL MODULE: ./node_modules/@babel/runtime/helpers/esm/typeof.js var esm_typeof = __webpack_require__(2284); ;// ./node_modules/@babel/runtime/helpers/esm/assertThisInitialized.js function _assertThisInitialized(e) { if (void 0 === e) throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); return e; } ;// ./node_modules/@babel/runtime/helpers/esm/possibleConstructorReturn.js function _possibleConstructorReturn(t, e) { if (e && ("object" == (0,esm_typeof/* default */.A)(e) || "function" == typeof e)) return e; if (void 0 !== e) throw new TypeError("Derived constructors may only return object or undefined"); return _assertThisInitialized(t); } /***/ }), /***/ 467: /***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { "use strict"; /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ A: () => (/* binding */ _asyncToGenerator) /* harmony export */ }); function asyncGeneratorStep(n, t, e, r, o, a, c) { try { var i = n[a](c), u = i.value; } catch (n) { return void e(n); } i.done ? t(u) : Promise.resolve(u).then(r, o); } function _asyncToGenerator(n) { return function () { var t = this, e = arguments; return new Promise(function (r, o) { var a = n.apply(t, e); function _next(n) { asyncGeneratorStep(a, r, o, _next, _throw, "next", n); } function _throw(n) { asyncGeneratorStep(a, r, o, _next, _throw, "throw", n); } _next(void 0); }); }; } /***/ }), /***/ 477: /***/ (() => { /* (ignored) */ /***/ }), /***/ 482: /***/ (function(module, exports, __webpack_require__) { ;(function (root, factory, undef) { if (true) { // CommonJS module.exports = exports = factory(__webpack_require__(9021), __webpack_require__(7165)); } else {} }(this, function (CryptoJS) { /** * ISO/IEC 9797-1 Padding Method 2. */ CryptoJS.pad.Iso97971 = { pad: function (data, blockSize) { // Add 0x80 byte data.concat(CryptoJS.lib.WordArray.create([0x80000000], 1)); // Zero pad the rest CryptoJS.pad.ZeroPadding.pad(data, blockSize); }, unpad: function (data) { // Remove zero padding CryptoJS.pad.ZeroPadding.unpad(data); // Remove one more byte -- the 0x80 byte data.sigBytes--; } }; return CryptoJS.pad.Iso97971; })); /***/ }), /***/ 736: /***/ ((module, __unused_webpack_exports, __webpack_require__) => { /** * This is the common logic for both the Node.js and web browser * implementations of `debug()`. */ function setup(env) { createDebug.debug = createDebug; createDebug.default = createDebug; createDebug.coerce = coerce; createDebug.disable = disable; createDebug.enable = enable; createDebug.enabled = enabled; createDebug.humanize = __webpack_require__(6585); createDebug.destroy = destroy; Object.keys(env).forEach(key => { createDebug[key] = env[key]; }); /** * The currently active debug mode names, and names to skip. */ createDebug.names = []; createDebug.skips = []; /** * Map of special "%n" handling functions, for the debug "format" argument. * * Valid key names are a single, lower or upper-case letter, i.e. "n" and "N". */ createDebug.formatters = {}; /** * Selects a color for a debug namespace * @param {String} namespace The namespace string for the debug instance to be colored * @return {Number|String} An ANSI color code for the given namespace * @api private */ function selectColor(namespace) { let hash = 0; for (let i = 0; i < namespace.length; i++) { hash = ((hash << 5) - hash) + namespace.charCodeAt(i); hash |= 0; // Convert to 32bit integer } return createDebug.colors[Math.abs(hash) % createDebug.colors.length]; } createDebug.selectColor = selectColor; /** * Create a debugger with the given `namespace`. * * @param {String} namespace * @return {Function} * @api public */ function createDebug(namespace) { let prevTime; let enableOverride = null; let namespacesCache; let enabledCache; function debug(...args) { // Disabled? if (!debug.enabled) { return; } const self = debug; // Set `diff` timestamp const curr = Number(new Date()); const ms = curr - (prevTime || curr); self.diff = ms; self.prev = prevTime; self.curr = curr; prevTime = curr; args[0] = createDebug.coerce(args[0]); if (typeof args[0] !== 'string') { // Anything else let's inspect with %O args.unshift('%O'); } // Apply any `formatters` transformations let index = 0; args[0] = args[0].replace(/%([a-zA-Z%])/g, (match, format) => { // If we encounter an escaped % then don't increase the array index if (match === '%%') { return '%'; } index++; const formatter = createDebug.formatters[format]; if (typeof formatter === 'function') { const val = args[index]; match = formatter.call(self, val); // Now we need to remove `args[index]` since it's inlined in the `format` args.splice(index, 1); index--; } return match; }); // Apply env-specific formatting (colors, etc.) createDebug.formatArgs.call(self, args); const logFn = self.log || createDebug.log; logFn.apply(self, args); } debug.namespace = namespace; debug.useColors = createDebug.useColors(); debug.color = createDebug.selectColor(namespace); debug.extend = extend; debug.destroy = createDebug.destroy; // XXX Temporary. Will be removed in the next major release. Object.defineProperty(debug, 'enabled', { enumerable: true, configurable: false, get: () => { if (enableOverride !== null) { return enableOverride; } if (namespacesCache !== createDebug.namespaces) { namespacesCache = createDebug.namespaces; enabledCache = createDebug.enabled(namespace); } return enabledCache; }, set: v => { enableOverride = v; } }); // Env-specific initialization logic for debug instances if (typeof createDebug.init === 'function') { createDebug.init(debug); } return debug; } function extend(namespace, delimiter) { const newDebug = createDebug(this.namespace + (typeof delimiter === 'undefined' ? ':' : delimiter) + namespace); newDebug.log = this.log; return newDebug; } /** * Enables a debug mode by namespaces. This can include modes * separated by a colon and wildcards. * * @param {String} namespaces * @api public */ function enable(namespaces) { createDebug.save(namespaces); createDebug.namespaces = namesp