@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
JavaScript
(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