@reactoo/watchtogether-sdk-js 2.7.17 → 2.7.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/*!
|
|
2
2
|
* @reactoo/watchtogether-sdk-js
|
|
3
|
-
* @version 2.7.
|
|
3
|
+
* @version 2.7.18
|
|
4
4
|
*/
|
|
5
5
|
(function webpackUniversalModuleDefinition(root, factory) {
|
|
6
6
|
if(typeof exports === 'object' && typeof module === 'object')
|
|
@@ -10510,7 +10510,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var aws_
|
|
|
10510
10510
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
10511
10511
|
|
|
10512
10512
|
"use strict";
|
|
10513
|
-
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var webrtc_adapter__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! webrtc-adapter */ \"./node_modules/webrtc-adapter/src/js/adapter_core.js\");\n/* harmony import */ var _wt_emitter__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./wt-emitter */ \"./src/modules/wt-emitter.js\");\n/* harmony import */ var _wt_utils__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./wt-utils */ \"./src/modules/wt-utils.js\");\nfunction _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }\nfunction _toPropertyKey(t) { var i = _toPrimitive(t, \"string\"); return \"symbol\" == typeof i ? i : i + \"\"; }\nfunction _toPrimitive(t, r) { if (\"object\" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || \"default\"); if (\"object\" != typeof i) return i; throw new TypeError(\"@@toPrimitive must return a primitive value.\"); } return (\"string\" === r ? String : Number)(t); }\n// Watch together janus webrtc library\n\n\n\n\nclass Room {\n constructor(debug) {\n this.debug = debug;\n this.sessions = [];\n this.safariVp8 = false;\n this.browser = webrtc_adapter__WEBPACK_IMPORTED_MODULE_0__[\"default\"].browserDetails.browser;\n this.browserDetails = webrtc_adapter__WEBPACK_IMPORTED_MODULE_0__[\"default\"].browserDetails;\n this.webrtcSupported = Room.isWebrtcSupported();\n this.safariVp8TestPromise = Room.testSafariVp8();\n this.safariVp8 = null;\n this.safariVp8TestPromise.then(safariVp8 => {\n this.safariVp8 = safariVp8;\n });\n\n // Let's get it started\n this.whenInitialized = this.initialize();\n }\n initialize() {\n return this.safariVp8TestPromise.then(() => this);\n }\n createSession() {\n let constructId = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;\n let type = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'reactooroom';\n let options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};\n return new RoomSession(constructId, type, {\n debug: this.debug,\n ...options\n });\n }\n static testSafariVp8() {\n return new Promise(resolve => {\n if (webrtc_adapter__WEBPACK_IMPORTED_MODULE_0__[\"default\"].browserDetails.browser === 'safari' && webrtc_adapter__WEBPACK_IMPORTED_MODULE_0__[\"default\"].browserDetails.version >= 605) {\n if (RTCRtpSender && RTCRtpSender.getCapabilities && RTCRtpSender.getCapabilities(\"video\") && RTCRtpSender.getCapabilities(\"video\").codecs && RTCRtpSender.getCapabilities(\"video\").codecs.length) {\n var isVp8 = false;\n for (var i in RTCRtpSender.getCapabilities(\"video\").codecs) {\n var codec = RTCRtpSender.getCapabilities(\"video\").codecs[i];\n if (codec && codec.mimeType && codec.mimeType.toLowerCase() === \"video/vp8\") {\n isVp8 = true;\n break;\n }\n }\n resolve(isVp8);\n } else {\n // We do it in a very ugly way, as there's no alternative...\n // We create a PeerConnection to see if VP8 is in an offer\n var testpc = new RTCPeerConnection({}, {});\n testpc.createOffer({\n offerToReceiveVideo: true\n }).then(function (offer) {\n let result = offer.sdp.indexOf(\"VP8\") !== -1;\n testpc.close();\n testpc = null;\n resolve(result);\n });\n }\n } else resolve(false);\n });\n }\n static isWebrtcSupported() {\n return window.RTCPeerConnection !== undefined && window.RTCPeerConnection !== null && navigator.mediaDevices !== undefined && navigator.mediaDevices !== null && navigator.mediaDevices.getUserMedia !== undefined && navigator.mediaDevices.getUserMedia !== null;\n }\n}\nclass RoomSession {\n static noop() {}\n static randomString(len) {\n var charSet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';\n var randomString = '';\n for (var i = 0; i < len; i++) {\n var randomPoz = Math.floor(Math.random() * charSet.length);\n randomString += charSet.substring(randomPoz, randomPoz + 1);\n }\n return randomString;\n }\n constructor() {\n let constructId = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;\n let type = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'reactooroom';\n let options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};\n _defineProperty(this, \"_intercomSubscribe\", stream => {\n var _JSON$parse;\n if (stream.type === 'data') {\n return true;\n }\n const intercomGroups = ((_JSON$parse = JSON.parse(stream.description || \"[]\")) === null || _JSON$parse === void 0 ? void 0 : _JSON$parse.intercomGroups) || [];\n return intercomGroups.some(g => this._listenIntercomChannels.indexOf(g) > -1);\n });\n Object.assign(this, Object(_wt_emitter__WEBPACK_IMPORTED_MODULE_1__[\"default\"])());\n this.options = {\n ...options\n };\n this.defaultDataChannelLabel = 'JanusDataChannel';\n this.server = null;\n this.iceServers = null;\n this.token = null;\n this.roomId = null;\n this.streamId = null;\n this.pin = null;\n this.userId = null;\n this.sessiontype = type;\n this.initialBitrate = 0;\n this.enableDtx = false;\n this.simulcast = false;\n this.defaultSimulcastSettings = {\n \"default\": {\n mode: \"controlled\",\n // controlled, manual, browserControlled\n defaultSubstream: 0,\n // 2 lowest quality, 0 highest quality\n bitrates: [{\n \"rid\": \"l\",\n \"active\": true,\n \"maxBitrate\": 180000,\n \"maxFramerate\": 20,\n \"scaleResolutionDownBy\": 3.3333333333333335,\n \"priority\": \"low\"\n }, {\n \"rid\": \"m\",\n \"active\": true,\n \"maxBitrate\": 500000,\n \"maxFramerate\": 25,\n \"scaleResolutionDownBy\": 1.3333333333333335,\n \"priority\": \"low\"\n }, {\n \"rid\": \"h\",\n \"active\": true,\n \"maxBitrate\": 2000000,\n \"maxFramerate\": 30,\n \"priority\": \"low\"\n }]\n }\n };\n this.recordingFilename = null;\n this.pluginName = RoomSession.sessionTypes[type];\n this.id = null;\n this.privateId = null;\n this.constructId = constructId || RoomSession.randomString(16);\n this.sessionId = null;\n this.handleId = null;\n this.ws = null;\n this.isRestarting = false;\n this.isConnecting = false;\n this.isDisconnecting = false;\n this.isConnected = false;\n this.isPublished = false;\n this.isReclaiming = false;\n this.isStreaming = false;\n this.isMuted = [];\n this.isVideoEnabled = false;\n this.isAudioEnabed = false;\n this._statsMaxLength = 31;\n this._upStatsLength = 30;\n this._downStatsLength = 5;\n this._statsTimeoutStopped = true;\n this._statsTimeoutId = null;\n this._statsInterval = 1000;\n this._aqInterval = 2500;\n this._aqIntervalCounter = 0;\n this._aqIntervalDivisor = 4;\n this._aqTimeoutId = null;\n this._sendMessageTimeout = 5000;\n this._retries = 0;\n this._maxRetries = 5;\n this._keepAliveId = null;\n this._participants = [];\n this._restrictSubscribeToUserIds = []; // all if empty\n this._talkIntercomChannels = ['participants'];\n this._listenIntercomChannels = ['participants'];\n this._roomType = 'watchparty';\n this._isDataChannelOpen = false;\n this._abortController = null;\n this._remoteUsersCache = [];\n this.userRoleSubscriptionRules = {\n ...RoomSession.userRoleSubscriptionRules,\n ...(this.options.userRoleSubscriptionRules || {})\n };\n this._log = RoomSession.noop;\n if (this.options.debug) {\n this._enableDebug();\n }\n }\n _pushToRemoteUsersCache(userId, streams, id) {\n const existingIndex = this._remoteUsersCache.findIndex(u => u.userId === userId);\n if (existingIndex > -1) {\n this._remoteUsersCache.splice(existingIndex, 1, {\n userId,\n streams,\n id\n });\n } else {\n this._remoteUsersCache.push({\n userId,\n streams,\n id\n });\n }\n }\n _removeFromRemoteUsersCache(rfid) {\n const existingIndex = this._remoteUsersCache.findIndex(u => u.id === rfid);\n if (existingIndex > -1) {\n this._remoteUsersCache.splice(existingIndex, 1);\n }\n }\n _participantShouldSubscribe(userId) {\n const myUser = Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"decodeJanusDisplay\"])(this.display);\n const remoteUser = Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"decodeJanusDisplay\"])(userId);\n let localUserRole = (myUser === null || myUser === void 0 ? void 0 : myUser.role) || 'participant';\n let remoteUserRole = (remoteUser === null || remoteUser === void 0 ? void 0 : remoteUser.role) || 'participant';\n return this.userRoleSubscriptionRules[localUserRole][this._roomType || 'watchparty'].indexOf(remoteUserRole) > -1 && (this._restrictSubscribeToUserIds.length === 0 || this._restrictSubscribeToUserIds.indexOf(remoteUser === null || remoteUser === void 0 ? void 0 : remoteUser.userId) > -1);\n }\n _getAddParticipantEventName(handleId) {\n var _decodeJanusDisplay;\n let handle = this._getHandle(handleId);\n if (!handle) {\n this.emit('error', {\n type: 'warning',\n id: 15,\n message: 'id non-existent',\n data: [handleId, 'getParticipantEventName']\n });\n return;\n }\n const participantRole = (_decodeJanusDisplay = Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"decodeJanusDisplay\"])(handle.userId)) === null || _decodeJanusDisplay === void 0 ? void 0 : _decodeJanusDisplay.role;\n switch (participantRole) {\n case 'participant':\n return 'addRemoteParticipant';\n case 'talkback':\n return 'addRemoteTalkback';\n case 'monitor':\n return 'addRemoteTalkback';\n case 'observer':\n case 'observerSolo1':\n case 'observerSolo2':\n case 'observerSolo3':\n return 'addRemoteObserver';\n case 'host':\n return 'addRemoteInstructor';\n case 'companionTV':\n return 'addRemoteCompanionTV';\n case 'companionPhone':\n return 'addRemoteCompanionPhone';\n default:\n return 'addRemoteParticipant';\n }\n }\n _getRemoveParticipantEventName(handleId) {\n var _decodeJanusDisplay2;\n let handle = this._getHandle(handleId);\n if (!handle) {\n this.emit('error', {\n type: 'warning',\n id: 15,\n message: 'id non-existent',\n data: [handleId, 'getParticipantEventName']\n });\n return;\n }\n const participantRole = (_decodeJanusDisplay2 = Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"decodeJanusDisplay\"])(handle.userId)) === null || _decodeJanusDisplay2 === void 0 ? void 0 : _decodeJanusDisplay2.role;\n switch (participantRole) {\n case 'participant':\n return 'removeRemoteParticipant';\n case 'talkback':\n return 'removeRemoteTalkback';\n case 'monitor':\n return 'removeRemoteTalkback';\n case 'observer':\n case 'observerSolo1':\n case 'observerSolo2':\n case 'observerSolo3':\n return 'removeRemoteObserver';\n case 'host':\n return 'removeRemoteInstructor';\n case 'companionTV':\n return 'removeRemoteCompanionTV';\n case 'companionPhone':\n return 'removeRemoteCompanionPhone';\n default:\n return 'removeRemoteParticipant';\n }\n }\n sendMessage(handleId) {\n let message = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {\n body: 'Example Body'\n };\n let dontWait = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;\n let dontResolveOnAck = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;\n let retry = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 0;\n return this._send({\n \"janus\": \"message\",\n \"handle_id\": handleId,\n ...message\n }, dontWait, dontResolveOnAck, retry).then(json => {\n if (json && json[\"janus\"] === \"success\") {\n let plugindata = json[\"plugindata\"] || {};\n let data = plugindata[\"data\"];\n return Promise.resolve(data);\n }\n return Promise.resolve();\n }).catch(json => {\n if (json && json[\"error\"]) {\n return Promise.reject({\n type: 'warning',\n id: 1,\n message: 'sendMessage failed',\n data: json[\"error\"]\n });\n } else {\n return Promise.reject({\n type: 'warning',\n id: 1,\n message: 'sendMessage failed',\n data: json\n });\n }\n });\n }\n _send() {\n let request = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n let ignoreResponse = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;\n let dontResolveOnAck = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;\n let retry = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0;\n let transaction = RoomSession.randomString(12);\n let requestData = {\n ...request,\n transaction,\n token: this.token,\n ...(this.sessionId && {\n 'session_id': this.sessionId\n } || {})\n };\n this._log(requestData);\n const op = () => new Promise((resolve, reject) => {\n let messageTimeoutId = null;\n let abortResponse = () => {\n this._abortController.signal.removeEventListener('abort', abortResponse);\n clearTimeout(messageTimeoutId);\n this.ws.removeEventListener('message', parseResponse);\n reject({\n type: 'warning',\n id: 17,\n message: 'connection cancelled'\n });\n };\n let parseResponse = event => {\n let json = JSON.parse(event.data);\n let r_transaction = json['transaction'];\n if (r_transaction === transaction && (!dontResolveOnAck || json['janus'] !== 'ack')) {\n clearTimeout(messageTimeoutId);\n this._abortController.signal.removeEventListener('abort', abortResponse);\n this.ws.removeEventListener('message', parseResponse);\n if (json['janus'] === 'error') {\n var _json$error;\n if ((json === null || json === void 0 ? void 0 : (_json$error = json.error) === null || _json$error === void 0 ? void 0 : _json$error.code) == 403) {\n this.disconnect(true);\n }\n reject({\n type: 'error',\n id: 2,\n message: 'send failed',\n data: json,\n requestData\n });\n } else {\n resolve(json);\n }\n }\n };\n if (ignoreResponse) {\n if (this.ws && this.ws.readyState === 1) {\n this.ws.send(JSON.stringify(requestData));\n }\n resolve();\n } else {\n if (this.ws && this.ws.readyState === 1) {\n this.ws.addEventListener('message', parseResponse);\n messageTimeoutId = setTimeout(() => {\n this.ws.removeEventListener('message', parseResponse);\n this._abortController.signal.removeEventListener('abort', abortResponse);\n reject({\n type: 'warning',\n id: 3,\n message: 'send timeout',\n data: requestData\n });\n }, this._sendMessageTimeout);\n this._abortController.signal.addEventListener('abort', abortResponse);\n this.ws.send(JSON.stringify(requestData));\n } else {\n reject({\n type: 'warning',\n id: 29,\n message: 'No connection to WebSockets',\n data: requestData\n });\n }\n }\n });\n return op().catch(e => {\n if (e.id === 17) {\n return Promise.reject(e);\n } else if (e.id === 29 && retry > 0) {\n return Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"wait\"])(this._sendMessageTimeout).then(() => this._send(request, ignoreResponse, dontResolveOnAck, retry - 1));\n } else if (retry > 0) {\n return this._send(request, ignoreResponse, dontResolveOnAck, retry - 1);\n } else {\n return Promise.reject(e);\n }\n });\n }\n _connectionClosed() {\n if (!this.isConnected || this.isConnecting || this.isDisconnecting) {\n return;\n }\n if (this._retries < this._maxRetries) {\n setTimeout(() => {\n this._retries++;\n this._reconnect().catch(e => {\n this.emit('error', e);\n });\n }, 3000 * this._retries);\n } else {\n if (this.sessiontype === 'reactooroom') {\n this.disconnect(true);\n } else if (this.sessiontype === 'streaming') {\n this.stopStream();\n }\n this.emit('error', {\n type: 'error',\n id: 4,\n message: 'Lost connection to WebSockets',\n data: null\n });\n }\n }\n _wipeListeners() {\n if (this.ws) {\n this.ws.removeEventListener('close', this.__connectionClosedBoundFn);\n this.ws.removeEventListener('message', this.__handleWsEventsBoundFn);\n }\n }\n _startKeepAlive() {\n this._send({\n \"janus\": \"keepalive\"\n }).then(json => {\n if (json[\"janus\"] !== 'ack') {\n this.emit('error', {\n type: 'warning',\n id: 5,\n message: 'keepalive response suspicious',\n data: json[\"janus\"]\n });\n }\n }).catch(e => {\n this.emit('error', {\n type: 'warning',\n id: 6,\n message: 'keepalive dead',\n data: e\n });\n this._connectionClosed();\n });\n this._keepAliveId = setTimeout(() => {\n this._startKeepAlive();\n }, 10000);\n }\n _stopKeepAlive() {\n clearTimeout(this._keepAliveId);\n }\n _handleWsEvents(event) {\n let json = JSON.parse(event.data);\n var sender = json[\"sender\"];\n var type = json[\"janus\"];\n let handle = this._getHandle(sender);\n if (!handle) {\n return;\n }\n if (type === \"trickle\") {\n let candidate = json[\"candidate\"];\n let config = handle.webrtcStuff;\n if (config.pc && config.remoteSdp) {\n if (!candidate || candidate.completed === true) {\n config.pc.addIceCandidate(null);\n } else {\n config.pc.addIceCandidate(candidate);\n }\n } else {\n if (!config.candidates) {\n config.candidates = [];\n }\n config.candidates.push(candidate);\n }\n } else if (type === \"webrtcup\") {\n //none universal\n } else if (type === \"hangup\") {\n this._log('hangup on', handle.handleId, handle.handleId === this.handleId, json);\n this._removeParticipant(handle.handleId, null, false);\n } else if (type === \"detached\") {\n this._log('detached on', handle.handleId, handle.handleId === this.handleId, json);\n this._removeParticipant(handle.handleId, null, true);\n } else if (type === \"media\") {\n this._log('Media event:', handle.handleId, json[\"type\"], json[\"receiving\"], json[\"mid\"]);\n } else if (type === \"slowlink\") {\n this._log('Slowlink', handle.handleId, json[\"uplink\"], json[\"lost\"], json[\"mid\"]);\n } else if (type === \"event\") {\n //none universal\n } else if (type === 'timeout') {\n this._log('WebSockets Gateway timeout', json);\n this.ws.close(3504, \"Gateway timeout\");\n } else if (type === 'success' || type === 'error') {\n // we're capturing those elsewhere\n } else {\n this._log(`Unknown event: ${type} on session: ${this.sessionId}`);\n }\n\n // LOCAL\n\n if (sender === this.handleId) {\n if (type === \"event\") {\n var plugindata = json[\"plugindata\"] || {};\n var msg = plugindata[\"data\"] || {};\n var jsep = json[\"jsep\"];\n let result = msg[\"result\"] || null;\n let event = msg[\"videoroom\"] || null;\n let list = msg[\"publishers\"] || {};\n let leaving = msg[\"leaving\"];\n let kicked = msg[\"kicked\"];\n let substream = msg[\"substream\"];\n let temporal = msg[\"temporal\"];\n\n //let joining = msg[\"joining\"];\n let unpublished = msg[\"unpublished\"];\n let error = msg[\"error\"];\n if (event === \"joined\") {\n var _decodeJanusDisplay3, _decodeJanusDisplay4;\n this.id = msg[\"id\"];\n this.privateId = msg[\"private_id\"];\n this.isConnected = true;\n this._log('We have successfully joined Room');\n this.emit('joined', true, this.constructId);\n this.emit('addLocalParticipant', {\n tid: Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"generateUUID\"])(),\n id: handle.handleId,\n constructId: this.constructId,\n userId: (_decodeJanusDisplay3 = Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"decodeJanusDisplay\"])(handle.userId)) === null || _decodeJanusDisplay3 === void 0 ? void 0 : _decodeJanusDisplay3.userId,\n role: (_decodeJanusDisplay4 = Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"decodeJanusDisplay\"])(this.display)) === null || _decodeJanusDisplay4 === void 0 ? void 0 : _decodeJanusDisplay4.role,\n track: null,\n stream: null,\n streamMap: {},\n source: null,\n adding: false,\n removing: false,\n hasAudioTrack: false,\n hasVideoTrack: false\n });\n for (let f in list) {\n let userId = list[f][\"display\"];\n let streams = list[f][\"streams\"] || [];\n let id = list[f][\"id\"];\n for (let i in streams) {\n streams[i][\"id\"] = id;\n streams[i][\"display\"] = userId;\n }\n this._log('Remote userId: ', userId);\n this._pushToRemoteUsersCache(userId, streams, id);\n if (this._participantShouldSubscribe(userId)) {\n this._log('Creating user: ', userId);\n this._createParticipant(userId, id).then(handle => {\n this._updateParticipantsTrackData(handle.handleId, streams);\n const subscribe = streams.filter(s => !s.disabled && this._intercomSubscribe(s)).map(stream => ({\n feed: stream.id,\n mid: stream.mid\n }));\n handle.webrtcStuff.subscribeMap = structuredClone(subscribe);\n return this.sendMessage(handle.handleId, {\n body: {\n \"request\": \"join\",\n \"room\": this.roomId,\n \"ptype\": \"subscriber\",\n \"private_id\": this.privateId,\n streams: subscribe,\n //\"feed\": id,\n ...(this.webrtcVersion > 1000 ? {\n id: this.userId\n } : {}),\n pin: this.pin\n }\n });\n }).catch(err => {\n this.emit('error', err);\n });\n }\n }\n } else if (event === \"event\") {\n if (substream !== undefined && substream !== null) {\n this._log('Substream event:', substream, sender);\n }\n if (temporal !== undefined && temporal !== null) {\n this._log('Temporal event:', temporal);\n }\n if (msg[\"streams\"] !== undefined && msg[\"streams\"] !== null) {\n this._log('Got my own streams back', msg[\"streams\"]);\n }\n for (let f in list) {\n let userId = list[f][\"display\"];\n let streams = list[f][\"streams\"] || [];\n let id = list[f][\"id\"];\n for (let i in streams) {\n streams[i][\"id\"] = id;\n streams[i][\"display\"] = userId;\n }\n this._log('Remote userId: ', userId);\n this._pushToRemoteUsersCache(userId, streams, id);\n if (this._participantShouldSubscribe(userId)) {\n let handle = this._getHandle(null, id);\n if (handle) {\n this._updateParticipantsTrackData(handle.handleId, streams);\n let subscribe = streams.filter(stream => !stream.disabled && this._intercomSubscribe(stream) && !this._isAlreadySubscribed(handle.handleId, stream.id, stream.mid)).map(s => ({\n feed: s.id,\n mid: s.mid\n }));\n let unsubscribe = streams.filter(stream => stream.disabled || !this._intercomSubscribe(stream)).map(s => ({\n feed: s.id,\n mid: s.mid\n }));\n this._updateSubscribeMap(handle.handleId, subscribe, unsubscribe);\n this._log('Already subscribed to user: ', userId, 'Update streams', subscribe, unsubscribe);\n if (subscribe.length || unsubscribe.length) {\n this.sendMessage(handle.handleId, {\n body: {\n \"request\": \"update\",\n ...(subscribe.length ? {\n subscribe\n } : {}),\n ...(unsubscribe.length ? {\n unsubscribe\n } : {})\n }\n });\n }\n } else {\n this._log('Creating user: ', userId, streams);\n this._createParticipant(userId, id).then(handle => {\n this._updateParticipantsTrackData(handle.handleId, streams);\n const subscribe = streams.filter(s => !s.disabled && this._intercomSubscribe(s)).map(stream => ({\n feed: stream.id,\n mid: stream.mid\n }));\n handle.webrtcStuff.subscribeMap = structuredClone(subscribe);\n return this.sendMessage(handle.handleId, {\n body: {\n \"request\": \"join\",\n \"room\": this.roomId,\n \"ptype\": \"subscriber\",\n \"private_id\": this.privateId,\n streams: subscribe,\n ...(this.webrtcVersion > 1000 ? {\n id: this.userId\n } : {}),\n pin: this.pin\n }\n });\n }).catch(err => {\n this.emit('error', err);\n });\n }\n }\n }\n if (leaving === 'ok') {\n this._log('leaving', this.handleId, 'this is us');\n this._removeParticipant(this.handleId);\n if (msg['reason'] === 'kicked') {\n this.emit('kicked');\n this.disconnect().catch(() => {});\n }\n } else if (leaving) {\n //TODO: in 1 PeerConnection case we only unsubscribe from streams, this may not be true, check janus docs\n this._log('leaving', leaving);\n this._removeFromRemoteUsersCache(leaving);\n this._removeParticipant(null, leaving, true);\n }\n if (unpublished === 'ok') {\n this._log('unpublished', this.handleId, 'this is us');\n this._removeParticipant(this.handleId, null, false); // we do just hangup\n } else if (unpublished) {\n //TODO: in 1 PeerConnection case we only unsubscribe from streams\n this._log('unpublished', unpublished);\n this._removeFromRemoteUsersCache(unpublished);\n this._removeParticipant(null, unpublished, true); // we do hangup and detach\n }\n if (kicked === 'ok') {\n // this case shouldn't exist\n } else if (kicked) {\n this._log('kicked', kicked);\n this._removeFromRemoteUsersCache(kicked);\n this._removeParticipant(null, kicked, true); // we do hangup and detach\n }\n if (error) {\n this.emit('error', {\n type: 'error',\n id: 7,\n message: 'local participant error',\n data: [sender, msg]\n });\n }\n }\n\n // Streaming related\n else if (result && result[\"status\"]) {\n this.emit('streamingStatus', result[\"status\"]);\n if (result[\"status\"] === 'stopped') {\n this.stopStream();\n }\n if (result[\"status\"] === 'started') {\n this.emit('streaming', true);\n this.isStreaming = true;\n }\n }\n if (jsep !== undefined && jsep !== null) {\n if (this.sessiontype === 'reactooroom') {\n this._webrtcPeer(this.handleId, jsep).catch(err => {\n this.emit('error', err);\n });\n } else if (this.sessiontype === 'streaming') {\n this._publishRemote(this.handleId, jsep).catch(err => {\n this.emit('error', err);\n });\n }\n }\n } else if (type === \"webrtcup\") {\n if (this.simulcast) {\n return;\n }\n this._log('Configuring bitrate: ' + this.initialBitrate);\n if (this.initialBitrate > 0) {\n this.sendMessage(this.handleId, {\n \"body\": {\n \"request\": \"configure\",\n \"bitrate\": this.initialBitrate\n }\n }).catch(() => null);\n }\n }\n }\n\n //REMOTE\n else {\n let plugindata = json[\"plugindata\"] || {};\n let msg = plugindata[\"data\"] || {};\n let jsep = json[\"jsep\"];\n let event = msg[\"videoroom\"];\n let error = msg[\"error\"];\n let substream = msg[\"substream\"];\n let mid = msg[\"mid\"];\n let temporal = msg[\"temporal\"];\n if (substream !== undefined && substream !== null) {\n this._log('Substream: ', sender, mid, substream);\n this._setSelectedSubstream(sender, mid, substream);\n this._resetStats(sender, mid);\n this.requestKeyFrame(sender, mid);\n }\n if (temporal !== undefined && temporal !== null) {\n this._log('Temporal: ', temporal);\n }\n if (type === \"webrtcup\") {\n this.requestKeyFrame(handle.handleId);\n }\n if (event === \"updated\") {\n this._log('Remote has updated tracks', msg);\n if (msg[\"streams\"]) {\n this._updateTransceiverMap(handle.handleId, msg[\"streams\"]);\n }\n }\n if (event === \"attached\") {\n var _decodeJanusDisplay5, _decodeJanusDisplay6, _handle$webrtcStuff, _handle$webrtcStuff2, _handle$webrtcStuff3;\n this._log('Remote have successfully joined Room', msg);\n if (msg[\"streams\"]) {\n this._updateTransceiverMap(handle.handleId, msg[\"streams\"] || []);\n }\n this.emit(this._getAddParticipantEventName(handle.handleId), {\n tid: Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"generateUUID\"])(),\n id: handle.handleId,\n userId: (_decodeJanusDisplay5 = Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"decodeJanusDisplay\"])(handle.userId)) === null || _decodeJanusDisplay5 === void 0 ? void 0 : _decodeJanusDisplay5.userId,\n role: (_decodeJanusDisplay6 = Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"decodeJanusDisplay\"])(handle.userId)) === null || _decodeJanusDisplay6 === void 0 ? void 0 : _decodeJanusDisplay6.role,\n stream: null,\n streamMap: structuredClone((_handle$webrtcStuff = handle.webrtcStuff) === null || _handle$webrtcStuff === void 0 ? void 0 : _handle$webrtcStuff.streamMap),\n tracksMap: structuredClone((_handle$webrtcStuff2 = handle.webrtcStuff) === null || _handle$webrtcStuff2 === void 0 ? void 0 : _handle$webrtcStuff2.tracksMap),\n transceiverMap: structuredClone((_handle$webrtcStuff3 = handle.webrtcStuff) === null || _handle$webrtcStuff3 === void 0 ? void 0 : _handle$webrtcStuff3.transceiverMap),\n source: null,\n track: null,\n adding: false,\n removing: false,\n constructId: this.constructId,\n hasAudioTrack: false,\n hasVideoTrack: false\n });\n }\n if (error) {\n this.emit('error', {\n type: 'warning',\n id: 8,\n message: 'remote participant error',\n data: [sender, msg]\n });\n }\n if (jsep) {\n this._publishRemote(handle.handleId, jsep).catch(err => {\n this.emit('error', err);\n });\n }\n }\n }\n _handleDataEvents(handleId, type, data) {\n let handle = this._getHandle(handleId);\n if (type === 'state') {\n this._log(` - Data channel status - `, `UID: ${handleId}`, `STATUS: ${JSON.stringify(data)}`, `ME: ${handleId === this.handleId}`);\n if (handle) {\n let config = handle.webrtcStuff;\n config.dataChannelOpen = this.defaultDataChannelLabel === (data === null || data === void 0 ? void 0 : data.label) && (data === null || data === void 0 ? void 0 : data.state) === 'open';\n }\n if (handleId === this.handleId && this.defaultDataChannelLabel === (data === null || data === void 0 ? void 0 : data.label)) {\n this._isDataChannelOpen = (data === null || data === void 0 ? void 0 : data.state) === 'open';\n this.emit('dataChannel', (data === null || data === void 0 ? void 0 : data.state) === 'open');\n }\n }\n if (type === 'error') {\n this.emit('error', {\n type: 'warning',\n id: 9,\n message: 'data event warning',\n data: [handleId, data]\n });\n if (handle) {\n let config = handle.webrtcStuff;\n if (this.defaultDataChannelLabel === data.label) {\n config.dataChannelOpen = false;\n }\n }\n if (handleId === this.handleId && this.defaultDataChannelLabel === data.label) {\n this._isDataChannelOpen = false;\n this.emit('dataChannel', false);\n }\n }\n if (handleId === this.handleId && type === 'message') {\n let d = null;\n try {\n d = JSON.parse(data);\n } catch (e) {\n this.emit('error', {\n type: 'warning',\n id: 10,\n message: 'data message parse error',\n data: [handleId, e]\n });\n return;\n }\n this.emit('data', d);\n }\n }\n\n //removeHandle === true -> hangup, detach, removeHandle === false -> hangup\n _removeParticipant(handleId, rfid) {\n let removeHandle = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;\n let handle = this._getHandle(handleId, rfid);\n if (!handle) {\n return Promise.resolve();\n } else {\n handleId = handle.handleId;\n }\n return this._send({\n \"janus\": \"hangup\",\n \"handle_id\": handleId\n }, true).then(() => removeHandle ? this._send({\n \"janus\": \"detach\",\n \"handle_id\": handleId\n }, true) : Promise.resolve()).finally(() => {\n try {\n if (handle.webrtcStuff.stream) {\n if (!this.isRestarting) {\n var _handle$webrtcStuff$s;\n (_handle$webrtcStuff$s = handle.webrtcStuff.stream) === null || _handle$webrtcStuff$s === void 0 ? void 0 : _handle$webrtcStuff$s.getTracks().forEach(track => track.stop());\n } else {\n var _handle$webrtcStuff$s2;\n (_handle$webrtcStuff$s2 = handle.webrtcStuff.stream) === null || _handle$webrtcStuff$s2 === void 0 ? void 0 : _handle$webrtcStuff$s2.getTracks().forEach(track => track.onended = null);\n }\n }\n } catch (e) {\n // Do nothing\n }\n if (handle.webrtcStuff.stream) {\n handle.webrtcStuff.stream.onremovetrack = null;\n handle.webrtcStuff.stream = null;\n }\n if (handle.webrtcStuff.dataChannel) {\n Object.keys(handle.webrtcStuff.dataChannel).forEach(label => {\n handle.webrtcStuff.dataChannel[label].onmessage = null;\n handle.webrtcStuff.dataChannel[label].onopen = null;\n handle.webrtcStuff.dataChannel[label].onclose = null;\n handle.webrtcStuff.dataChannel[label].onerror = null;\n });\n }\n if (handle.webrtcStuff.pc) {\n handle.webrtcStuff.pc.onicecandidate = null;\n handle.webrtcStuff.pc.ontrack = null;\n handle.webrtcStuff.pc.ondatachannel = null;\n handle.webrtcStuff.pc.onconnectionstatechange = null;\n handle.webrtcStuff.pc.oniceconnectionstatechange = null;\n }\n try {\n handle.webrtcStuff.pc.close();\n } catch (e) {}\n handle.webrtcStuff = {\n stream: null,\n streamMap: {},\n tracksMap: [],\n transceiverMap: [],\n subscribeMap: [],\n mySdp: null,\n mediaConstraints: null,\n pc: null,\n dataChannelOpen: false,\n dataChannel: null,\n dtmfSender: null,\n trickle: true,\n iceDone: false,\n isIceRestarting: false,\n stats: {},\n selectedSubstream: {},\n initialSimulcastSubstreamBeenSet: {},\n overriddenSimulcastMode: {}\n };\n if (handleId === this.handleId) {\n var _decodeJanusDisplay7, _decodeJanusDisplay8;\n this._isDataChannelOpen = false;\n this.isPublished = false;\n this.emit('published', {\n status: false,\n hasStream: false\n });\n this.emit('removeLocalParticipant', {\n id: handleId,\n userId: (_decodeJanusDisplay7 = Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"decodeJanusDisplay\"])(handle.userId)) === null || _decodeJanusDisplay7 === void 0 ? void 0 : _decodeJanusDisplay7.userId,\n role: (_decodeJanusDisplay8 = Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"decodeJanusDisplay\"])(handle.userId)) === null || _decodeJanusDisplay8 === void 0 ? void 0 : _decodeJanusDisplay8.role\n });\n } else {\n var _decodeJanusDisplay9, _decodeJanusDisplay10;\n this.emit(this._getRemoveParticipantEventName(handleId), {\n id: handleId,\n userId: (_decodeJanusDisplay9 = Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"decodeJanusDisplay\"])(handle.userId)) === null || _decodeJanusDisplay9 === void 0 ? void 0 : _decodeJanusDisplay9.userId,\n role: (_decodeJanusDisplay10 = Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"decodeJanusDisplay\"])(handle.userId)) === null || _decodeJanusDisplay10 === void 0 ? void 0 : _decodeJanusDisplay10.role\n });\n }\n if (removeHandle) {\n let handleIndex = this._participants.findIndex(p => p.handleId === handleId);\n this._participants.splice(handleIndex, 1);\n }\n return true;\n });\n }\n _createParticipant() {\n let userId = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;\n let rfid = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;\n return this._send({\n \"janus\": \"attach\",\n \"plugin\": this.pluginName\n }).then(json => {\n let handleId = json.data[\"id\"];\n let handle = {\n handleId,\n rfid,\n userId,\n webrtcStuff: {\n stream: null,\n streamMap: {},\n tracksMap: [],\n transceiverMap: [],\n subscribeMap: [],\n mySdp: null,\n mediaConstraints: null,\n pc: null,\n dataChannelOpen: false,\n dataChannel: null,\n dtmfSender: null,\n trickle: true,\n iceDone: false,\n isIceRestarting: false,\n stats: {},\n selectedSubstream: {},\n initialSimulcastSubstreamBeenSet: {},\n overriddenSimulcastMode: {}\n }\n };\n this._participants.push(handle);\n return handle;\n });\n }\n _updateSubscribeMap(handleId, subscribe, unsubscribe) {\n let handle = this._getHandle(handleId);\n if (!handle) {\n this.emit('error', {\n type: 'warning',\n id: 15,\n message: 'id non-existent',\n data: [handleId, 'updateSubscribeMap']\n });\n return;\n }\n let currentSubscribeMap = handle.webrtcStuff.subscribeMap;\n subscribe.forEach(s => {\n if (!currentSubscribeMap.find(c => c.feed === s.feed && c.mid === s.mid)) {\n currentSubscribeMap.push(s);\n }\n });\n unsubscribe.forEach(s => {\n let index = currentSubscribeMap.findIndex(c => c.feed === s.feed && c.mid === s.mid);\n if (index > -1) {\n currentSubscribeMap.splice(index, 1);\n }\n });\n }\n _isAlreadySubscribed(handleId, feed, mid) {\n let handle = this._getHandle(handleId);\n if (!handle) {\n this.emit('error', {\n type: 'warning',\n id: 15,\n message: 'id non-existent',\n data: [handleId, 'isAlreadySubscribed']\n });\n return false;\n }\n return handle.webrtcStuff.subscribeMap.findIndex(t => t.mid === mid && t.feed === feed) > -1;\n }\n _updateTransceiverMap(handleId, streams) {\n this._log('Updating current transceiver map', handleId, streams);\n let handle = this._getHandle(handleId);\n if (!handle) {\n this.emit('error', {\n type: 'warning',\n id: 15,\n message: 'id non-existent',\n data: [handleId, 'updateTransceiverMap']\n });\n return;\n }\n let config = handle.webrtcStuff;\n config.transceiverMap = structuredClone(streams);\n }\n _updateParticipantsTrackData(handleId, streams) {\n this._log('Updating participants track data', handleId, streams);\n let handle = this._getHandle(handleId);\n if (!handle) {\n this.emit('error', {\n type: 'warning',\n id: 15,\n message: 'id non-existent',\n data: [handleId, 'updateParticipantsTrackData']\n });\n return;\n }\n let config = handle.webrtcStuff;\n config.tracksMap = structuredClone(streams.map(s => {\n let source = null;\n let simulcastBitrates = null;\n try {\n const description = JSON.parse(s.description);\n source = description === null || description === void 0 ? void 0 : description.source;\n simulcastBitrates = description === null || description === void 0 ? void 0 : description.simulcastBitrates;\n } catch (e) {}\n return {\n active: !s.disabled,\n description: s.description,\n source: source,\n simulcastBitrates: simulcastBitrates,\n display: s.display,\n id: s.id,\n mid: s.mid,\n mindex: s.mindex,\n codec: s.codec,\n type: s.type\n };\n }));\n }\n _updateRemoteParticipantStreamMap(handleId) {\n this._log('Updating participants stream map', handleId);\n let handle = this._getHandle(handleId);\n if (!handle) {\n this.emit('error', {\n type: 'warning',\n id: 15,\n message: 'id non-existent',\n data: [handleId, 'updateRemoteParticipantStreamMap']\n });\n return;\n }\n let config = handle.webrtcStuff;\n config.streamMap = {};\n config.transceiverMap.forEach(tItem => {\n var _JSON$parse2, _config$pc$getTransce, _config$pc$getTransce2, _config$pc$getTransce3;\n if (tItem.type === 'data') {\n return;\n }\n if (tItem.active === false) {\n return;\n }\n const source = (_JSON$parse2 = JSON.parse(tItem.feed_description)) === null || _JSON$parse2 === void 0 ? void 0 : _JSON$parse2.source;\n if (!config.streamMap[source]) {\n config.streamMap[source] = [];\n }\n let trackId = (_config$pc$getTransce = config.pc.getTransceivers().find(t => t.mid === tItem.mid)) === null || _config$pc$getTransce === void 0 ? void 0 : (_config$pc$getTransce2 = _config$pc$getTransce.receiver) === null || _config$pc$getTransce2 === void 0 ? void 0 : (_config$pc$getTransce3 = _config$pc$getTransce2.track) === null || _config$pc$getTransce3 === void 0 ? void 0 : _config$pc$getTransce3.id;\n if (trackId) {\n config.streamMap[source].push(trackId);\n }\n });\n }\n _joinRoom(roomId, pin, userId, display) {\n return this.sendMessage(this.handleId, {\n body: {\n \"request\": \"join\",\n \"room\": roomId,\n \"pin\": pin,\n \"ptype\": \"publisher\",\n \"display\": display,\n ...(this.webrtcVersion > 1000 ? {\n id: userId\n } : {})\n }\n }, false, true);\n }\n _watchStream(id) {\n return this.sendMessage(this.handleId, {\n body: {\n \"request\": \"watch\",\n id: String(id)\n }\n }, false, true);\n }\n _leaveRoom() {\n let dontWait = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;\n return this.isConnected ? this.sendMessage(this.handleId, {\n body: {\n \"request\": \"leave\"\n }\n }, dontWait).finally(() => {\n this.isConnected = false;\n this.emit('joined', false);\n }) : Promise.resolve();\n }\n\n // internal reconnect\n\n _reconnect() {\n if (this.isReclaiming) {\n return Promise.resolve();\n }\n if (this.ws) {\n this._wipeListeners();\n if (this.ws.readyState === 1) {\n this.ws.close();\n }\n }\n this._stopKeepAlive();\n this.isReclaiming = true;\n this.emit('joining', true);\n return new Promise((resolve, reject) => {\n let abortReconnect = () => {\n this._abortController.signal.removeEventListener('abort', abortReconnect);\n this.ws.removeEventListener('close', this.__connectionClosedBoundFn);\n this.ws.removeEventListener('message', this.__handleWsEventsBoundFn);\n this.ws.onopen = null;\n this.ws.onerror = null;\n this.isReclaiming = false;\n this.emit('joining', false);\n reject({\n type: 'warning',\n id: 17,\n message: 'Connection cancelled',\n data: e\n });\n };\n this.ws = new WebSocket(this.server, 'janus-protocol');\n this.__connectionClosedBoundFn = this._connectionClosed.bind(this);\n this.__handleWsEventsBoundFn = this._handleWsEvents.bind(this);\n this.ws.addEventListener('close', this.__connectionClosedBoundFn);\n this.ws.addEventListener('message', this.__handleWsEventsBoundFn);\n this.ws.onopen = () => {\n this._abortController.signal.removeEventListener('abort', abortReconnect);\n this._send({\n \"janus\": \"claim\"\n }).then(json => {\n this.sessionId = json[\"session_id\"] ? json[\"session_id\"] : json.data[\"id\"];\n this._startKeepAlive();\n this.isReclaiming = false;\n this.emit('joining', false);\n this._retries = 0;\n resolve(json);\n }).catch(error => {\n this.isReclaiming = false;\n this.emit('joining', false);\n reject({\n type: 'error',\n id: 11,\n message: 'reconnection error',\n data: error\n });\n });\n };\n\n // this is called before 'close' event callback so it doesn't break reconnect loop\n this.ws.onerror = e => {\n this._abortController.signal.removeEventListener('abort', abortReconnect);\n this.isReclaiming = false;\n this.emit('joining', false);\n reject({\n type: 'warning',\n id: 12,\n message: 'ws reconnection error',\n data: e\n });\n };\n this._abortController.signal.addEventListener('abort', abortReconnect);\n });\n }\n connect(roomId, pin, server, iceServers, token, display, userId) {\n let webrtcVersion = arguments.length > 7 && arguments[7] !== undefined ? arguments[7] : 0;\n let initialBitrate = arguments.length > 8 && arguments[8] !== undefined ? arguments[8] : 0;\n let recordingFilename = arguments.length > 9 ? arguments[9] : undefined;\n let simulcast = arguments.length > 10 && arguments[10] !== undefined ? arguments[10] : false;\n let simulcastSettings = arguments.length > 11 && arguments[11] !== undefined ? arguments[11] : this.defaultSimulcastSettings;\n let enableDtx = arguments.length > 12 && arguments[12] !== undefined ? arguments[12] : false;\n if (this.isConnecting) {\n return Promise.reject({\n type: 'warning',\n id: 16,\n message: 'connection already in progress'\n });\n }\n if (this.ws) {\n this._wipeListeners();\n }\n this._stopKeepAlive();\n this._abortController = new AbortController();\n this.sessionId = null;\n this.server = server;\n this.iceServers = iceServers;\n this.token = token;\n this.roomId = roomId;\n this.pin = pin;\n this.display = display;\n this.userId = userId;\n this.webrtcVersion = webrtcVersion;\n this.initialBitrate = initialBitrate;\n this.recordingFilename = recordingFilename;\n this.isConnecting = true;\n this.enableDtx = enableDtx;\n this.simulcast = simulcast;\n this.simulcastSettings = structuredClone(simulcastSettings);\n\n // sort simulcast bitrates\n if (this.simulcastSettings && typeof this.simulcastSettings === 'object' && Object.keys(this.simulcastSettings).length) {\n Object.keys(this.simulcastSettings).forEach(k => {\n this.simulcastSettings[k].bitrates = this.simulcastSettings[k].bitrates.sort((a, b) => {\n if (a.maxBitrate === b.maxBitrate) {\n return a.maxFramerate - b.maxFramerate;\n }\n return a.maxBitrate - b.maxBitrate;\n });\n });\n }\n this.emit('joining', true);\n return new Promise((resolve, reject) => {\n this.ws = new WebSocket(this.server, 'janus-protocol');\n this.__connectionClosedBoundFn = this._connectionClosed.bind(this);\n this.__handleWsEventsBoundFn = this._handleWsEvents.bind(this);\n let abortConnect = () => {\n this._abortController.signal.removeEventListener('abort', abortConnect);\n this.ws.removeEventListener('close', this.__connectionClosedBoundFn);\n this.ws.removeEventListener('message', this.__handleWsEventsBoundFn);\n this.ws.onopen = null;\n this.ws.onerror = null;\n this.isConnecting = false;\n this.emit('joining', false);\n reject({\n type: 'warning',\n id: 17,\n message: 'Connection cancelled'\n });\n };\n this.ws.addEventListener('close', this.__connectionClosedBoundFn);\n this.ws.addEventListener('message', this.__handleWsEventsBoundFn);\n this.ws.onopen = () => {\n this._abortController.signal.removeEventListener('abort', abortConnect);\n this._retries = 0;\n this._send({\n \"janus\": \"create\"\n }).then(json => {\n this.sessionId = json[\"session_id\"] ? json[\"session_id\"] : json.data[\"id\"];\n this._startKeepAlive();\n return 1;\n }).then(() => this._createParticipant(userId)).then(handle => {\n this.handleId = handle.handleId;\n return 1;\n }).then(() => this._joinRoom(roomId, pin, userId, display)).then(() => {\n this._enableStatsWatch();\n this._enableSubstreamAutoSelect();\n this.isConnecting = false;\n this.emit('joining', false);\n resolve(this);\n }).catch(error => {\n this.isConnecting = false;\n this.emit('joining', false);\n reject({\n type: (error === null || error === void 0 ? void 0 : error.type) === 'warning' ? 'warning' : 'error',\n id: 13,\n message: 'connection error',\n data: error\n });\n });\n };\n this.ws.onerror = e => {\n this._abortController.signal.removeEventListener('abort', abortConnect);\n this.isConnecting = false;\n this.emit('joining', false);\n reject({\n type: 'error',\n id: 14,\n message: 'ws connection error',\n data: e\n });\n };\n this._abortController.signal.addEventListener('abort', abortConnect);\n });\n }\n disconnect() {\n var _this$_abortControlle, _this$_abortControlle2;\n if (this.isDisconnecting) {\n return Promise.resolve();\n }\n (_this$_abortControlle = this._abortController) === null || _this$_abortControlle === void 0 ? void 0 : (_this$_abortControlle2 = _this$_abortControlle.abort) === null || _this$_abortControlle2 === void 0 ? void 0 : _this$_abortControlle2.call(_this$_abortControlle);\n this.isDisconnecting = true;\n this._stopKeepAlive();\n this._disableStatsWatch();\n this._disableSubstreamAutoSelect();\n let isConnected = this.isConnected;\n return Promise.all(this._participants.map(p => this._removeParticipant(p.handleId))).finally(() => {\n this._wipeListeners();\n if (this.ws && this.ws.readyState === 1) {\n this._send({\n \"janus\": \"destroy\"\n }, true);\n this.ws.close();\n }\n this.sessionId = null;\n this.isPublished = false;\n this.isConnected = false;\n this.isDisconnecting = false;\n this.emit('publishing', false);\n this.emit('published', {\n status: false,\n hasStream: false\n });\n this.emit('joining', false);\n this.emit('joined', false);\n this.emit('disconnect', isConnected);\n return Promise.resolve('Disconnected');\n });\n }\n startStream(streamId, server, iceServers, token, userId) {\n if (this.isConnecting) {\n return Promise.reject({\n type: 'warning',\n id: 16,\n message: 'connection error',\n data: 'Connection is in progress'\n });\n }\n if (this.ws) {\n this._wipeListeners();\n }\n this._stopKeepAlive();\n this._abortController = new AbortController();\n this.server = server;\n this.iceServers = iceServers;\n this.token = token;\n this.streamId = streamId;\n this.userId = userId;\n this.sessionId = null;\n this.isConnecting = true;\n this.emit('streamStarting', true);\n return new Promise((resolve, reject) => {\n this.ws = new WebSocket(this.server, 'janus-protocol');\n this.__connectionClosedBoundFn = this._connectionClosed.bind(this);\n this.__handleWsEventsBoundFn = this._handleWsEvents.bind(this);\n let abortConnect = () => {\n this._abortController.signal.removeEventListener('abort', abortConnect);\n this.ws.removeEventListener('close', this.__connectionClosedBoundFn);\n this.ws.removeEventListener('message', this.__handleWsEventsBoundFn);\n this.ws.onopen = null;\n this.ws.onerror = null;\n this.isConnecting = false;\n this.emit('streamStarting', false);\n reject({\n type: 'warning',\n id: 17,\n message: 'Connection cancelled'\n });\n };\n this.ws.addEventListener('close', this.__connectionClosedBoundFn);\n this.ws.addEventListener('message', this.__handleWsEventsBoundFn);\n this.ws.onopen = () => {\n this._abortController.signal.removeEventListener('abort', abortConnect);\n this._retries = 0;\n this._send({\n \"janus\": \"create\"\n }).then(json => {\n this.sessionId = json[\"session_id\"] ? json[\"session_id\"] : json.data[\"id\"];\n this._startKeepAlive();\n return 1;\n }).then(() => this._createParticipant(userId)).then(handle => {\n this.handleId = handle.handleId;\n return 1;\n }).then(() => this._watchStream(streamId)).then(() => {\n this.isConnecting = false;\n this.emit('streamStarting', false);\n resolve(this);\n }).catch(error => {\n this.isConnecting = false;\n this.emit('streamStarting', false);\n reject({\n type: 'error',\n id: 13,\n message: 'connection error',\n data: error\n });\n });\n };\n this.ws.onerror = e => {\n this._abortController.signal.removeEventListener('abort', abortConnect);\n this.isConnecting = false;\n this.emit('streamStarting', false);\n reject({\n type: 'error',\n id: 14,\n message: 'ws connection error',\n data: e\n });\n };\n this._abortController.signal.addEventListener('abort', abortConnect);\n });\n }\n stopStream() {\n var _this$_abortControlle3, _this$_abortControlle4;\n if (this.isDisconnecting) {\n return Promise.resolve();\n }\n (_this$_abortControlle3 = this._abortController) === null || _this$_abortControlle3 === void 0 ? void 0 : (_this$_abortControlle4 = _this$_abortControlle3.abort) === null || _this$_abortControlle4 === void 0 ? void 0 : _this$_abortControlle4.call(_this$_abortControlle3);\n this._stopKeepAlive();\n let isStreaming = this.isStreaming;\n this.isDisconnecting = true;\n return this._removeParticipant(this.handleId).finally(() => {\n this.sendMessage(this.handleId, {\n body: {\n \"request\": \"stop\"\n }\n }, true);\n this._wipeListeners();\n this._send({\n \"janus\": \"destroy\"\n }, true);\n if (this.ws && this.ws.readyState === 1) {\n this.ws.close();\n }\n this.sessionId = null;\n this.isDisconnecting = false;\n this.isStreaming = false;\n this.emit('streamStarting', false);\n this.emit('streaming', false);\n this.emit('disconnect', isStreaming);\n return Promise.resolve('Disconnected');\n });\n }\n destroy() {\n if (this.sessiontype === 'reactooroom') {\n return this.disconnect().then(() => {\n this.clear();\n return true;\n });\n } else if (this.sessiontype === 'streaming') {\n return this.stopStream().then(() => {\n this.clear();\n return true;\n });\n }\n }\n _enableDebug() {\n this._log = console.log.bind(console);\n }\n _getHandle(handleId) {\n let rfid = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;\n let userId = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;\n let fullUserId = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null;\n return this._participants.find(p => {\n var _decodeJanusDisplay11;\n return p.handleId === handleId || rfid && p.rfid === rfid || userId && ((_decodeJanusDisplay11 = Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"decodeJanusDisplay\"])(p.userId)) === null || _decodeJanusDisplay11 === void 0 ? void 0 : _decodeJanusDisplay11.userId) === userId || fullUserId && p.userId === fullUserId;\n });\n }\n _findSimulcastConfig(source, settings) {\n return Object.keys(settings).reduce((acc, key) => {\n var _key$match;\n if (settings[source]) {\n return settings[source];\n } else if (source.indexOf((_key$match = key.match(/\\*(.*?)\\*/)) === null || _key$match === void 0 ? void 0 : _key$match[1]) > -1) {\n return settings[key];\n } else return acc;\n }, settings['default']);\n }\n _disableStatsWatch() {\n if (this._statsTimeoutId) {\n clearInterval(this._statsTimeout);\n this._statsTimeoutStopped = true;\n this._statsTimeoutId = null;\n }\n }\n _enableStatsWatch() {\n if (this._statsTimeoutId) {\n clearTimeout(this._statsTimeoutId);\n this._statsTimeoutId = null;\n }\n this._statsTimeoutStopped = false;\n const loop = () => {\n let startTime = performance.now();\n let endTime = null;\n this._getStats('video').then(participantsStats => {\n endTime = performance.now();\n this._parseVideoStats(participantsStats);\n }).finally(() => {\n if (!this._statsTimeoutStopped) {\n this._statsTimeoutId = setTimeout(loop, this._statsInterval - Math.min(endTime - startTime, this._statsInterval));\n }\n });\n };\n loop();\n }\n\n // This method completely ignores temporal layers\n // We prefer higher fps and lower resolution so if the fps in not in the range of 0.7 of the max fps we go to the next lower resolution\n\n _enableSubstreamAutoSelect() {\n if (!this.simulcast) {\n return;\n }\n if (this._aqTimeoutId) {\n clearTimeout(this._aqTimeoutId);\n this._aqTimeoutId = null;\n this._aqIntervalCounter = 0;\n }\n const checkStats = () => {\n this._participants.forEach(p => {\n if (p.handleId !== this.handleId) {\n var _p$webrtcStuff, _p$webrtcStuff$pc, _transceivers$filter;\n const transceivers = (_p$webrtcStuff = p.webrtcStuff) === null || _p$webrtcStuff === void 0 ? void 0 : (_p$webrtcStuff$pc = _p$webrtcStuff.pc) === null || _p$webrtcStuff$pc === void 0 ? void 0 : _p$webrtcStuff$pc.getTransceivers();\n const mids = (transceivers === null || transceivers === void 0 ? void 0 : (_transceivers$filter = transceivers.filter(t => t.receiver.track.kind === \"video\")) === null || _transceivers$filter === void 0 ? void 0 : _transceivers$filter.map(t => t.mid)) || [];\n mids.forEach(mid => {\n var _p$webrtcStuff2, _p$webrtcStuff2$overr, _p$webrtcStuff3, _p$webrtcStuff3$overr;\n const {\n source,\n simulcastBitrates\n } = p.webrtcStuff.tracksMap.find(t => t.mid === mid) || {};\n\n // track is gone\n if (!simulcastBitrates) {\n return;\n }\n const simulcastConfigForSource = this._findSimulcastConfig(source, this.simulcastSettings);\n const initialSubstreamBeenSet = !!p.webrtcStuff.initialSimulcastSubstreamBeenSet[mid];\n const defaultSelectedSubstream = ((_p$webrtcStuff2 = p.webrtcStuff) === null || _p$webrtcStuff2 === void 0 ? void 0 : (_p$webrtcStuff2$overr = _p$webrtcStuff2.overriddenSimulcastMode[mid]) === null || _p$webrtcStuff2$overr === void 0 ? void 0 : _p$webrtcStuff2$overr.defaultSubstream) || (simulcastConfigForSource === null || simulcastConfigForSource === void 0 ? void 0 : simulcastConfigForSource.defaultSubstream);\n const simulcastMode = ((_p$webrtcStuff3 = p.webrtcStuff) === null || _p$webrtcStuff3 === void 0 ? void 0 : (_p$webrtcStuff3$overr = _p$webrtcStuff3.overriddenSimulcastMode[mid]) === null || _p$webrtcStuff3$overr === void 0 ? void 0 : _p$webrtcStuff3$overr.mode) || (simulcastConfigForSource === null || simulcastConfigForSource === void 0 ? void 0 : simulcastConfigForSource.mode);\n if (simulcastMode === 'browserControlled') {\n // do nothing\n } else if (simulcastMode === 'manual' && this._aqIntervalCounter % this._aqIntervalDivisor === 0 || !initialSubstreamBeenSet) {\n p.webrtcStuff.initialSimulcastSubstreamBeenSet[mid] = true;\n const currentSubstream = p.webrtcStuff.selectedSubstream[mid];\n if (defaultSelectedSubstream !== undefined && defaultSelectedSubstream !== null && defaultSelectedSubstream !== currentSubstream) {\n this._log('Attempting to force substream quality', defaultSelectedSubstream);\n this.selectSubStream(p.handleId, defaultSelectedSubstream, undefined, mid, false).catch(reason => this._log(`Changing substream for mid: ${mid} failed. Reason: ${reason}`));\n }\n } else if (simulcastMode === 'controlled') {\n var _p$webrtcStuff4, _p$webrtcStuff4$stats, _p$webrtcStuff4$stats2, _p$webrtcStuff5, _p$webrtcStuff5$stats, _p$webrtcStuff5$stats2;\n const currentSubstream = p.webrtcStuff.selectedSubstream[mid];\n const settingsForCurrentSubstream = simulcastBitrates === null || simulcastBitrates === void 0 ? void 0 : simulcastBitrates[simulcastBitrates.length - 1 - currentSubstream];\n let directionDecision = 0;\n if (((_p$webrtcStuff4 = p.webrtcStuff) === null || _p$webrtcStuff4 === void 0 ? void 0 : (_p$webrtcStuff4$stats = _p$webrtcStuff4.stats) === null || _p$webrtcStuff4$stats === void 0 ? void 0 : (_p$webrtcStuff4$stats2 = _p$webrtcStuff4$stats[mid]) === null || _p$webrtcStuff4$stats2 === void 0 ? void 0 : _p$webrtcStuff4$stats2.length) > this._upStatsLength) {\n const upMedianStats = this._calculateMedianStats(p.webrtcStuff.stats[mid].slice(this._upStatsLength * -1));\n if ((upMedianStats === null || upMedianStats === void 0 ? void 0 : upMedianStats.framesPerSecond) >= Math.floor(((settingsForCurrentSubstream === null || settingsForCurrentSubstream === void 0 ? void 0 : settingsForCurrentSubstream.maxFramerate) || 30) * 0.7) && (upMedianStats === null || upMedianStats === void 0 ? void 0 : upMedianStats.freezeDurationSinceLast) < this._upStatsLength * this._statsInterval * 0.33 / 1000 /* && upMedianStats?.freezeCountSinceLast < 3 */) {\n directionDecision = 1;\n }\n }\n if (((_p$webrtcStuff5 = p.webrtcStuff) === null || _p$webrtcStuff5 === void 0 ? void 0 : (_p$webrtcStuff5$stats = _p$webrtcStuff5.stats) === null || _p$webrtcStuff5$stats === void 0 ? void 0 : (_p$webrtcStuff5$stats2 = _p$webrtcStuff5$stats[mid]) === null || _p$webrtcStuff5$stats2 === void 0 ? void 0 : _p$webrtcStuff5$stats2.length) > this._downStatsLength) {\n const downMedianStats = this._calculateMedianStats(p.webrtcStuff.stats[mid].slice(this._downStatsLength * -1));\n if ((downMedianStats === null || downMedianStats === void 0 ? void 0 : downMedianStats.framesPerSecond) < Math.floor(((settingsForCurrentSubstream === null || settingsForCurrentSubstream === void 0 ? void 0 : settingsForCurrentSubstream.maxFramerate) || 30) * 0.7) || (downMedianStats === null || downMedianStats === void 0 ? void 0 : downMedianStats.freezeDurationSinceLast) > this._downStatsLength * this._statsInterval * 0.33 / 1000 /* || downMedianStats?.freezeCountSinceLast > 5 || downMedianStats?.jitter > maxJitter(settingsForCurrentSubstream.maxFramerate) */) {\n directionDecision = -1;\n }\n }\n if (directionDecision === -1) {\n if (currentSubstream < simulcastBitrates.length - 1) {\n this._log('Attempting to down the quality for mid: ', mid, ' quality:', currentSubstream + 1);\n this._resetStats(p.handleId, mid);\n this.selectSubStream(p.handleId, currentSubstream + 1, undefined, mid, false).catch(reason => this._log(`Changing substream for mid: ${mid} failed. Reason: ${reason}`));\n }\n } else if (directionDecision === 1) {\n if (currentSubstream > 0) {\n this._log('Attempting to up the quality for mid: ', mid, ' quality:', currentSubstream - 1);\n this._resetStats(p.handleId, mid);\n this.selectSubStream(p.handleId, currentSubstream - 1, undefined, mid, false).catch(reason => this._log(`Changing substream for mid: ${mid} failed. Reason: ${reason}`));\n }\n } else {\n this._log('No quality change for mid: ', mid);\n }\n }\n });\n }\n });\n this._aqIntervalCounter++;\n this._aqTimeoutId = setTimeout(checkStats, this._aqInterval);\n };\n checkStats();\n }\n _disableSubstreamAutoSelect() {\n if (this._aqTimeoutId) {\n clearTimeout(this._aqTimeoutId);\n this._aqTimeoutId = null;\n this._aqIntervalCounter = 0;\n }\n }\n _calculateMedianStats(stats) {\n let medianStats = {\n framesPerSecond: null,\n jitter: null,\n roundTripTime: null,\n freezeDurationSinceLast: null,\n freezeCountSinceLast: null\n };\n let keys = Object.keys(medianStats);\n keys.forEach(key => {\n if (key === 'freezeDurationSinceLast' || key === 'freezeCountSinceLast') {\n medianStats[key] = stats.reduce((acc, cur) => acc + cur[key], 0);\n }\n // median but ignore first value of stats array\n else {\n let values = stats.map(s => s[key]);\n medianStats[key] = Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"median\"])(values);\n }\n });\n medianStats.statsLength = stats.length;\n return medianStats;\n }\n _parseVideoStats(participantsStats) {\n participantsStats.forEach(sourceStats => {\n sourceStats.forEach(participantStats => {\n var _participantStats$han;\n if (participantStats !== null && (participantStats === null || participantStats === void 0 ? void 0 : (_participantStats$han = participantStats.handle) === null || _participantStats$han === void 0 ? void 0 : _participantStats$han.handleId) !== this.handleId) {\n let handle = this._getHandle(participantStats.handle.handleId);\n if (handle) {\n var _decodeJanusDisplay12;\n if (!handle.webrtcStuff.stats[participantStats.mid]) {\n handle.webrtcStuff.stats[participantStats.mid] = [];\n }\n const stats = {\n framesPerSecond: null,\n framesDropped: null,\n totalFreezesDuration: null,\n freezeDurationSinceLast: null,\n freezeCount: null,\n jitter: null,\n packetsLost: null,\n nackCount: null,\n roundTripTime: null,\n width: null,\n height: null,\n networkType: null,\n powerEfficientDecoder: null\n };\n participantStats.stats.forEach(report => {\n var _handle$webrtcStuff4, _handle$webrtcStuff4$;\n const simulcastConfigForSource = this._findSimulcastConfig(participantStats.source, this.simulcastSettings);\n const simulcastMode = ((_handle$webrtcStuff4 = handle.webrtcStuff) === null || _handle$webrtcStuff4 === void 0 ? void 0 : (_handle$webrtcStuff4$ = _handle$webrtcStuff4.overriddenSimulcastMode[participantStats.mid]) === null || _handle$webrtcStuff4$ === void 0 ? void 0 : _handle$webrtcStuff4$.mode) || (simulcastConfigForSource === null || simulcastConfigForSource === void 0 ? void 0 : simulcastConfigForSource.mode);\n if (report.type === 'inbound-rtp' && report.kind === 'video') {\n var _handle$webrtcStuff$s3, _handle$webrtcStuff$s4, _handle$webrtcStuff$s5, _handle$webrtcStuff$s6, _handle$webrtcStuff$s7, _handle$webrtcStuff$s8, _handle$webrtcStuff$s9, _handle$webrtcStuff$s10, _handle$webrtcStuff$s11, _handle$webrtcStuff$s12;\n stats.framesPerSecond = report.framesPerSecond || 0;\n stats.framesDropped = report.framesDropped || 0;\n stats.totalFreezesDuration = report.totalFreezesDuration || 0;\n stats.freezeDurationSinceLast = (report.totalFreezesDuration || 0) - (((_handle$webrtcStuff$s3 = handle.webrtcStuff.stats) === null || _handle$webrtcStuff$s3 === void 0 ? void 0 : (_handle$webrtcStuff$s4 = _handle$webrtcStuff$s3[participantStats.mid]) === null || _handle$webrtcStuff$s4 === void 0 ? void 0 : (_handle$webrtcStuff$s5 = _handle$webrtcStuff$s4[((_handle$webrtcStuff$s6 = handle.webrtcStuff.stats) === null || _handle$webrtcStuff$s6 === void 0 ? void 0 : (_handle$webrtcStuff$s7 = _handle$webrtcStuff$s6[participantStats.mid]) === null || _handle$webrtcStuff$s7 === void 0 ? void 0 : _handle$webrtcStuff$s7.length) - 1]) === null || _handle$webrtcStuff$s5 === void 0 ? void 0 : _handle$webrtcStuff$s5.totalFreezesDuration) || 0);\n stats.freezeCount = report.freezeCount || 0;\n stats.freezeCountSinceLast = (report.freezeCount || 0) - (((_handle$webrtcStuff$s8 = handle.webrtcStuff.stats) === null || _handle$webrtcStuff$s8 === void 0 ? void 0 : (_handle$webrtcStuff$s9 = _handle$webrtcStuff$s8[participantStats.mid]) === null || _handle$webrtcStuff$s9 === void 0 ? void 0 : (_handle$webrtcStuff$s10 = _handle$webrtcStuff$s9[((_handle$webrtcStuff$s11 = handle.webrtcStuff.stats) === null || _handle$webrtcStuff$s11 === void 0 ? void 0 : (_handle$webrtcStuff$s12 = _handle$webrtcStuff$s11[participantStats.mid]) === null || _handle$webrtcStuff$s12 === void 0 ? void 0 : _handle$webrtcStuff$s12.length) - 1]) === null || _handle$webrtcStuff$s10 === void 0 ? void 0 : _handle$webrtcStuff$s10.freezeCount) || 0);\n stats.jitter = report.jitter;\n stats.packetsLost = report.packetsLost;\n stats.nackCount = report.nackCount;\n stats.width = report.frameWidth;\n stats.height = report.frameHeight;\n stats.powerEfficientDecoder = report.powerEfficientDecoder;\n }\n if (report.type === 'candidate-pair') {\n stats.roundTripTime = report.currentRoundTripTime;\n }\n if (report.type === 'local-candidate') {\n stats.networkType = report.networkType;\n }\n stats.selectedSubstream = handle.webrtcStuff.selectedSubstream[participantStats.mid];\n stats.simulcastMode = simulcastMode;\n });\n\n // pushing stats into handle stats array but keeping only 6 last stats\n handle.webrtcStuff.stats[participantStats.mid].push(stats);\n if (handle.webrtcStuff.stats[participantStats.mid].length > this._statsMaxLength) {\n handle.webrtcStuff.stats[participantStats.mid].shift();\n }\n this.emit('rtcStats', {\n handleId: participantStats.handle.handleId,\n stats,\n userId: (_decodeJanusDisplay12 = Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"decodeJanusDisplay\"])(participantStats.handle.userId)) === null || _decodeJanusDisplay12 === void 0 ? void 0 : _decodeJanusDisplay12.userId,\n source: participantStats.source,\n mid: participantStats.mid\n });\n }\n }\n });\n });\n }\n _getStats() {\n let type = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;\n return Promise.all(this._participants.map(participant => {\n let mediaTrack = [];\n if (type === 'video') {\n var _participant$webrtcSt, _participant$webrtcSt2;\n mediaTrack = (participant === null || participant === void 0 ? void 0 : (_participant$webrtcSt = participant.webrtcStuff) === null || _participant$webrtcSt === void 0 ? void 0 : (_participant$webrtcSt2 = _participant$webrtcSt.stream) === null || _participant$webrtcSt2 === void 0 ? void 0 : _participant$webrtcSt2.getVideoTracks()) || [];\n } else if (type === 'audio') {\n var _participant$webrtcSt3, _participant$webrtcSt4;\n mediaTrack = (participant === null || participant === void 0 ? void 0 : (_participant$webrtcSt3 = participant.webrtcStuff) === null || _participant$webrtcSt3 === void 0 ? void 0 : (_participant$webrtcSt4 = _participant$webrtcSt3.stream) === null || _participant$webrtcSt4 === void 0 ? void 0 : _participant$webrtcSt4.getAudioTracks()) || [];\n }\n if (type !== null) {\n var _participant$webrtcSt5, _participant$webrtcSt6;\n const transceivers = participant === null || participant === void 0 ? void 0 : (_participant$webrtcSt5 = participant.webrtcStuff) === null || _participant$webrtcSt5 === void 0 ? void 0 : (_participant$webrtcSt6 = _participant$webrtcSt5.pc) === null || _participant$webrtcSt6 === void 0 ? void 0 : _participant$webrtcSt6.getTransceivers();\n return Promise.all(mediaTrack.map(track => {\n var _transceivers$find;\n const source = Object.keys(participant.webrtcStuff.streamMap).find(s => participant.webrtcStuff.streamMap[s].find(t => t === track.id));\n const mid = (_transceivers$find = transceivers.find(t => {\n var _t$receiver, _t$receiver$track, _t$sender, _t$sender$track;\n return ((_t$receiver = t.receiver) === null || _t$receiver === void 0 ? void 0 : (_t$receiver$track = _t$receiver.track) === null || _t$receiver$track === void 0 ? void 0 : _t$receiver$track.id) === track.id || ((_t$sender = t.sender) === null || _t$sender === void 0 ? void 0 : (_t$sender$track = _t$sender.track) === null || _t$sender$track === void 0 ? void 0 : _t$sender$track.id) === track.id;\n })) === null || _transceivers$find === void 0 ? void 0 : _transceivers$find.mid;\n return participant.webrtcStuff.pc.getStats(track).then(r => ({\n stats: r,\n source,\n mid,\n handle: participant\n })).catch(e => Promise.reject({\n stats: null,\n error: e,\n handle: participant,\n source,\n mid\n }));\n }));\n } else {\n var _participant$webrtcSt7, _participant$webrtcSt8;\n return participant === null || participant === void 0 ? void 0 : (_participant$webrtcSt7 = participant.webrtcStuff) === null || _participant$webrtcSt7 === void 0 ? void 0 : (_participant$webrtcSt8 = _participant$webrtcSt7.pc) === null || _participant$webrtcSt8 === void 0 ? void 0 : _participant$webrtcSt8.getStats(null).then(r => ({\n handle: participant,\n stats: r\n })).catch(e => Promise.reject({\n handle: participant,\n error: e\n }));\n }\n }));\n }\n _resetStats(handleId, mid) {\n let handle = this._getHandle(handleId);\n if (handle) {\n let config = handle.webrtcStuff;\n if (!mid) {\n Object.keys(config.stats).forEach(mid => {\n config.stats[mid] = [config.stats[mid][config.stats[mid].length - 1]];\n });\n } else {\n // clearing stats for the new substream\n if (config.stats[mid]) {\n config.stats[mid] = [config.stats[mid][config.stats[mid].length - 1]];\n }\n }\n }\n }\n _sendTrickleCandidate(handleId, candidate) {\n return this._send({\n \"janus\": \"trickle\",\n \"candidate\": candidate,\n \"handle_id\": handleId\n }, false, false, 5);\n }\n _webrtc(handleId) {\n let enableOntrack = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;\n let handle = this._getHandle(handleId);\n if (!handle) {\n this.emit('error', {\n type: 'error',\n id: 15,\n message: 'id non-existent',\n data: [handleId, 'create rtc connection']\n });\n return;\n }\n let config = handle.webrtcStuff;\n if (!config.pc) {\n let pc_config = {\n \"iceServers\": this.iceServers,\n \"iceTransportPolicy\": 'all',\n \"bundlePolicy\": undefined\n };\n pc_config[\"sdpSemantics\"] = \"unified-plan\";\n let pc_constraints = {};\n if (webrtc_adapter__WEBPACK_IMPORTED_MODULE_0__[\"default\"].browserDetails.browser === \"edge\") {\n // This is Edge, enable BUNDLE explicitly\n pc_config.bundlePolicy = \"max-bundle\";\n }\n\n // pc_config.bundlePolicy = 'balanced';\n // pc_config.iceTransportPolicy = 'relay';\n // pc_config.rtcpMuxPolicy = \"negotiate\";\n\n this._log('new RTCPeerConnection', pc_config, pc_constraints);\n config.pc = new RTCPeerConnection(pc_config, pc_constraints);\n config.pc.onnegotiationneeded = () => {\n this._log('onnegotiationneeded');\n };\n config.pc.onconnectionstatechange = () => {\n if (config.pc.connectionState === 'failed') {\n this._log('connectionState failed');\n this._iceRestart(handleId);\n }\n this.emit('connectionState', [handleId, handleId === this.handleId, config.pc.connectionState]);\n if (handleId !== this.handleId && config.pc.connectionState === 'connected') {\n var _decodeJanusDisplay13, _decodeJanusDisplay14;\n this.emit(this._getAddParticipantEventName(handle.handleId), {\n tid: Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"generateUUID\"])(),\n id: handle.handleId,\n userId: (_decodeJanusDisplay13 = Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"decodeJanusDisplay\"])(handle.userId)) === null || _decodeJanusDisplay13 === void 0 ? void 0 : _decodeJanusDisplay13.userId,\n role: (_decodeJanusDisplay14 = Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"decodeJanusDisplay\"])(handle.userId)) === null || _decodeJanusDisplay14 === void 0 ? void 0 : _decodeJanusDisplay14.role,\n stream: config.stream,\n streamMap: structuredClone(config.streamMap),\n tracksMap: structuredClone(config.tracksMap),\n transceiverMap: structuredClone(config.transceiverMap),\n track: null,\n source: null,\n constructId: this.constructId,\n adding: false,\n removing: false,\n hasAudioTrack: !!(config.stream && config.stream.getAudioTracks().length),\n hasVideoTrack: !!(config.stream && config.stream.getVideoTracks().length)\n });\n }\n };\n config.pc.oniceconnectionstatechange = () => {\n if (config.pc.iceConnectionState === 'failed') {\n this._log('iceConnectionState failed');\n this._iceRestart(handleId);\n }\n this.emit('iceState', [handleId, handleId === this.handleId, config.pc.iceConnectionState]);\n if (handleId !== this.handleId && (config.pc.iceConnectionState === 'completed' || config.pc.iceConnectionState === 'connected')) {\n var _decodeJanusDisplay15, _decodeJanusDisplay16;\n this.emit(this._getAddParticipantEventName(handle.handleId), {\n tid: Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"generateUUID\"])(),\n id: handle.handleId,\n userId: (_decodeJanusDisplay15 = Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"decodeJanusDisplay\"])(handle.userId)) === null || _decodeJanusDisplay15 === void 0 ? void 0 : _decodeJanusDisplay15.userId,\n role: (_decodeJanusDisplay16 = Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"decodeJanusDisplay\"])(handle.userId)) === null || _decodeJanusDisplay16 === void 0 ? void 0 : _decodeJanusDisplay16.role,\n stream: config.stream,\n streamMap: structuredClone(config.streamMap),\n tracksMap: structuredClone(config.tracksMap),\n transceiverMap: structuredClone(config.transceiverMap),\n track: null,\n source: null,\n constructId: this.constructId,\n adding: false,\n hasAudioTrack: !!(config.stream && config.stream.getAudioTracks().length),\n hasVideoTrack: !!(config.stream && config.stream.getVideoTracks().length)\n });\n }\n };\n config.pc.onicecandidate = event => {\n if (event.candidate == null || webrtc_adapter__WEBPACK_IMPORTED_MODULE_0__[\"default\"].browserDetails.browser === 'edge' && event.candidate.candidate.indexOf('endOfCandidates') > 0) {\n config.iceDone = true;\n this._sendTrickleCandidate(handleId, {\n \"completed\": true\n }).catch(e => {\n this.emit('error', e);\n });\n } else {\n // JSON.stringify doesn't work on some WebRTC objects anymore\n // See https://code.google.com/p/chromium/issues/detail?id=467366\n var candidate = {\n \"candidate\": event.candidate.candidate,\n \"sdpMid\": event.candidate.sdpMid,\n \"sdpMLineIndex\": event.candidate.sdpMLineIndex\n };\n this._sendTrickleCandidate(handleId, candidate).catch(e => {\n this.emit('error', e);\n });\n }\n };\n if (enableOntrack) {\n config.pc.ontrack = event => {\n var _event$streams, _event$streams$;\n if (!event.streams) return;\n if (!config.stream) {\n config.stream = new MediaStream();\n }\n if (!((_event$streams = event.streams) !== null && _event$streams !== void 0 && (_event$streams$ = _event$streams[0]) !== null && _event$streams$ !== void 0 && _event$streams$.onremovetrack)) {\n event.streams[0].onremovetrack = ev => {\n var _config$stream, _config$pc, _config$pc$getTransce4, _decodeJanusDisplay17, _decodeJanusDisplay18;\n this._log('Remote track removed', ev);\n (_config$stream = config.stream) === null || _config$stream === void 0 ? void 0 : _config$stream.removeTrack(ev.track);\n\n // check if handle still exists\n if (!this._getHandle(handle.handleId)) {\n return;\n }\n this._updateRemoteParticipantStreamMap(handle.handleId);\n let transceiver = (_config$pc = config.pc) === null || _config$pc === void 0 ? void 0 : (_config$pc$getTransce4 = _config$pc.getTransceivers()) === null || _config$pc$getTransce4 === void 0 ? void 0 : _config$pc$getTransce4.find(t => t.receiver.track === ev.track);\n let mid = (transceiver === null || transceiver === void 0 ? void 0 : transceiver.mid) || ev.track.id;\n let source = Object.keys(config.streamMap).find(key => config.streamMap[key].includes(ev.track.id));\n this.emit(this._getAddParticipantEventName(handle.handleId), {\n tid: Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"generateUUID\"])(),\n id: handle.handleId,\n mid,\n constructId: this.constructId,\n userId: (_decodeJanusDisplay17 = Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"decodeJanusDisplay\"])(handle.userId)) === null || _decodeJanusDisplay17 === void 0 ? void 0 : _decodeJanusDisplay17.userId,\n role: (_decodeJanusDisplay18 = Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"decodeJanusDisplay\"])(handle.userId)) === null || _decodeJanusDisplay18 === void 0 ? void 0 : _decodeJanusDisplay18.role,\n stream: config.stream,\n streamMap: structuredClone(config.streamMap),\n tracksMap: structuredClone(config.tracksMap),\n transceiverMap: structuredClone(config.transceiverMap),\n source,\n track: ev.track,\n adding: false,\n removing: true,\n hasAudioTrack: !!(config.stream && config.stream.getAudioTracks().length),\n hasVideoTrack: !!(config.stream && config.stream.getVideoTracks().length)\n });\n };\n }\n if (event.track) {\n var _config$stream2, _decodeJanusDisplay19, _decodeJanusDisplay20;\n (_config$stream2 = config.stream) === null || _config$stream2 === void 0 ? void 0 : _config$stream2.addTrack(event.track);\n this._updateRemoteParticipantStreamMap(handle.handleId);\n let mid = event.transceiver ? event.transceiver.mid : event.track.id;\n let source = Object.keys(config.streamMap).find(key => config.streamMap[key].includes(event.track.id));\n if (event.track.kind === 'video') {\n this.requestKeyFrame(handle.handleId, mid);\n }\n this.emit(this._getAddParticipantEventName(handle.handleId), {\n tid: Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"generateUUID\"])(),\n mid,\n id: handle.handleId,\n userId: (_decodeJanusDisplay19 = Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"decodeJanusDisplay\"])(handle.userId)) === null || _decodeJanusDisplay19 === void 0 ? void 0 : _decodeJanusDisplay19.userId,\n role: (_decodeJanusDisplay20 = Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"decodeJanusDisplay\"])(handle.userId)) === null || _decodeJanusDisplay20 === void 0 ? void 0 : _decodeJanusDisplay20.role,\n stream: config.stream,\n streamMap: structuredClone(config.streamMap),\n tracksMap: structuredClone(config.tracksMap),\n transceiverMap: structuredClone(config.transceiverMap),\n source,\n track: event.track,\n constructId: this.constructId,\n adding: true,\n removing: false,\n hasAudioTrack: !!(config.stream && config.stream.getAudioTracks().length),\n hasVideoTrack: !!(config.stream && config.stream.getVideoTracks().length)\n });\n if (event.track.onended) return;\n event.track.onended = ev => {\n var _config$stream3, _config$pc2, _config$pc2$getTransc, _decodeJanusDisplay21, _decodeJanusDisplay22;\n this._log('Remote track ended');\n (_config$stream3 = config.stream) === null || _config$stream3 === void 0 ? void 0 : _config$stream3.removeTrack(ev.target);\n // check if handle still exists\n if (!this._getHandle(handle.handleId)) {\n return;\n }\n this._updateRemoteParticipantStreamMap(handle.handleId);\n let transceiver = (_config$pc2 = config.pc) === null || _config$pc2 === void 0 ? void 0 : (_config$pc2$getTransc = _config$pc2.getTransceivers()) === null || _config$pc2$getTransc === void 0 ? void 0 : _config$pc2$getTransc.find(t => t.receiver.track === ev.target);\n let mid = (transceiver === null || transceiver === void 0 ? void 0 : transceiver.mid) || ev.target.id;\n let source = Object.keys(config.streamMap).find(key => config.streamMap[key].includes(ev.target.id));\n this.emit(this._getAddParticipantEventName(handle.handleId), {\n tid: Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"generateUUID\"])(),\n id: handle.handleId,\n mid,\n constructId: this.constructId,\n userId: (_decodeJanusDisplay21 = Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"decodeJanusDisplay\"])(handle.userId)) === null || _decodeJanusDisplay21 === void 0 ? void 0 : _decodeJanusDisplay21.userId,\n role: (_decodeJanusDisplay22 = Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"decodeJanusDisplay\"])(handle.userId)) === null || _decodeJanusDisplay22 === void 0 ? void 0 : _decodeJanusDisplay22.role,\n stream: config.stream,\n streamMap: structuredClone(config.streamMap),\n tracksMap: structuredClone(config.tracksMap),\n transceiverMap: structuredClone(config.transceiverMap),\n source,\n track: ev.target,\n adding: false,\n removing: true,\n hasAudioTrack: !!(config.stream && config.stream.getAudioTracks().length),\n hasVideoTrack: !!(config.stream && config.stream.getVideoTracks().length)\n });\n };\n let mutedTimerId = {};\n let waitPeriod = 300; // ms\n let screenShareWaitPeriod = 5000; // ms\n\n event.track.onmute = ev => {\n var _decodeJanusDisplay23, _decodeJanusDisplay24;\n this._log('Remote track muted');\n let transceiver = config.pc.getTransceivers().find(t => t.receiver.track === ev.target);\n let mid = transceiver.mid || ev.target.id;\n let source = Object.keys(config.streamMap).find(key => config.streamMap[key].includes(ev.target.id));\n this.emit('remoteTrackMuted', {\n id: handle.handleId,\n mid,\n constructId: this.constructId,\n userId: (_decodeJanusDisplay23 = Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"decodeJanusDisplay\"])(handle.userId)) === null || _decodeJanusDisplay23 === void 0 ? void 0 : _decodeJanusDisplay23.userId,\n role: (_decodeJanusDisplay24 = Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"decodeJanusDisplay\"])(handle.userId)) === null || _decodeJanusDisplay24 === void 0 ? void 0 : _decodeJanusDisplay24.role,\n stream: config.stream,\n streamMap: structuredClone(config.streamMap),\n source,\n kind: ev.target.kind,\n track: ev.target,\n muted: true\n });\n\n // when a track is muted, we try to switch to lower quality substream, but not for screen sharing\n\n if (!this.simulcast) {\n return;\n }\n const wPeriod = source.indexOf('screen') > -1 ? screenShareWaitPeriod : waitPeriod;\n if (!mutedTimerId[mid]) {\n mutedTimerId[mid] = setTimeout(() => {\n var _handle$webrtcStuff5, _handle$webrtcStuff5$;\n mutedTimerId[mid] = null;\n const simulcastConfigForSource = this._findSimulcastConfig(source, this.simulcastSettings);\n const simulcastMode = ((_handle$webrtcStuff5 = handle.webrtcStuff) === null || _handle$webrtcStuff5 === void 0 ? void 0 : (_handle$webrtcStuff5$ = _handle$webrtcStuff5.overriddenSimulcastMode[mid]) === null || _handle$webrtcStuff5$ === void 0 ? void 0 : _handle$webrtcStuff5$.mode) || (simulcastConfigForSource === null || simulcastConfigForSource === void 0 ? void 0 : simulcastConfigForSource.mode);\n const {\n simulcastBitrates\n } = handle.webrtcStuff.tracksMap.find(t => t.mid === mid) || {};\n\n // track is gone\n if (!simulcastBitrates) {\n return;\n }\n const currentSubstream = handle.webrtcStuff.selectedSubstream[mid];\n if (!(simulcastMode === 'browserControlled') && ev.target.kind === 'video' && currentSubstream < simulcastBitrates.length - 1) {\n this._log('Attempting to down the quality due to track muted');\n this.selectSubStream(handle.handleId, currentSubstream + 1, undefined, mid, false).catch(reason => this._log(`Changing substream for mid: ${mid} failed. Reason: ${reason}`));\n }\n }, wPeriod);\n }\n };\n event.track.onunmute = ev => {\n var _decodeJanusDisplay25, _decodeJanusDisplay26;\n this._log('Remote track unmuted');\n let transceiver = config.pc.getTransceivers().find(t => t.receiver.track === ev.target);\n let mid = transceiver.mid || ev.target.id;\n let source = Object.keys(config.streamMap).find(key => config.streamMap[key].includes(ev.target.id));\n if (mutedTimerId[mid]) {\n clearTimeout(mutedTimerId[mid]);\n mutedTimerId[mid] = null;\n }\n this.emit('remoteTrackMuted', {\n id: handle.handleId,\n mid,\n constructId: this.constructId,\n userId: (_decodeJanusDisplay25 = Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"decodeJanusDisplay\"])(handle.userId)) === null || _decodeJanusDisplay25 === void 0 ? void 0 : _decodeJanusDisplay25.userId,\n role: (_decodeJanusDisplay26 = Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"decodeJanusDisplay\"])(handle.userId)) === null || _decodeJanusDisplay26 === void 0 ? void 0 : _decodeJanusDisplay26.role,\n stream: config.stream,\n streamMap: structuredClone(config.streamMap),\n source,\n kind: ev.target.kind,\n track: ev.target,\n muted: false\n });\n };\n }\n };\n }\n }\n if (!config.dataChannel || !config.dataChannelOpen) {\n config.dataChannel = {};\n var onDataChannelMessage = event => {\n this._handleDataEvents(handleId, 'message', event.data);\n };\n var onDataChannelStateChange = event => {\n let label = event.target.label;\n let protocol = event.target.protocol;\n let state = config.dataChannel[label] ? config.dataChannel[label].readyState : \"null\";\n this._handleDataEvents(handleId, 'state', {\n state,\n label\n });\n };\n var onDataChannelError = error => {\n var _error$channel;\n this._handleDataEvents(handleId, 'error', {\n label: error === null || error === void 0 ? void 0 : (_error$channel = error.channel) === null || _error$channel === void 0 ? void 0 : _error$channel.label,\n error\n });\n };\n const createDataChannel = (label, protocol, incoming) => {\n let options = {\n ordered: true\n };\n if (!incoming) {\n if (protocol) {\n options = {\n ...options,\n protocol\n };\n }\n config.dataChannel[label] = config.pc.createDataChannel(label, options);\n } else {\n config.dataChannel[label] = incoming;\n }\n config.dataChannel[label].onmessage = onDataChannelMessage;\n config.dataChannel[label].onopen = onDataChannelStateChange;\n config.dataChannel[label].onclose = onDataChannelStateChange;\n config.dataChannel[label].onerror = onDataChannelError;\n };\n createDataChannel(this.defaultDataChannelLabel, null, null);\n config.pc.ondatachannel = function (event) {\n createDataChannel(event.channel.label, event.channel.protocol, event.channel);\n };\n }\n }\n _webrtcPeer(handleId, jsep) {\n let handle = this._getHandle(handleId);\n if (!handle) {\n return Promise.reject({\n type: 'warning',\n id: 15,\n message: 'id non-existent',\n data: [handleId, 'rtc peer']\n });\n }\n var config = handle.webrtcStuff;\n if (jsep !== undefined && jsep !== null) {\n if (config.pc === null) {\n this._log(\"No PeerConnection: if this is an answer, use createAnswer and not _webrtcPeer\");\n return Promise.resolve(null);\n }\n return config.pc.setRemoteDescription(jsep).then(() => {\n config.remoteSdp = jsep.sdp;\n // Any trickle candidate we cached?\n if (config.candidates && config.candidates.length > 0) {\n for (var i = 0; i < config.candidates.length; i++) {\n var candidate = config.candidates[i];\n if (!candidate || candidate.completed === true) {\n config.pc.addIceCandidate(null);\n } else {\n config.pc.addIceCandidate(candidate);\n }\n }\n config.candidates = [];\n }\n // Done\n return true;\n }).catch(e => {\n return Promise.reject({\n type: 'warning',\n id: 32,\n message: 'rtc peer',\n data: [handleId, e]\n });\n });\n } else {\n return Promise.reject({\n type: 'warning',\n id: 22,\n message: 'rtc peer',\n data: [handleId, 'invalid jsep']\n });\n }\n }\n _iceRestart(handleId) {\n let handle = this._getHandle(handleId);\n if (!handle) {\n return;\n }\n var config = handle.webrtcStuff;\n\n // Already restarting;\n if (config.isIceRestarting) {\n return;\n }\n config.isIceRestarting = true;\n if (this.handleId === handleId) {\n this._log('Performing local ICE restart');\n let hasAudio = !!(config.stream && config.stream.getAudioTracks().length > 0);\n let hasVideo = !!(config.stream && config.stream.getVideoTracks().length > 0);\n this._createAO('offer', handleId, true).then(jsep => {\n if (!jsep) {\n return null;\n }\n return this.sendMessage(handleId, {\n body: {\n \"request\": \"configure\",\n \"keyframe\": true,\n \"audio\": hasAudio,\n \"video\": hasVideo,\n \"data\": true,\n ...(this.recordingFilename ? {\n filename: this.recordingFilename\n } : {})\n },\n jsep\n }, false, false, 5);\n }).then(r => {\n config.isIceRestarting = false;\n this._log('ICE restart success');\n }).catch(e => {\n config.isIceRestarting = false;\n this.emit('error', {\n type: 'warning',\n id: 28,\n message: 'iceRestart failed',\n data: e\n });\n });\n } else {\n this._log('Performing remote ICE restart', handleId);\n return this.sendMessage(handleId, {\n body: {\n \"request\": \"configure\",\n \"restart\": true\n }\n }, false, false, 5).then(() => {}).then(() => {\n config.isIceRestarting = false;\n this._log('ICE restart success');\n }).catch(() => {\n config.isIceRestarting = false;\n });\n }\n }\n _setupTransceivers(handleId, _ref) {\n let [audioSend, audioRecv, videoSend, videoRecv, audioTransceiver = null, videoTransceiver = null] = _ref;\n //TODO: this should be refactored to use handle's trackMap so we dont have to pass any parameters\n\n let handle = this._getHandle(handleId);\n if (!handle) {\n return null;\n }\n let config = handle.webrtcStuff;\n const setTransceiver = function (transceiver, send, recv) {\n let kind = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 'audio';\n if (!send && !recv) {\n // disabled: have we removed it?\n if (transceiver) {\n if (transceiver.setDirection) {\n transceiver.setDirection(\"inactive\");\n } else {\n transceiver.direction = \"inactive\";\n }\n }\n } else {\n if (send && recv) {\n if (transceiver) {\n if (transceiver.setDirection) {\n transceiver.setDirection(\"sendrecv\");\n } else {\n transceiver.direction = \"sendrecv\";\n }\n }\n } else if (send && !recv) {\n if (transceiver) {\n if (transceiver.setDirection) {\n transceiver.setDirection(\"sendonly\");\n } else {\n transceiver.direction = \"sendonly\";\n }\n }\n } else if (!send && recv) {\n if (transceiver) {\n if (transceiver.setDirection) {\n transceiver.setDirection(\"recvonly\");\n } else {\n transceiver.direction = \"recvonly\";\n }\n } else {\n // In theory, this is the only case where we might not have a transceiver yet\n config.pc.addTransceiver(kind, {\n direction: \"recvonly\"\n });\n }\n }\n }\n };\n\n // if we're passing any transceivers, we work only on them, doesn't matter if one of them is null\n if (audioTransceiver || videoTransceiver) {\n if (audioTransceiver) {\n setTransceiver(audioTransceiver, audioSend, audioRecv, 'audio');\n }\n if (videoTransceiver) {\n setTransceiver(videoTransceiver, videoSend, videoRecv, 'video');\n }\n }\n // else we work on all transceivers\n else {\n let transceivers = config.pc.getTransceivers();\n if (transceivers && transceivers.length > 0) {\n for (let i in transceivers) {\n let t = transceivers[i];\n if (t.sender && t.sender.track && t.sender.track.kind === \"audio\" || t.receiver && t.receiver.track && t.receiver.track.kind === \"audio\") {\n setTransceiver(t, audioSend, audioRecv, 'audio');\n }\n if (t.sender && t.sender.track && t.sender.track.kind === \"video\" || t.receiver && t.receiver.track && t.receiver.track.kind === \"video\") {\n setTransceiver(t, videoSend, videoRecv, 'video');\n }\n }\n }\n }\n }\n _createAO() {\n let type = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'offer';\n let handleId = arguments.length > 1 ? arguments[1] : undefined;\n let iceRestart = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;\n let handle = this._getHandle(handleId);\n if (!handle) {\n return Promise.reject({\n type: 'warning',\n id: 15,\n message: 'id non-existent',\n data: [handleId, 'createAO', type]\n });\n }\n let methodName = null;\n if (type === 'offer') {\n methodName = 'createOffer';\n } else {\n methodName = 'createAnswer';\n }\n let config = handle.webrtcStuff;\n let mediaConstraints = {};\n if (iceRestart) {\n mediaConstraints[\"iceRestart\"] = true;\n }\n return config.pc[methodName](mediaConstraints).then(response => {\n // if type offer and its me and we want dtx we mungle the sdp\n if (handleId === this.handleId && type === 'offer' && this.enableDtx) {\n // enable DTX\n response.sdp = response.sdp.replace(\"useinbandfec=1\", \"useinbandfec=1;usedtx=1\");\n }\n config.mySdp = response.sdp;\n let _p = config.pc.setLocalDescription(response).catch(e => {\n return Promise.reject({\n type: 'warning',\n id: 24,\n message: 'setLocalDescription',\n data: [handleId, e]\n });\n });\n config.mediaConstraints = mediaConstraints;\n if (!config.iceDone && !config.trickle) {\n // Don't do anything until we have all candidates\n return Promise.resolve(null);\n }\n\n // JSON.stringify doesn't work on some WebRTC objects anymore\n // See https://code.google.com/p/chromium/issues/detail?id=467366\n var jsep = {\n \"type\": response.type,\n \"sdp\": response.sdp\n };\n if (response.e2ee) jsep.e2ee = true;\n if (response.rid_order === \"hml\" || response.rid_order === \"lmh\") jsep.rid_order = response.rid_order;\n if (response.force_relay) jsep.force_relay = true;\n return _p.then(() => jsep);\n }, e => {\n return Promise.reject({\n type: 'warning',\n id: 25,\n message: methodName,\n data: [handleId, e]\n });\n });\n }\n _publishRemote(handleId, jsep) {\n let handle = this._getHandle(handleId);\n if (!handle) {\n return Promise.reject({\n type: 'warning',\n id: 15,\n message: 'id non-existent',\n data: [handleId, 'publish remote participant']\n });\n }\n this._webrtc(handleId, true);\n let config = handle.webrtcStuff;\n if (jsep) {\n return config.pc.setRemoteDescription(jsep).then(() => {\n config.remoteSdp = jsep.sdp;\n // Any trickle candidate we cached?\n if (config.candidates && config.candidates.length > 0) {\n for (var i = 0; i < config.candidates.length; i++) {\n var candidate = config.candidates[i];\n if (!candidate || candidate.completed === true) {\n // end-of-candidates\n config.pc.addIceCandidate(null);\n } else {\n // New candidate\n config.pc.addIceCandidate(candidate);\n }\n }\n config.candidates = [];\n }\n this._setupTransceivers(handleId, [false, true, false, true]);\n\n // Create the answer now\n return this._createAO('answer', handleId, false).then(_jsep => {\n if (!_jsep) {\n this.emit('error', {\n type: 'warning',\n id: 19,\n message: 'publish remote participant',\n data: [handleId, 'no jsep']\n });\n return Promise.resolve();\n }\n return this.sendMessage(handleId, {\n \"body\": {\n \"request\": \"start\",\n ...(this.roomId && {\n \"room\": this.roomId\n }),\n ...(this.pin && {\n pin: this.pin\n })\n },\n \"jsep\": _jsep\n });\n });\n }, e => Promise.reject({\n type: 'warning',\n id: 23,\n message: 'setRemoteDescription',\n data: [handleId, e]\n }));\n } else {\n return Promise.resolve();\n }\n }\n\n //Public methods\n _republishOnTrackEnded(source) {\n let handle = this._getHandle(this.handleId);\n if (!handle) {\n return;\n }\n let config = handle.webrtcStuff;\n if (!config.stream) {\n return;\n }\n let sourceTrackIds = config.streamMap[source] || [];\n let remainingTracks = [];\n for (let i = 0; i < sourceTrackIds.length; i++) {\n let foundTrack = config.stream.getTracks().find(t => t.id === sourceTrackIds[i]);\n if (foundTrack) {\n remainingTracks.push(foundTrack);\n }\n }\n if (remainingTracks.length) {\n let stream = new MediaStream();\n remainingTracks.forEach(track => stream.addTrack(track));\n return this.publishLocal(stream, source);\n } else {\n return this.publishLocal(null, source);\n }\n }\n publishLocal() {\n var _stream$getVideoTrack, _stream$getAudioTrack, _config$stream4, _config$stream4$getAu, _config$stream5, _config$stream5$getVi, _stream$getAudioTrack2, _stream$getVideoTrack2;\n let stream = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;\n let source = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'camera0';\n if (this.isDisconnecting || !this.isConnected) {\n return Promise.reject({\n type: 'warning',\n id: 18,\n message: 'Either not connected or disconnecting'\n });\n }\n if ((stream === null || stream === void 0 ? void 0 : (_stream$getVideoTrack = stream.getVideoTracks()) === null || _stream$getVideoTrack === void 0 ? void 0 : _stream$getVideoTrack.length) > 1) {\n return Promise.reject({\n type: 'warning',\n id: 30,\n message: 'multiple video tracks not supported',\n data: null\n });\n }\n if ((stream === null || stream === void 0 ? void 0 : (_stream$getAudioTrack = stream.getAudioTracks()) === null || _stream$getAudioTrack === void 0 ? void 0 : _stream$getAudioTrack.length) > 1) {\n return Promise.reject({\n type: 'warning',\n id: 30,\n message: 'multiple audio tracks not supported',\n data: null\n });\n }\n let handle = this._getHandle(this.handleId);\n if (!handle) {\n return Promise.reject({\n type: 'error',\n id: 31,\n message: 'no local id, connect before publishing',\n data: null\n });\n }\n this.emit('publishing', true);\n this._webrtc(this.handleId);\n let config = handle.webrtcStuff;\n if (!config.stream) {\n config.stream = new MediaStream();\n }\n let needsNegotiation = !this.isPublished;\n let transceivers = config.pc.getTransceivers();\n let existingTracks = [...(config.streamMap[source] || [])];\n if (stream !== null && stream !== void 0 && stream.getTracks().length) {\n var _stream$getTracks;\n config.streamMap[source] = (stream === null || stream === void 0 ? void 0 : (_stream$getTracks = stream.getTracks()) === null || _stream$getTracks === void 0 ? void 0 : _stream$getTracks.map(track => track.id)) || [];\n } else {\n delete config.streamMap[source];\n }\n\n // remove old audio track related to this source\n let oldAudioStream = config === null || config === void 0 ? void 0 : (_config$stream4 = config.stream) === null || _config$stream4 === void 0 ? void 0 : (_config$stream4$getAu = _config$stream4.getAudioTracks()) === null || _config$stream4$getAu === void 0 ? void 0 : _config$stream4$getAu.find(track => existingTracks.includes(track.id));\n if (oldAudioStream) {\n try {\n oldAudioStream.stop();\n } catch (e) {\n this._log(e);\n }\n config.stream.removeTrack(oldAudioStream);\n }\n\n // remove old video track related to this source\n let oldVideoStream = config === null || config === void 0 ? void 0 : (_config$stream5 = config.stream) === null || _config$stream5 === void 0 ? void 0 : (_config$stream5$getVi = _config$stream5.getVideoTracks()) === null || _config$stream5$getVi === void 0 ? void 0 : _config$stream5$getVi.find(track => existingTracks.includes(track.id));\n if (oldVideoStream) {\n try {\n oldVideoStream.stop();\n } catch (e) {\n this._log(e);\n }\n config.stream.removeTrack(oldVideoStream);\n }\n const simulcastConfigForSource = this._findSimulcastConfig(source, this.simulcastSettings);\n let audioTrackReplacePromise = Promise.resolve();\n let videoTrackReplacePromise = Promise.resolve();\n let audioTransceiver = null;\n let videoTransceiver = null;\n let replaceAudio = stream === null || stream === void 0 ? void 0 : (_stream$getAudioTrack2 = stream.getAudioTracks()) === null || _stream$getAudioTrack2 === void 0 ? void 0 : _stream$getAudioTrack2.length;\n let replaceVideo = stream === null || stream === void 0 ? void 0 : (_stream$getVideoTrack2 = stream.getVideoTracks()) === null || _stream$getVideoTrack2 === void 0 ? void 0 : _stream$getVideoTrack2.length;\n for (const transceiver of transceivers) {\n var _transceiver$sender, _transceiver$sender$t, _transceiver$sender2, _transceiver$sender2$, _transceiver$sender3, _transceiver$sender3$, _transceiver$sender4, _transceiver$sender4$, _transceiver$sender5, _transceiver$sender5$, _transceiver$sender5$2, _transceiver$sender6, _transceiver$sender6$, _transceiver$sender6$2;\n if (['sendonly', 'sendrecv'].includes(transceiver.currentDirection) && ((_transceiver$sender = transceiver.sender) === null || _transceiver$sender === void 0 ? void 0 : (_transceiver$sender$t = _transceiver$sender.track) === null || _transceiver$sender$t === void 0 ? void 0 : _transceiver$sender$t.kind) === 'audio' && existingTracks.includes((_transceiver$sender2 = transceiver.sender) === null || _transceiver$sender2 === void 0 ? void 0 : (_transceiver$sender2$ = _transceiver$sender2.track) === null || _transceiver$sender2$ === void 0 ? void 0 : _transceiver$sender2$.id)) {\n audioTransceiver = transceiver;\n } else if (['sendonly', 'sendrecv'].includes(transceiver.currentDirection) && ((_transceiver$sender3 = transceiver.sender) === null || _transceiver$sender3 === void 0 ? void 0 : (_transceiver$sender3$ = _transceiver$sender3.track) === null || _transceiver$sender3$ === void 0 ? void 0 : _transceiver$sender3$.kind) === 'video' && existingTracks.includes((_transceiver$sender4 = transceiver.sender) === null || _transceiver$sender4 === void 0 ? void 0 : (_transceiver$sender4$ = _transceiver$sender4.track) === null || _transceiver$sender4$ === void 0 ? void 0 : _transceiver$sender4$.id)) {\n videoTransceiver = transceiver;\n }\n\n // Reusing existing transceivers\n // TODO: if we start using different codecs for different sources, we need to check for that here\n else if (transceiver.currentDirection === 'inactive' && (_transceiver$sender5 = transceiver.sender) !== null && _transceiver$sender5 !== void 0 && (_transceiver$sender5$ = _transceiver$sender5.getParameters()) !== null && _transceiver$sender5$ !== void 0 && (_transceiver$sender5$2 = _transceiver$sender5$.codecs) !== null && _transceiver$sender5$2 !== void 0 && _transceiver$sender5$2.find(c => c.mimeType.indexOf('audio') > -1) && replaceAudio && !audioTransceiver) {\n audioTransceiver = transceiver;\n needsNegotiation = true;\n } else if (transceiver.currentDirection === 'inactive' && (_transceiver$sender6 = transceiver.sender) !== null && _transceiver$sender6 !== void 0 && (_transceiver$sender6$ = _transceiver$sender6.getParameters()) !== null && _transceiver$sender6$ !== void 0 && (_transceiver$sender6$2 = _transceiver$sender6$.codecs) !== null && _transceiver$sender6$2 !== void 0 && _transceiver$sender6$2.find(c => c.mimeType.indexOf('video') > -1) && replaceVideo && !videoTransceiver) {\n videoTransceiver = transceiver;\n needsNegotiation = true;\n }\n }\n if (replaceAudio) {\n config.stream.addTrack(stream.getAudioTracks()[0]);\n if (audioTransceiver && audioTransceiver.sender) {\n audioTrackReplacePromise = audioTransceiver.sender.replaceTrack(stream.getAudioTracks()[0]);\n } else {\n config.pc.addTrack(stream.getAudioTracks()[0], config.stream);\n needsNegotiation = true;\n }\n } else {\n if (audioTransceiver && audioTransceiver.sender) {\n audioTrackReplacePromise = audioTransceiver.sender.replaceTrack(null);\n needsNegotiation = true;\n }\n }\n if (replaceVideo) {\n config.stream.addTrack(stream.getVideoTracks()[0]);\n if (videoTransceiver && videoTransceiver.sender) {\n videoTrackReplacePromise = videoTransceiver.sender.replaceTrack(stream.getVideoTracks()[0]);\n } else {\n if (!this.simulcast) {\n config.pc.addTrack(stream.getVideoTracks()[0], config.stream);\n } else {\n if (webrtc_adapter__WEBPACK_IMPORTED_MODULE_0__[\"default\"].browserDetails.browser !== 'firefox') {\n // standard\n config.pc.addTransceiver(stream.getVideoTracks()[0], {\n direction: 'sendonly',\n streams: [config.stream],\n sendEncodings: structuredClone(simulcastConfigForSource === null || simulcastConfigForSource === void 0 ? void 0 : simulcastConfigForSource.bitrates)\n });\n } else {\n // firefox\n let transceiver = config.pc.addTransceiver(stream.getVideoTracks()[0], {\n direction: 'sendonly',\n streams: [config.stream]\n });\n let sender = transceiver ? transceiver.sender : null;\n if (sender) {\n let parameters = sender.getParameters() || {};\n parameters.encodings = stream.getVideoTracks()[0].sendEncodings || structuredClone(simulcastConfigForSource === null || simulcastConfigForSource === void 0 ? void 0 : simulcastConfigForSource.bitrates);\n sender.setParameters(parameters);\n }\n }\n }\n needsNegotiation = true;\n }\n } else {\n if (videoTransceiver && videoTransceiver.sender) {\n videoTrackReplacePromise = videoTransceiver.sender.replaceTrack(null);\n needsNegotiation = true;\n }\n }\n this.isAudioEnabed = !!(config.stream && config.stream.getAudioTracks().length > 0);\n this.isVideoEnabled = !!(config.stream && config.stream.getVideoTracks().length > 0);\n\n // we possibly created new transceivers, so we need to get them again\n transceivers = config.pc.getTransceivers();\n existingTracks = [...(config.streamMap[source] || [])];\n if (!audioTransceiver) {\n audioTransceiver = transceivers.find(transceiver => transceiver.sender.track && transceiver.sender.track.kind === 'audio' && existingTracks.includes(transceiver.sender.track.id));\n }\n if (!videoTransceiver) {\n videoTransceiver = transceivers.find(transceiver => transceiver.sender.track && transceiver.sender.track.kind === 'video' && existingTracks.includes(transceiver.sender.track.id));\n }\n let hasAudio = !!(stream && stream.getAudioTracks().length > 0);\n let hasVideo = !!(stream && stream.getVideoTracks().length > 0);\n this._setupTransceivers(this.handleId, [hasAudio, false, hasVideo, false, audioTransceiver, videoTransceiver]);\n const emitEvents = () => {\n var _decodeJanusDisplay27, _decodeJanusDisplay28;\n this.isPublished = true;\n if (!config.stream.onremovetrack) {\n config.stream.onremovetrack = ev => {};\n }\n let republishTimeoutId = null;\n let tracks = config.stream.getTracks();\n this.emit('addLocalParticipant', {\n tid: Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"generateUUID\"])(),\n id: handle.handleId,\n userId: (_decodeJanusDisplay27 = Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"decodeJanusDisplay\"])(handle.userId)) === null || _decodeJanusDisplay27 === void 0 ? void 0 : _decodeJanusDisplay27.userId,\n role: (_decodeJanusDisplay28 = Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"decodeJanusDisplay\"])(this.display)) === null || _decodeJanusDisplay28 === void 0 ? void 0 : _decodeJanusDisplay28.role,\n stream: tracks.length ? config.stream : null,\n // that null is there due to backward compatibility\n track: null,\n streamMap: structuredClone(config.streamMap),\n source,\n adding: false,\n removing: false,\n constructId: this.constructId,\n hasAudioTrack: hasAudio,\n hasVideoTrack: hasVideo\n });\n if (tracks.length) {\n tracks.forEach(track => {\n // used as a flag to not emit tracks that been already emitted\n if (!track.onended) {\n var _decodeJanusDisplay29, _decodeJanusDisplay30;\n this.emit('addLocalParticipant', {\n tid: Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"generateUUID\"])(),\n id: handle.handleId,\n userId: (_decodeJanusDisplay29 = Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"decodeJanusDisplay\"])(handle.userId)) === null || _decodeJanusDisplay29 === void 0 ? void 0 : _decodeJanusDisplay29.userId,\n role: (_decodeJanusDisplay30 = Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"decodeJanusDisplay\"])(this.display)) === null || _decodeJanusDisplay30 === void 0 ? void 0 : _decodeJanusDisplay30.role,\n track,\n stream: config.stream,\n streamMap: structuredClone(config.streamMap),\n source,\n adding: true,\n removing: false,\n constructId: this.constructId,\n hasAudioTrack: hasAudio,\n hasVideoTrack: hasVideo\n });\n track.onended = ev => {\n config.stream.removeTrack(track);\n clearTimeout(republishTimeoutId);\n republishTimeoutId = setTimeout(() => {\n this._republishOnTrackEnded(source);\n }, 100);\n };\n }\n });\n }\n this.isMuted = [];\n for (const source of Object.keys(config.streamMap)) {\n var _config$stream6, _config$stream6$getAu, _config$stream7, _config$stream7$getVi, _transceivers$find2, _transceivers$find3;\n const audioTrack = (_config$stream6 = config.stream) === null || _config$stream6 === void 0 ? void 0 : (_config$stream6$getAu = _config$stream6.getAudioTracks()) === null || _config$stream6$getAu === void 0 ? void 0 : _config$stream6$getAu.find(track => config.streamMap[source].includes(track.id));\n const videoTrack = (_config$stream7 = config.stream) === null || _config$stream7 === void 0 ? void 0 : (_config$stream7$getVi = _config$stream7.getVideoTracks()) === null || _config$stream7$getVi === void 0 ? void 0 : _config$stream7$getVi.find(track => config.streamMap[source].includes(track.id));\n this.isMuted.push({\n type: 'audio',\n value: !audioTrack || !audioTrack.enabled,\n source,\n mid: (_transceivers$find2 = transceivers.find(transceiver => {\n var _transceiver$sender7, _transceiver$sender7$, _transceiver$sender8, _transceiver$sender8$;\n return ((_transceiver$sender7 = transceiver.sender) === null || _transceiver$sender7 === void 0 ? void 0 : (_transceiver$sender7$ = _transceiver$sender7.track) === null || _transceiver$sender7$ === void 0 ? void 0 : _transceiver$sender7$.kind) === 'audio' && ((_transceiver$sender8 = transceiver.sender) === null || _transceiver$sender8 === void 0 ? void 0 : (_transceiver$sender8$ = _transceiver$sender8.track) === null || _transceiver$sender8$ === void 0 ? void 0 : _transceiver$sender8$.id) === (audioTrack === null || audioTrack === void 0 ? void 0 : audioTrack.id);\n })) === null || _transceivers$find2 === void 0 ? void 0 : _transceivers$find2.mid\n });\n this.isMuted.push({\n type: 'video',\n value: !videoTrack || !videoTrack.enabled,\n source,\n mid: (_transceivers$find3 = transceivers.find(transceiver => {\n var _transceiver$sender9, _transceiver$sender9$, _transceiver$sender10, _transceiver$sender11;\n return ((_transceiver$sender9 = transceiver.sender) === null || _transceiver$sender9 === void 0 ? void 0 : (_transceiver$sender9$ = _transceiver$sender9.track) === null || _transceiver$sender9$ === void 0 ? void 0 : _transceiver$sender9$.kind) === 'video' && ((_transceiver$sender10 = transceiver.sender) === null || _transceiver$sender10 === void 0 ? void 0 : (_transceiver$sender11 = _transceiver$sender10.track) === null || _transceiver$sender11 === void 0 ? void 0 : _transceiver$sender11.id) === (videoTrack === null || videoTrack === void 0 ? void 0 : videoTrack.id);\n })) === null || _transceivers$find3 === void 0 ? void 0 : _transceivers$find3.mid\n });\n this.emit('localHasVideo', !!videoTrack, source);\n this.emit('localHasAudio', !!audioTrack, source);\n }\n for (const val of this.isMuted) {\n this.emit('localMuted', {\n ...val\n });\n }\n this.emit('published', {\n status: true,\n hasStream: tracks.length > 0\n });\n this.emit('publishing', false);\n return this;\n };\n\n // this should be enough\n if (!needsNegotiation) {\n return Promise.all([audioTrackReplacePromise, videoTrackReplacePromise]).then(() => emitEvents());\n }\n this._log('Starting negotiation');\n return Promise.all([audioTrackReplacePromise, videoTrackReplacePromise]).then(() => this._createAO('offer', this.handleId, false)).then(jsep => {\n if (!jsep) {\n return null;\n }\n //HOTFIX: Temporary fix for Safari 13\n if (jsep.sdp && jsep.sdp.indexOf(\"\\r\\na=ice-ufrag\") === -1) {\n jsep.sdp = jsep.sdp.replace(\"\\na=ice-ufrag\", \"\\r\\na=ice-ufrag\");\n }\n let descriptions = [];\n Object.keys(config.streamMap).forEach(source => {\n const simulcastConfigForSource = this._findSimulcastConfig(source, this.simulcastSettings);\n config.streamMap[source].forEach(trackId => {\n let t = transceivers.find(transceiver => transceiver.sender.track && transceiver.sender.track.id === trackId);\n if (t) {\n descriptions.push({\n mid: t.mid,\n description: JSON.stringify({\n source,\n simulcastBitrates: simulcastConfigForSource === null || simulcastConfigForSource === void 0 ? void 0 : simulcastConfigForSource.bitrates,\n intercomGroups: this._talkIntercomChannels\n })\n });\n }\n });\n });\n return this.sendMessage(this.handleId, {\n body: {\n \"request\": \"configure\",\n \"audio\": this.isAudioEnabed,\n \"video\": this.isVideoEnabled,\n \"data\": true,\n ...(this.recordingFilename ? {\n filename: this.recordingFilename\n } : {}),\n descriptions: descriptions\n },\n jsep\n });\n }).then(r => {\n if (this._isDataChannelOpen === true) {\n return Promise.resolve(r);\n } else {\n return new Promise((resolve, reject) => {\n let dataChannelTimeoutId = null;\n let _resolve = val => {\n if (val) {\n clearTimeout(dataChannelTimeoutId);\n this._abortController.signal.removeEventListener('abort', _rejectAbort);\n this.off('dataChannel', _resolve, this);\n resolve(this);\n }\n };\n let _rejectTimeout = () => {\n this.off('dataChannel', _resolve, this);\n this._abortController.signal.removeEventListener('abort', _rejectAbort);\n reject({\n type: 'error',\n id: 27,\n message: 'Data channel did not open',\n data: null\n });\n };\n let _rejectAbort = () => {\n this._abortController.signal.removeEventListener('abort', _rejectAbort);\n clearTimeout(dataChannelTimeoutId);\n this.off('dataChannel', _resolve, this);\n reject({\n type: 'warning',\n id: 17,\n message: 'Connection cancelled'\n });\n };\n dataChannelTimeoutId = setTimeout(_rejectTimeout, 10000);\n this._abortController.signal.addEventListener('abort', _rejectAbort);\n this.on('dataChannel', _resolve, this);\n });\n }\n }).then(() => emitEvents()).catch(e => {\n this.emit('publishing', false);\n return Promise.reject(e);\n });\n }\n unpublishLocal() {\n let dontWait = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;\n return this.isPublished ? this.sendMessage(this.handleId, {\n body: {\n \"request\": \"unpublish\"\n }\n }, dontWait).finally(r => {\n this.isPublished = false;\n this.emit('published', {\n status: false,\n hasStream: false\n });\n return r;\n }) : Promise.resolve();\n }\n getUserTalkIntercomChannels(userId) {\n let talkIntercomChannels = [];\n let handle = this._getHandle(null, null, userId);\n if (handle) {\n let config = handle.webrtcStuff;\n talkIntercomChannels = config.tracksMap.reduce((acc, val) => {\n if (val.description) {\n try {\n let description = JSON.parse(val.description);\n if (description.intercomGroups) {\n description.intercomGroups.forEach(group => {\n if (!acc.includes(group)) {\n acc.push(group);\n }\n });\n }\n } catch (e) {}\n }\n return acc;\n }, []);\n }\n return talkIntercomChannels;\n }\n toggleAudio() {\n var _config$pc3;\n let value = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;\n let source = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'camera0';\n let mid = arguments.length > 2 ? arguments[2] : undefined;\n let handle = this._getHandle(this.handleId);\n if (!handle) {\n this.emit('error', {\n type: 'warning',\n id: 21,\n message: 'no local id, connect first',\n data: null\n });\n return;\n }\n let config = handle.webrtcStuff;\n let transceivers = (_config$pc3 = config.pc) === null || _config$pc3 === void 0 ? void 0 : _config$pc3.getTransceivers();\n let transceiver = null;\n if (source) {\n transceiver = transceivers === null || transceivers === void 0 ? void 0 : transceivers.find(t => t.sender && t.sender.track && t.receiver.track.kind === \"audio\" && (config.streamMap[source] || []).includes(t.sender.track.id));\n } else {\n transceiver = transceivers === null || transceivers === void 0 ? void 0 : transceivers.find(t => t.sender && t.sender.track && t.receiver.track.kind === \"audio\" && (mid ? t.mid === mid : true));\n }\n if (transceiver) {\n transceiver.sender.track.enabled = value !== null ? !!value : !transceiver.sender.track.enabled;\n }\n this.isMuted = [];\n for (const source of Object.keys(config.streamMap)) {\n var _config$stream8, _config$stream8$getAu, _config$stream9, _config$stream9$getVi, _audioTransceiver$sen, _audioTransceiver$sen2, _videoTransceiver$sen, _videoTransceiver$sen2;\n const audioTrack = (_config$stream8 = config.stream) === null || _config$stream8 === void 0 ? void 0 : (_config$stream8$getAu = _config$stream8.getAudioTracks()) === null || _config$stream8$getAu === void 0 ? void 0 : _config$stream8$getAu.find(track => config.streamMap[source].includes(track.id));\n const audioTransceiver = transceivers === null || transceivers === void 0 ? void 0 : transceivers.find(transceiver => transceiver.sender.track && transceiver.sender.track.kind === 'audio' && transceiver.sender.track.id === (audioTrack === null || audioTrack === void 0 ? void 0 : audioTrack.id));\n const videoTrack = (_config$stream9 = config.stream) === null || _config$stream9 === void 0 ? void 0 : (_config$stream9$getVi = _config$stream9.getVideoTracks()) === null || _config$stream9$getVi === void 0 ? void 0 : _config$stream9$getVi.find(track => config.streamMap[source].includes(track.id));\n const videoTransceiver = transceivers === null || transceivers === void 0 ? void 0 : transceivers.find(transceiver => transceiver.sender.track && transceiver.sender.track.kind === 'video' && transceiver.sender.track.id === (videoTrack === null || videoTrack === void 0 ? void 0 : videoTrack.id));\n this.isMuted.push({\n type: 'audio',\n value: !audioTrack || !audioTransceiver || !(audioTransceiver !== null && audioTransceiver !== void 0 && (_audioTransceiver$sen = audioTransceiver.sender) !== null && _audioTransceiver$sen !== void 0 && (_audioTransceiver$sen2 = _audioTransceiver$sen.track) !== null && _audioTransceiver$sen2 !== void 0 && _audioTransceiver$sen2.enabled),\n source,\n mid: audioTransceiver === null || audioTransceiver === void 0 ? void 0 : audioTransceiver.mid\n });\n this.isMuted.push({\n type: 'video',\n value: !videoTrack || !videoTransceiver || !(videoTransceiver !== null && videoTransceiver !== void 0 && (_videoTransceiver$sen = videoTransceiver.sender) !== null && _videoTransceiver$sen !== void 0 && (_videoTransceiver$sen2 = _videoTransceiver$sen.track) !== null && _videoTransceiver$sen2 !== void 0 && _videoTransceiver$sen2.enabled),\n source,\n mid: videoTransceiver === null || videoTransceiver === void 0 ? void 0 : videoTransceiver.mid\n });\n }\n for (let val of this.isMuted) {\n this.emit('localMuted', {\n ...val\n });\n }\n }\n toggleVideo() {\n var _config$pc4;\n let value = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;\n let source = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'camera0';\n let mid = arguments.length > 2 ? arguments[2] : undefined;\n let handle = this._getHandle(this.handleId);\n if (!handle) {\n this.emit('error', {\n type: 'warning',\n id: 21,\n message: 'no local id, connect first',\n data: null\n });\n return;\n }\n let config = handle.webrtcStuff;\n let transceivers = (_config$pc4 = config.pc) === null || _config$pc4 === void 0 ? void 0 : _config$pc4.getTransceivers();\n let transceiver = null;\n if (source) {\n transceiver = transceivers === null || transceivers === void 0 ? void 0 : transceivers.find(t => t.sender && t.sender.track && t.receiver.track.kind === \"video\" && (config.streamMap[source] || []).includes(t.sender.track.id));\n } else {\n transceiver = transceivers === null || transceivers === void 0 ? void 0 : transceivers.find(t => t.sender && t.sender.track && t.receiver.track.kind === \"video\" && (mid ? t.mid === mid : true));\n }\n if (transceiver) {\n transceiver.sender.track.enabled = value !== null ? !!value : !transceiver.sender.track.enabled;\n }\n this.isMuted = [];\n for (const source of Object.keys(config.streamMap)) {\n var _config$stream10, _config$stream10$getA, _config$stream11, _config$stream11$getV, _audioTransceiver$sen3, _audioTransceiver$sen4, _videoTransceiver$sen3, _videoTransceiver$sen4;\n const audioTrack = (_config$stream10 = config.stream) === null || _config$stream10 === void 0 ? void 0 : (_config$stream10$getA = _config$stream10.getAudioTracks()) === null || _config$stream10$getA === void 0 ? void 0 : _config$stream10$getA.find(track => config.streamMap[source].includes(track.id));\n const audioTransceiver = transceivers === null || transceivers === void 0 ? void 0 : transceivers.find(transceiver => transceiver.sender.track && transceiver.sender.track.kind === 'audio' && transceiver.sender.track.id === (audioTrack === null || audioTrack === void 0 ? void 0 : audioTrack.id));\n const videoTrack = (_config$stream11 = config.stream) === null || _config$stream11 === void 0 ? void 0 : (_config$stream11$getV = _config$stream11.getVideoTracks()) === null || _config$stream11$getV === void 0 ? void 0 : _config$stream11$getV.find(track => config.streamMap[source].includes(track.id));\n const videoTransceiver = transceivers === null || transceivers === void 0 ? void 0 : transceivers.find(transceiver => transceiver.sender.track && transceiver.sender.track.kind === 'video' && transceiver.sender.track.id === (videoTrack === null || videoTrack === void 0 ? void 0 : videoTrack.id));\n this.isMuted.push({\n type: 'audio',\n value: !audioTrack || !audioTransceiver || !(audioTransceiver !== null && audioTransceiver !== void 0 && (_audioTransceiver$sen3 = audioTransceiver.sender) !== null && _audioTransceiver$sen3 !== void 0 && (_audioTransceiver$sen4 = _audioTransceiver$sen3.track) !== null && _audioTransceiver$sen4 !== void 0 && _audioTransceiver$sen4.enabled),\n source,\n mid: audioTransceiver === null || audioTransceiver === void 0 ? void 0 : audioTransceiver.mid\n });\n this.isMuted.push({\n type: 'video',\n value: !videoTrack || !videoTransceiver || !(videoTransceiver !== null && videoTransceiver !== void 0 && (_videoTransceiver$sen3 = videoTransceiver.sender) !== null && _videoTransceiver$sen3 !== void 0 && (_videoTransceiver$sen4 = _videoTransceiver$sen3.track) !== null && _videoTransceiver$sen4 !== void 0 && _videoTransceiver$sen4.enabled),\n source,\n mid: videoTransceiver === null || videoTransceiver === void 0 ? void 0 : videoTransceiver.mid\n });\n }\n for (let val of this.isMuted) {\n this.emit('localMuted', {\n ...val\n });\n }\n }\n requestKeyFrame(handleId, mid) {\n this.sendMessage(handleId, {\n \"body\": {\n \"request\": \"configure\",\n \"keyframe\": true,\n ...(mid !== undefined ? {\n streams: [{\n mid,\n keyframe: true\n }]\n } : {})\n }\n }).catch(() => null);\n }\n _setSelectedSubstream(handleId, mid, substream) {\n let handle = this._getHandle(handleId);\n if (handle) {\n let config = handle.webrtcStuff;\n if (!mid) {\n Object.keys(config.selectedSubstream).forEach(mid => {\n config.selectedSubstream[mid] = substream;\n });\n } else {\n config.selectedSubstream[mid] = substream;\n }\n }\n }\n overrideSimulcastSettings(handleId, mid, source) {\n let settings = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};\n const {\n mode,\n defaultSubstream\n } = settings;\n let handle = this._getHandle(handleId);\n if (!handle) {\n return Promise.resolve();\n }\n let config = handle.webrtcStuff;\n if (source !== undefined || mid !== undefined) {\n if (mid === undefined) {\n let transceivers = config.pc.getTransceivers();\n for (let trackId of config.streamMap[source]) {\n let transceiver = transceivers.find(transceiver => transceiver.receiver.track && transceiver.receiver.track.kind === 'video' && transceiver.receiver.track.id === trackId);\n if (transceiver) {\n mid = transceiver.mid;\n break;\n }\n }\n }\n if (mid !== undefined) {\n if (!config.overriddenSimulcastMode[mid]) {\n config.overriddenSimulcastMode[mid] = {};\n }\n config.overriddenSimulcastMode[mid]['defaultSubstream'] = defaultSubstream;\n config.overriddenSimulcastMode[mid]['mode'] = mode;\n return true;\n } else {\n return false;\n }\n }\n }\n selectSubStream(handleId) {\n let substream = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 2;\n let source = arguments.length > 2 ? arguments[2] : undefined;\n let mid = arguments.length > 3 ? arguments[3] : undefined;\n let manual = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false;\n this._log('Select substream called for handle:', handleId, 'Source or mid:', source ? source : mid, 'Substream:', substream);\n let handle = this._getHandle(handleId);\n if (!handle) {\n return Promise.resolve();\n }\n let config = handle.webrtcStuff;\n return new Promise((resolve, reject) => {\n let messageTimeoutId;\n let abortResponse = () => {\n clearTimeout(messageTimeoutId);\n this._abortController.signal.removeEventListener('abort', abortResponse);\n this.ws.removeEventListener('message', parseResponse);\n reject('aborted');\n };\n let parseResponse = event => {\n let json = JSON.parse(event.data);\n var sender = json[\"sender\"];\n if (sender === handleId) {\n let plugindata = json[\"plugindata\"] || {};\n let msg = plugindata[\"data\"] || {};\n let substream = msg[\"substream\"];\n if (substream !== undefined && substream !== null && (mid !== undefined ? msg[\"mid\"] === mid : true)) {\n clearTimeout(messageTimeoutId);\n this._abortController.signal.removeEventListener('abort', abortResponse);\n this.ws.removeEventListener('message', parseResponse);\n resolve({\n substream,\n sender\n });\n }\n }\n };\n if (source !== undefined || mid !== undefined) {\n if (mid === undefined) {\n let transceivers = config.pc.getTransceivers();\n for (let trackId of config.streamMap[source]) {\n let transceiver = transceivers.find(transceiver => transceiver.receiver.track && transceiver.receiver.track.kind === 'video' && transceiver.receiver.track.id === trackId);\n if (transceiver) {\n mid = transceiver.mid;\n break;\n }\n }\n }\n if (mid !== undefined) {\n if (!config.overriddenSimulcastMode[mid]) {\n config.overriddenSimulcastMode[mid] = {};\n }\n if (substream === null) {\n if (manual) {\n // reset to previous state\n config.overriddenSimulcastMode[mid]['defaultSubstream'] = null;\n config.overriddenSimulcastMode[mid]['mode'] = null;\n }\n resolve({\n substream,\n sender: handleId\n });\n return;\n }\n if (manual) {\n config.overriddenSimulcastMode[mid]['defaultSubstream'] = substream;\n config.overriddenSimulcastMode[mid]['mode'] = \"manual\";\n }\n this.ws.addEventListener('message', parseResponse);\n this._abortController.signal.addEventListener('abort', abortResponse);\n messageTimeoutId = setTimeout(() => {\n this._abortController.signal.removeEventListener('abort', abortResponse);\n this.ws.removeEventListener('message', parseResponse);\n reject('timeout');\n }, 10000);\n this.sendMessage(handleId, {\n \"body\": {\n \"request\": \"configure\",\n \"streams\": [{\n mid,\n substream: parseInt(substream)\n }]\n }\n });\n } else {\n reject('no mid found');\n }\n } else {\n reject('no source or mid');\n }\n });\n }\n setTalkIntercomChannels() {\n let groups = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ['participants'];\n if (typeof groups !== 'object' || !(\"length\" in groups)) {\n this._log('setTalkIntercomChannels: groups must be an array');\n groups = [groups];\n }\n this._talkIntercomChannels = structuredClone(groups);\n let handle = this._getHandle(this.handleId);\n if (!handle) {\n return Promise.resolve();\n }\n let config = handle.webrtcStuff;\n let transceivers = config.pc.getTransceivers();\n let descriptions = [];\n Object.keys(config.streamMap).forEach(source => {\n const simulcastConfigForSource = this._findSimulcastConfig(source, this.simulcastSettings);\n config.streamMap[source].forEach(trackId => {\n let t = transceivers.find(transceiver => transceiver.sender.track && transceiver.sender.track.id === trackId);\n if (t) {\n descriptions.push({\n mid: t.mid,\n description: JSON.stringify({\n simulcastBitrates: simulcastConfigForSource === null || simulcastConfigForSource === void 0 ? void 0 : simulcastConfigForSource.bitrates,\n intercomGroups: groups,\n source: source\n })\n });\n }\n });\n });\n return this.sendMessage(this.handleId, {\n body: {\n \"request\": \"configure\",\n descriptions: descriptions\n }\n });\n }\n setListenIntercomChannels() {\n let groups = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ['participants'];\n if (typeof groups !== 'object' || !(\"length\" in groups)) {\n this._log('setListenIntercomChannels: groups must be an array');\n groups = [groups];\n }\n this._listenIntercomChannels = structuredClone(groups);\n let handle = this._getHandle(this.handleId);\n if (!handle) {\n return Promise.resolve();\n }\n let promises = [];\n this._participants.forEach(participant => {\n if (participant.handleId === this.handleId) {\n return;\n }\n let handle = this._getHandle(participant.handleId);\n if (handle) {\n const tracksMap = handle.webrtcStuff.tracksMap.filter(t => t.active);\n const subscribe = [];\n const unsubscribe = [];\n tracksMap.forEach(track => {\n if (track.type === 'data') {\n return;\n }\n const description = JSON.parse(track.description);\n const intercomGroups = (description === null || description === void 0 ? void 0 : description.intercomGroups) || [];\n if (this._listenIntercomChannels.some(g => intercomGroups.includes(g))) {\n if (!this._isAlreadySubscribed(participant.handleId, track.id, track.mid)) {\n subscribe.push({\n feed: track.id,\n mid: track.mid\n });\n }\n } else {\n unsubscribe.push({\n feed: track.id,\n mid: track.mid\n });\n }\n });\n this._updateSubscribeMap(participant.handleId, subscribe, unsubscribe);\n if (subscribe.length || unsubscribe.length) {\n promises.push(this.sendMessage(handle.handleId, {\n body: {\n \"request\": \"update\",\n ...(subscribe.length ? {\n subscribe\n } : {}),\n ...(unsubscribe.length ? {\n unsubscribe\n } : {})\n }\n }));\n }\n }\n });\n return Promise.all(promises);\n }\n setRoomType() {\n let type = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'watchparty';\n this._roomType = type;\n return this._roomType;\n }\n setRestrictSubscribeToUserIds() {\n let userIds = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];\n this._restrictSubscribeToUserIds = structuredClone(userIds);\n if (!this.isConnected) {\n return Promise.resolve(this._restrictSubscribeToUserIds);\n }\n // sanity check by getting listparticipants and comparing it to _remoteUsersCache\n this.sendMessage(this.handleId, {\n body: {\n request: 'listparticipants',\n room: this.roomId\n }\n }).then(r => {\n let participants = r.participants;\n let remoteUsersCache = this._remoteUsersCache;\n let handle = this._getHandle(this.handleId);\n // filter out my user id from response and compare it to remoteUsersCache\n participants = participants.filter(p => p.id !== handle.userId);\n // get rid of participants that are in participants but not in remoteUsersCache\n remoteUsersCache = remoteUsersCache.filter(r => participants.find(p => p.id === r.id));\n remoteUsersCache.forEach(r => {\n const handle = this._getHandle(null, null, null, r.userId);\n if (this._participantShouldSubscribe(r.userId)) {\n var _handle$webrtcStuff6;\n // todo: do a nicer flag to indicate inactive handle than just checking for pc\n if (!handle || !((_handle$webrtcStuff6 = handle.webrtcStuff) !== null && _handle$webrtcStuff6 !== void 0 && _handle$webrtcStuff6.pc)) {\n this._log('Subscribing to ', r.userId);\n this._createParticipant(r.userId, r.id).then(handle => {\n this._updateParticipantsTrackData(handle.handleId, r.streams);\n const subscribe = r.streams.filter(s => !s.disabled && this._intercomSubscribe(s)).map(stream => ({\n feed: stream.id,\n mid: stream.mid\n }));\n handle.webrtcStuff.subscribeMap = structuredClone(subscribe);\n return this.sendMessage(handle.handleId, {\n body: {\n \"request\": \"join\",\n \"room\": this.roomId,\n \"ptype\": \"subscriber\",\n \"private_id\": this.privateId,\n streams: subscribe,\n //\"feed\": id,\n ...(this.webrtcVersion > 1000 ? {\n id: this.userId\n } : {}),\n pin: this.pin\n }\n });\n }).catch(err => {\n this.emit('error', err);\n });\n } else {\n this._log('Already subscribed to ', r.userId);\n }\n } else if (handle) {\n this._log('Unsubscribing from ', r.userId);\n this._removeParticipant(handle.handleId);\n }\n });\n });\n }\n}\n_defineProperty(RoomSession, \"sessionTypes\", {\n 'reactooroom': 'janus.plugin.reactooroom',\n 'streaming': 'janus.plugin.streaming'\n});\n_defineProperty(RoomSession, \"userRoleSubscriptionRules\", {\n participant: {\n \"watchparty\": ['participant', 'talkback'],\n \"studio\": ['participant', 'talkback', 'host', 'observer'],\n \"commentary\": ['participant', 'talkback', 'host'],\n \"intercom\": ['host', 'talkback', 'observer', 'observerSolo1', 'observerSolo2', 'observerSolo3'],\n \"videowall\": ['host', 'talkback', 'observer', 'observerSolo1', 'observerSolo2', 'observerSolo3'],\n \"videowall-queue\": ['host', 'talkback', 'observer', 'observerSolo1', 'observerSolo2', 'observerSolo3'],\n \"videowall-queue-video\": ['host', 'talkback', 'observer', 'observerSolo1', 'observerSolo2', 'observerSolo3']\n },\n monitor: {\n \"watchparty\": ['participant', 'host'],\n \"studio\": ['participant', 'host', 'observer'],\n \"commentary\": ['participant', 'host'],\n \"intercom\": ['host', 'participant'],\n \"videowall\": ['host', 'participant'],\n \"videowall-queue\": ['host', 'participant'],\n \"videowall-queue-video\": ['host', 'participant']\n },\n talkback: {\n \"watchparty\": ['participant', 'host', 'talkback'],\n \"studio\": ['participant', 'host', 'observer', 'talkback'],\n \"commentary\": ['host', 'participant', 'talkback'],\n \"intercom\": ['host', 'participant', 'talkback'],\n \"videowall\": ['host', 'participant', 'talkback'],\n \"videowall-queue\": ['host', 'participant', 'talkback'],\n \"videowall-queue-video\": ['host', 'participant', 'talkback']\n },\n observer: {\n \"watchparty\": ['participant'],\n \"studio\": ['participant'],\n \"commentary\": ['participant'],\n \"intercom\": ['participant'],\n \"videowall\": ['participant'],\n \"videowall-queue\": ['participant'],\n \"videowall-queue-video\": ['participant']\n },\n observerSolo1: {\n \"watchparty\": ['participant'],\n \"studio\": ['participant'],\n \"commentary\": ['participant'],\n \"intercom\": ['participant'],\n \"videowall\": ['participant'],\n \"videowall-queue\": ['participant'],\n \"videowall-queue-video\": ['participant']\n },\n observerSolo2: {\n \"watchparty\": ['participant'],\n \"studio\": ['participant'],\n \"commentary\": ['participant'],\n \"intercom\": ['participant'],\n \"videowall\": ['participant'],\n \"videowall-queue\": ['participant'],\n \"videowall-queue-video\": ['participant']\n },\n observerSolo3: {\n \"watchparty\": ['participant'],\n \"studio\": ['participant'],\n \"commentary\": ['participant'],\n \"intercom\": ['participant'],\n \"videowall\": ['participant'],\n \"videowall-queue\": ['participant'],\n \"videowall-queue-video\": ['participant']\n },\n host: {\n \"watchparty\": [],\n \"studio\": [],\n \"commentary\": [],\n \"intercom\": [],\n \"videowall\": [],\n \"videowall-queue\": [],\n \"videowall-queue-video\": []\n },\n companionTV: {},\n companionPhone: {}\n});\n/* harmony default export */ __webpack_exports__[\"default\"] = (Room);\n\n//# sourceURL=webpack://WatchTogetherSDK/./src/modules/wt-room.js?");
|
|
10513
|
+
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var webrtc_adapter__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! webrtc-adapter */ \"./node_modules/webrtc-adapter/src/js/adapter_core.js\");\n/* harmony import */ var _wt_emitter__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./wt-emitter */ \"./src/modules/wt-emitter.js\");\n/* harmony import */ var _wt_utils__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./wt-utils */ \"./src/modules/wt-utils.js\");\nfunction _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }\nfunction _toPropertyKey(t) { var i = _toPrimitive(t, \"string\"); return \"symbol\" == typeof i ? i : i + \"\"; }\nfunction _toPrimitive(t, r) { if (\"object\" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || \"default\"); if (\"object\" != typeof i) return i; throw new TypeError(\"@@toPrimitive must return a primitive value.\"); } return (\"string\" === r ? String : Number)(t); }\n// Watch together janus webrtc library\n\n\n\n\nclass Room {\n constructor(debug) {\n this.debug = debug;\n this.sessions = [];\n this.safariVp8 = false;\n this.browser = webrtc_adapter__WEBPACK_IMPORTED_MODULE_0__[\"default\"].browserDetails.browser;\n this.browserDetails = webrtc_adapter__WEBPACK_IMPORTED_MODULE_0__[\"default\"].browserDetails;\n this.webrtcSupported = Room.isWebrtcSupported();\n this.safariVp8TestPromise = Room.testSafariVp8();\n this.safariVp8 = null;\n this.safariVp8TestPromise.then(safariVp8 => {\n this.safariVp8 = safariVp8;\n });\n\n // Let's get it started\n this.whenInitialized = this.initialize();\n }\n initialize() {\n return this.safariVp8TestPromise.then(() => this);\n }\n createSession() {\n let constructId = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;\n let type = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'reactooroom';\n let options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};\n return new RoomSession(constructId, type, {\n debug: this.debug,\n ...options\n });\n }\n static testSafariVp8() {\n return new Promise(resolve => {\n if (webrtc_adapter__WEBPACK_IMPORTED_MODULE_0__[\"default\"].browserDetails.browser === 'safari' && webrtc_adapter__WEBPACK_IMPORTED_MODULE_0__[\"default\"].browserDetails.version >= 605) {\n if (RTCRtpSender && RTCRtpSender.getCapabilities && RTCRtpSender.getCapabilities(\"video\") && RTCRtpSender.getCapabilities(\"video\").codecs && RTCRtpSender.getCapabilities(\"video\").codecs.length) {\n var isVp8 = false;\n for (var i in RTCRtpSender.getCapabilities(\"video\").codecs) {\n var codec = RTCRtpSender.getCapabilities(\"video\").codecs[i];\n if (codec && codec.mimeType && codec.mimeType.toLowerCase() === \"video/vp8\") {\n isVp8 = true;\n break;\n }\n }\n resolve(isVp8);\n } else {\n // We do it in a very ugly way, as there's no alternative...\n // We create a PeerConnection to see if VP8 is in an offer\n var testpc = new RTCPeerConnection({}, {});\n testpc.createOffer({\n offerToReceiveVideo: true\n }).then(function (offer) {\n let result = offer.sdp.indexOf(\"VP8\") !== -1;\n testpc.close();\n testpc = null;\n resolve(result);\n });\n }\n } else resolve(false);\n });\n }\n static isWebrtcSupported() {\n return window.RTCPeerConnection !== undefined && window.RTCPeerConnection !== null && navigator.mediaDevices !== undefined && navigator.mediaDevices !== null && navigator.mediaDevices.getUserMedia !== undefined && navigator.mediaDevices.getUserMedia !== null;\n }\n}\nclass RoomSession {\n static noop() {}\n static randomString(len) {\n var charSet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';\n var randomString = '';\n for (var i = 0; i < len; i++) {\n var randomPoz = Math.floor(Math.random() * charSet.length);\n randomString += charSet.substring(randomPoz, randomPoz + 1);\n }\n return randomString;\n }\n constructor() {\n let constructId = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;\n let type = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'reactooroom';\n let options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};\n _defineProperty(this, \"_intercomSubscribe\", stream => {\n var _JSON$parse;\n if (stream.type === 'data') {\n return true;\n }\n const intercomGroups = ((_JSON$parse = JSON.parse(stream.description || \"[]\")) === null || _JSON$parse === void 0 ? void 0 : _JSON$parse.intercomGroups) || [];\n return intercomGroups.some(g => this._listenIntercomChannels.indexOf(g) > -1);\n });\n Object.assign(this, Object(_wt_emitter__WEBPACK_IMPORTED_MODULE_1__[\"default\"])());\n this.options = {\n ...options\n };\n this.defaultDataChannelLabel = 'JanusDataChannel';\n this.server = null;\n this.iceServers = null;\n this.token = null;\n this.roomId = null;\n this.streamId = null;\n this.pin = null;\n this.userId = null;\n this.sessiontype = type;\n this.initialBitrate = 0;\n this.enableDtx = false;\n this.simulcast = false;\n this.defaultSimulcastSettings = {\n \"default\": {\n mode: \"controlled\",\n // controlled, manual, browserControlled\n defaultSubstream: 0,\n // 2 lowest quality, 0 highest quality\n bitrates: [{\n \"rid\": \"l\",\n \"active\": true,\n \"maxBitrate\": 180000,\n \"maxFramerate\": 20,\n \"scaleResolutionDownBy\": 3.3333333333333335,\n \"priority\": \"low\"\n }, {\n \"rid\": \"m\",\n \"active\": true,\n \"maxBitrate\": 500000,\n \"maxFramerate\": 25,\n \"scaleResolutionDownBy\": 1.3333333333333335,\n \"priority\": \"low\"\n }, {\n \"rid\": \"h\",\n \"active\": true,\n \"maxBitrate\": 2000000,\n \"maxFramerate\": 30,\n \"priority\": \"low\"\n }]\n }\n };\n this.recordingFilename = null;\n this.pluginName = RoomSession.sessionTypes[type];\n this.id = null;\n this.privateId = null;\n this.constructId = constructId || RoomSession.randomString(16);\n this.sessionId = null;\n this.handleId = null;\n this.ws = null;\n this.isRestarting = false;\n this.isConnecting = false;\n this.isDisconnecting = false;\n this.isConnected = false;\n this.isPublished = false;\n this.isReclaiming = false;\n this.isStreaming = false;\n this.isMuted = [];\n this.isVideoEnabled = false;\n this.isAudioEnabed = false;\n this._statsMaxLength = 31;\n this._upStatsLength = 30;\n this._downStatsLength = 5;\n this._statsTimeoutStopped = true;\n this._statsTimeoutId = null;\n this._statsInterval = 1000;\n this._aqInterval = 2500;\n this._aqIntervalCounter = 0;\n this._aqIntervalDivisor = 4;\n this._aqTimeoutId = null;\n this._sendMessageTimeout = 5000;\n this._retries = 0;\n this._maxRetries = 5;\n this._keepAliveId = null;\n this._participants = [];\n this._restrictSubscribeToUserIds = []; // all if empty\n this._talkIntercomChannels = ['participants'];\n this._listenIntercomChannels = ['participants'];\n this._roomType = 'watchparty';\n this._isDataChannelOpen = false;\n this._abortController = null;\n this._remoteUsersCache = [];\n this.userRoleSubscriptionRules = {\n ...RoomSession.userRoleSubscriptionRules,\n ...(this.options.userRoleSubscriptionRules || {})\n };\n this._log = RoomSession.noop;\n if (this.options.debug) {\n this._enableDebug();\n }\n }\n _pushToRemoteUsersCache(userId, streams, id) {\n const existingIndex = this._remoteUsersCache.findIndex(u => u.userId === userId);\n if (existingIndex > -1) {\n this._remoteUsersCache.splice(existingIndex, 1, {\n userId,\n streams,\n id\n });\n } else {\n this._remoteUsersCache.push({\n userId,\n streams,\n id\n });\n }\n }\n _removeFromRemoteUsersCache(rfid) {\n const existingIndex = this._remoteUsersCache.findIndex(u => u.id === rfid);\n if (existingIndex > -1) {\n this._remoteUsersCache.splice(existingIndex, 1);\n }\n }\n _participantShouldSubscribe(userId) {\n const myUser = Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"decodeJanusDisplay\"])(this.display);\n const remoteUser = Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"decodeJanusDisplay\"])(userId);\n let localUserRole = (myUser === null || myUser === void 0 ? void 0 : myUser.role) || 'participant';\n let remoteUserRole = (remoteUser === null || remoteUser === void 0 ? void 0 : remoteUser.role) || 'participant';\n return this.userRoleSubscriptionRules[localUserRole][this._roomType || 'watchparty'].indexOf(remoteUserRole) > -1 && (this._restrictSubscribeToUserIds.length === 0 || this._restrictSubscribeToUserIds.indexOf(remoteUser === null || remoteUser === void 0 ? void 0 : remoteUser.userId) > -1);\n }\n _getAddParticipantEventName(handleId) {\n var _decodeJanusDisplay;\n let handle = this._getHandle(handleId);\n if (!handle) {\n this.emit('error', {\n type: 'warning',\n id: 15,\n message: 'id non-existent',\n data: [handleId, 'getParticipantEventName']\n });\n return;\n }\n const participantRole = (_decodeJanusDisplay = Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"decodeJanusDisplay\"])(handle.userId)) === null || _decodeJanusDisplay === void 0 ? void 0 : _decodeJanusDisplay.role;\n switch (participantRole) {\n case 'participant':\n return 'addRemoteParticipant';\n case 'talkback':\n return 'addRemoteTalkback';\n case 'monitor':\n return 'addRemoteTalkback';\n case 'observer':\n case 'observerSolo1':\n case 'observerSolo2':\n case 'observerSolo3':\n return 'addRemoteObserver';\n case 'host':\n return 'addRemoteInstructor';\n case 'companionTV':\n return 'addRemoteCompanionTV';\n case 'companionPhone':\n return 'addRemoteCompanionPhone';\n default:\n return 'addRemoteParticipant';\n }\n }\n _getRemoveParticipantEventName(handleId) {\n var _decodeJanusDisplay2;\n let handle = this._getHandle(handleId);\n if (!handle) {\n this.emit('error', {\n type: 'warning',\n id: 15,\n message: 'id non-existent',\n data: [handleId, 'getParticipantEventName']\n });\n return;\n }\n const participantRole = (_decodeJanusDisplay2 = Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"decodeJanusDisplay\"])(handle.userId)) === null || _decodeJanusDisplay2 === void 0 ? void 0 : _decodeJanusDisplay2.role;\n switch (participantRole) {\n case 'participant':\n return 'removeRemoteParticipant';\n case 'talkback':\n return 'removeRemoteTalkback';\n case 'monitor':\n return 'removeRemoteTalkback';\n case 'observer':\n case 'observerSolo1':\n case 'observerSolo2':\n case 'observerSolo3':\n return 'removeRemoteObserver';\n case 'host':\n return 'removeRemoteInstructor';\n case 'companionTV':\n return 'removeRemoteCompanionTV';\n case 'companionPhone':\n return 'removeRemoteCompanionPhone';\n default:\n return 'removeRemoteParticipant';\n }\n }\n sendMessage(handleId) {\n let message = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {\n body: 'Example Body'\n };\n let dontWait = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;\n let dontResolveOnAck = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;\n let retry = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 0;\n return this._send({\n \"janus\": \"message\",\n \"handle_id\": handleId,\n ...message\n }, dontWait, dontResolveOnAck, retry).then(json => {\n if (json && json[\"janus\"] === \"success\") {\n let plugindata = json[\"plugindata\"] || {};\n let data = plugindata[\"data\"];\n return Promise.resolve(data);\n }\n return Promise.resolve();\n }).catch(json => {\n if (json && json[\"error\"]) {\n return Promise.reject({\n type: 'warning',\n id: 1,\n message: 'sendMessage failed',\n data: json[\"error\"]\n });\n } else {\n return Promise.reject({\n type: 'warning',\n id: 1,\n message: 'sendMessage failed',\n data: json\n });\n }\n });\n }\n _send() {\n let request = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n let ignoreResponse = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;\n let dontResolveOnAck = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;\n let retry = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0;\n let transaction = RoomSession.randomString(12);\n let requestData = {\n ...request,\n transaction,\n token: this.token,\n ...(this.sessionId && {\n 'session_id': this.sessionId\n } || {})\n };\n this._log(requestData);\n const op = () => new Promise((resolve, reject) => {\n let messageTimeoutId = null;\n let abortResponse = () => {\n this._abortController.signal.removeEventListener('abort', abortResponse);\n clearTimeout(messageTimeoutId);\n this.ws.removeEventListener('message', parseResponse);\n reject({\n type: 'warning',\n id: 17,\n message: 'connection cancelled'\n });\n };\n let parseResponse = event => {\n let json = JSON.parse(event.data);\n let r_transaction = json['transaction'];\n if (r_transaction === transaction && (!dontResolveOnAck || json['janus'] !== 'ack')) {\n clearTimeout(messageTimeoutId);\n this._abortController.signal.removeEventListener('abort', abortResponse);\n this.ws.removeEventListener('message', parseResponse);\n if (json['janus'] === 'error') {\n var _json$error;\n if ((json === null || json === void 0 ? void 0 : (_json$error = json.error) === null || _json$error === void 0 ? void 0 : _json$error.code) == 403) {\n this.disconnect(true);\n }\n reject({\n type: 'error',\n id: 2,\n message: 'send failed',\n data: json,\n requestData\n });\n } else {\n resolve(json);\n }\n }\n };\n if (ignoreResponse) {\n if (this.ws && this.ws.readyState === 1) {\n this.ws.send(JSON.stringify(requestData));\n }\n resolve();\n } else {\n if (this.ws && this.ws.readyState === 1) {\n this.ws.addEventListener('message', parseResponse);\n messageTimeoutId = setTimeout(() => {\n this.ws.removeEventListener('message', parseResponse);\n this._abortController.signal.removeEventListener('abort', abortResponse);\n reject({\n type: 'warning',\n id: 3,\n message: 'send timeout',\n data: requestData\n });\n }, this._sendMessageTimeout);\n this._abortController.signal.addEventListener('abort', abortResponse);\n this.ws.send(JSON.stringify(requestData));\n } else {\n reject({\n type: 'warning',\n id: 29,\n message: 'No connection to WebSockets',\n data: requestData\n });\n }\n }\n });\n return op().catch(e => {\n if (e.id === 17) {\n return Promise.reject(e);\n } else if (e.id === 29 && retry > 0) {\n return Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"wait\"])(this._sendMessageTimeout).then(() => this._send(request, ignoreResponse, dontResolveOnAck, retry - 1));\n } else if (retry > 0) {\n return this._send(request, ignoreResponse, dontResolveOnAck, retry - 1);\n } else {\n return Promise.reject(e);\n }\n });\n }\n _connectionClosed() {\n if (!this.isConnected || this.isConnecting || this.isDisconnecting) {\n return;\n }\n if (this._retries < this._maxRetries) {\n setTimeout(() => {\n this._retries++;\n this._reconnect().catch(e => {\n this.emit('error', e);\n });\n }, 3000 * this._retries);\n } else {\n if (this.sessiontype === 'reactooroom') {\n this.disconnect(true);\n } else if (this.sessiontype === 'streaming') {\n this.stopStream();\n }\n this.emit('error', {\n type: 'error',\n id: 4,\n message: 'Lost connection to WebSockets',\n data: null\n });\n }\n }\n _wipeListeners() {\n if (this.ws) {\n this.ws.removeEventListener('close', this.__connectionClosedBoundFn);\n this.ws.removeEventListener('message', this.__handleWsEventsBoundFn);\n }\n }\n _startKeepAlive() {\n this._send({\n \"janus\": \"keepalive\"\n }).then(json => {\n if (json[\"janus\"] !== 'ack') {\n this.emit('error', {\n type: 'warning',\n id: 5,\n message: 'keepalive response suspicious',\n data: json[\"janus\"]\n });\n }\n }).catch(e => {\n this.emit('error', {\n type: 'warning',\n id: 6,\n message: 'keepalive dead',\n data: e\n });\n this._connectionClosed();\n });\n this._keepAliveId = setTimeout(() => {\n this._startKeepAlive();\n }, 10000);\n }\n _stopKeepAlive() {\n clearTimeout(this._keepAliveId);\n }\n _handleWsEvents(event) {\n let json = JSON.parse(event.data);\n var sender = json[\"sender\"];\n var type = json[\"janus\"];\n let handle = this._getHandle(sender);\n if (!handle) {\n return;\n }\n if (type === \"trickle\") {\n let candidate = json[\"candidate\"];\n let config = handle.webrtcStuff;\n if (config.pc && config.remoteSdp) {\n if (!candidate || candidate.completed === true) {\n config.pc.addIceCandidate(null);\n } else {\n config.pc.addIceCandidate(candidate);\n }\n } else {\n if (!config.candidates) {\n config.candidates = [];\n }\n config.candidates.push(candidate);\n }\n } else if (type === \"webrtcup\") {\n //none universal\n } else if (type === \"hangup\") {\n this._log('hangup on', handle.handleId, handle.handleId === this.handleId, json);\n this._removeParticipant(handle.handleId, null, false);\n } else if (type === \"detached\") {\n this._log('detached on', handle.handleId, handle.handleId === this.handleId, json);\n this._removeParticipant(handle.handleId, null, true);\n } else if (type === \"media\") {\n this._log('Media event:', handle.handleId, json[\"type\"], json[\"receiving\"], json[\"mid\"]);\n } else if (type === \"slowlink\") {\n this._log('Slowlink', handle.handleId, json[\"uplink\"], json[\"lost\"], json[\"mid\"]);\n } else if (type === \"event\") {\n //none universal\n } else if (type === 'timeout') {\n this._log('WebSockets Gateway timeout', json);\n this.ws.close(3504, \"Gateway timeout\");\n } else if (type === 'success' || type === 'error') {\n // we're capturing those elsewhere\n } else {\n this._log(`Unknown event: ${type} on session: ${this.sessionId}`);\n }\n\n // LOCAL\n\n if (sender === this.handleId) {\n if (type === \"event\") {\n var plugindata = json[\"plugindata\"] || {};\n var msg = plugindata[\"data\"] || {};\n var jsep = json[\"jsep\"];\n let result = msg[\"result\"] || null;\n let event = msg[\"videoroom\"] || null;\n let list = msg[\"publishers\"] || {};\n let leaving = msg[\"leaving\"];\n let kicked = msg[\"kicked\"];\n let substream = msg[\"substream\"];\n let temporal = msg[\"temporal\"];\n\n //let joining = msg[\"joining\"];\n let unpublished = msg[\"unpublished\"];\n let error = msg[\"error\"];\n if (event === \"joined\") {\n var _decodeJanusDisplay3, _decodeJanusDisplay4;\n this.id = msg[\"id\"];\n this.privateId = msg[\"private_id\"];\n this.isConnected = true;\n this._log('We have successfully joined Room');\n this.emit('joined', true, this.constructId);\n this.emit('addLocalParticipant', {\n tid: Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"generateUUID\"])(),\n id: handle.handleId,\n constructId: this.constructId,\n userId: (_decodeJanusDisplay3 = Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"decodeJanusDisplay\"])(handle.userId)) === null || _decodeJanusDisplay3 === void 0 ? void 0 : _decodeJanusDisplay3.userId,\n role: (_decodeJanusDisplay4 = Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"decodeJanusDisplay\"])(this.display)) === null || _decodeJanusDisplay4 === void 0 ? void 0 : _decodeJanusDisplay4.role,\n track: null,\n stream: null,\n streamMap: {},\n source: null,\n adding: false,\n removing: false,\n hasAudioTrack: false,\n hasVideoTrack: false\n });\n for (let f in list) {\n let userId = list[f][\"display\"];\n let streams = list[f][\"streams\"] || [];\n let id = list[f][\"id\"];\n for (let i in streams) {\n streams[i][\"id\"] = id;\n streams[i][\"display\"] = userId;\n }\n this._log('Remote userId: ', userId);\n this._pushToRemoteUsersCache(userId, streams, id);\n if (this._participantShouldSubscribe(userId)) {\n this._log('Creating user: ', userId);\n this._createParticipant(userId, id).then(handle => {\n this._updateParticipantsTrackData(handle.handleId, streams);\n const subscribe = streams.filter(s => !s.disabled && this._intercomSubscribe(s)).map(stream => ({\n feed: stream.id,\n mid: stream.mid\n }));\n handle.webrtcStuff.subscribeMap = structuredClone(subscribe);\n return this.sendMessage(handle.handleId, {\n body: {\n \"request\": \"join\",\n \"room\": this.roomId,\n \"ptype\": \"subscriber\",\n \"private_id\": this.privateId,\n streams: subscribe,\n //\"feed\": id,\n ...(this.webrtcVersion > 1000 ? {\n id: this.userId\n } : {}),\n pin: this.pin\n }\n });\n }).catch(err => {\n this.emit('error', err);\n });\n }\n }\n } else if (event === \"event\") {\n if (substream !== undefined && substream !== null) {\n this._log('Substream event:', substream, sender);\n }\n if (temporal !== undefined && temporal !== null) {\n this._log('Temporal event:', temporal);\n }\n if (msg[\"streams\"] !== undefined && msg[\"streams\"] !== null) {\n this._log('Got my own streams back', msg[\"streams\"]);\n }\n for (let f in list) {\n let userId = list[f][\"display\"];\n let streams = list[f][\"streams\"] || [];\n let id = list[f][\"id\"];\n for (let i in streams) {\n streams[i][\"id\"] = id;\n streams[i][\"display\"] = userId;\n }\n this._log('Remote userId: ', userId);\n this._pushToRemoteUsersCache(userId, streams, id);\n if (this._participantShouldSubscribe(userId)) {\n let handle = this._getHandle(null, id);\n if (handle) {\n this._updateParticipantsTrackData(handle.handleId, streams);\n let subscribe = streams.filter(stream => !stream.disabled && this._intercomSubscribe(stream) && !this._isAlreadySubscribed(handle.handleId, stream.id, stream.mid)).map(s => ({\n feed: s.id,\n mid: s.mid\n }));\n let unsubscribe = streams.filter(stream => stream.disabled || !this._intercomSubscribe(stream)).map(s => ({\n feed: s.id,\n mid: s.mid\n }));\n this._updateSubscribeMap(handle.handleId, subscribe, unsubscribe);\n this._log('Already subscribed to user: ', userId, 'Update streams', subscribe, unsubscribe);\n if (subscribe.length || unsubscribe.length) {\n this.sendMessage(handle.handleId, {\n body: {\n \"request\": \"update\",\n ...(subscribe.length ? {\n subscribe\n } : {}),\n ...(unsubscribe.length ? {\n unsubscribe\n } : {})\n }\n });\n }\n } else {\n this._log('Creating user: ', userId, streams);\n this._createParticipant(userId, id).then(handle => {\n this._updateParticipantsTrackData(handle.handleId, streams);\n const subscribe = streams.filter(s => !s.disabled && this._intercomSubscribe(s)).map(stream => ({\n feed: stream.id,\n mid: stream.mid\n }));\n handle.webrtcStuff.subscribeMap = structuredClone(subscribe);\n return this.sendMessage(handle.handleId, {\n body: {\n \"request\": \"join\",\n \"room\": this.roomId,\n \"ptype\": \"subscriber\",\n \"private_id\": this.privateId,\n streams: subscribe,\n ...(this.webrtcVersion > 1000 ? {\n id: this.userId\n } : {}),\n pin: this.pin\n }\n });\n }).catch(err => {\n this.emit('error', err);\n });\n }\n }\n }\n if (leaving === 'ok') {\n this._log('leaving', this.handleId, 'this is us');\n this._removeParticipant(this.handleId);\n if (msg['reason'] === 'kicked') {\n this.emit('kicked');\n this.disconnect().catch(() => {});\n }\n } else if (leaving) {\n //TODO: in 1 PeerConnection case we only unsubscribe from streams, this may not be true, check janus docs\n this._log('leaving', leaving);\n this._removeFromRemoteUsersCache(leaving);\n this._removeParticipant(null, leaving, true);\n }\n if (unpublished === 'ok') {\n this._log('unpublished', this.handleId, 'this is us');\n this._removeParticipant(this.handleId, null, false); // we do just hangup\n } else if (unpublished) {\n //TODO: in 1 PeerConnection case we only unsubscribe from streams\n this._log('unpublished', unpublished);\n this._removeFromRemoteUsersCache(unpublished);\n this._removeParticipant(null, unpublished, true); // we do hangup and detach\n }\n if (kicked === 'ok') {\n // this case shouldn't exist\n } else if (kicked) {\n this._log('kicked', kicked);\n this._removeFromRemoteUsersCache(kicked);\n this._removeParticipant(null, kicked, true); // we do hangup and detach\n }\n if (error) {\n this.emit('error', {\n type: 'error',\n id: 7,\n message: 'local participant error',\n data: [sender, msg]\n });\n }\n }\n\n // Streaming related\n else if (result && result[\"status\"]) {\n this.emit('streamingStatus', result[\"status\"]);\n if (result[\"status\"] === 'stopped') {\n this.stopStream();\n }\n if (result[\"status\"] === 'started') {\n this.emit('streaming', true);\n this.isStreaming = true;\n }\n }\n if (jsep !== undefined && jsep !== null) {\n if (this.sessiontype === 'reactooroom') {\n this._webrtcPeer(this.handleId, jsep).catch(err => {\n this.emit('error', err);\n });\n } else if (this.sessiontype === 'streaming') {\n this._publishRemote(this.handleId, jsep).catch(err => {\n this.emit('error', err);\n });\n }\n }\n } else if (type === \"webrtcup\") {\n if (this.simulcast) {\n return;\n }\n this._log('Configuring bitrate: ' + this.initialBitrate);\n if (this.initialBitrate > 0) {\n this.sendMessage(this.handleId, {\n \"body\": {\n \"request\": \"configure\",\n \"bitrate\": this.initialBitrate\n }\n }).catch(() => null);\n }\n }\n }\n\n //REMOTE\n else {\n let plugindata = json[\"plugindata\"] || {};\n let msg = plugindata[\"data\"] || {};\n let jsep = json[\"jsep\"];\n let event = msg[\"videoroom\"];\n let error = msg[\"error\"];\n let substream = msg[\"substream\"];\n let mid = msg[\"mid\"];\n let temporal = msg[\"temporal\"];\n if (substream !== undefined && substream !== null) {\n this._log('Substream: ', sender, mid, substream);\n this._setSelectedSubstream(sender, mid, substream);\n this._resetStats(sender, mid);\n this.requestKeyFrame(sender, mid);\n }\n if (temporal !== undefined && temporal !== null) {\n this._log('Temporal: ', temporal);\n }\n if (type === \"webrtcup\") {\n this.requestKeyFrame(handle.handleId);\n }\n if (event === \"updated\") {\n this._log('Remote has updated tracks', msg);\n if (msg[\"streams\"]) {\n this._updateTransceiverMap(handle.handleId, msg[\"streams\"]);\n }\n }\n if (event === \"attached\") {\n var _decodeJanusDisplay5, _decodeJanusDisplay6, _handle$webrtcStuff, _handle$webrtcStuff2, _handle$webrtcStuff3;\n this._log('Remote have successfully joined Room', msg);\n if (msg[\"streams\"]) {\n this._updateTransceiverMap(handle.handleId, msg[\"streams\"] || []);\n }\n this.emit(this._getAddParticipantEventName(handle.handleId), {\n tid: Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"generateUUID\"])(),\n id: handle.handleId,\n userId: (_decodeJanusDisplay5 = Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"decodeJanusDisplay\"])(handle.userId)) === null || _decodeJanusDisplay5 === void 0 ? void 0 : _decodeJanusDisplay5.userId,\n role: (_decodeJanusDisplay6 = Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"decodeJanusDisplay\"])(handle.userId)) === null || _decodeJanusDisplay6 === void 0 ? void 0 : _decodeJanusDisplay6.role,\n fullUserId: handle.userId,\n stream: null,\n streamMap: structuredClone((_handle$webrtcStuff = handle.webrtcStuff) === null || _handle$webrtcStuff === void 0 ? void 0 : _handle$webrtcStuff.streamMap),\n tracksMap: structuredClone((_handle$webrtcStuff2 = handle.webrtcStuff) === null || _handle$webrtcStuff2 === void 0 ? void 0 : _handle$webrtcStuff2.tracksMap),\n transceiverMap: structuredClone((_handle$webrtcStuff3 = handle.webrtcStuff) === null || _handle$webrtcStuff3 === void 0 ? void 0 : _handle$webrtcStuff3.transceiverMap),\n source: null,\n track: null,\n adding: false,\n removing: false,\n constructId: this.constructId,\n hasAudioTrack: false,\n hasVideoTrack: false\n });\n }\n if (error) {\n this.emit('error', {\n type: 'warning',\n id: 8,\n message: 'remote participant error',\n data: [sender, msg]\n });\n }\n if (jsep) {\n this._publishRemote(handle.handleId, jsep).catch(err => {\n this.emit('error', err);\n });\n }\n }\n }\n _handleDataEvents(handleId, type, data) {\n let handle = this._getHandle(handleId);\n if (type === 'state') {\n this._log(` - Data channel status - `, `UID: ${handleId}`, `STATUS: ${JSON.stringify(data)}`, `ME: ${handleId === this.handleId}`);\n if (handle) {\n let config = handle.webrtcStuff;\n config.dataChannelOpen = this.defaultDataChannelLabel === (data === null || data === void 0 ? void 0 : data.label) && (data === null || data === void 0 ? void 0 : data.state) === 'open';\n }\n if (handleId === this.handleId && this.defaultDataChannelLabel === (data === null || data === void 0 ? void 0 : data.label)) {\n this._isDataChannelOpen = (data === null || data === void 0 ? void 0 : data.state) === 'open';\n this.emit('dataChannel', (data === null || data === void 0 ? void 0 : data.state) === 'open');\n }\n }\n if (type === 'error') {\n this.emit('error', {\n type: 'warning',\n id: 9,\n message: 'data event warning',\n data: [handleId, data]\n });\n if (handle) {\n let config = handle.webrtcStuff;\n if (this.defaultDataChannelLabel === data.label) {\n config.dataChannelOpen = false;\n }\n }\n if (handleId === this.handleId && this.defaultDataChannelLabel === data.label) {\n this._isDataChannelOpen = false;\n this.emit('dataChannel', false);\n }\n }\n if (handleId === this.handleId && type === 'message') {\n let d = null;\n try {\n d = JSON.parse(data);\n } catch (e) {\n this.emit('error', {\n type: 'warning',\n id: 10,\n message: 'data message parse error',\n data: [handleId, e]\n });\n return;\n }\n this.emit('data', d);\n }\n }\n\n //removeHandle === true -> hangup, detach, removeHandle === false -> hangup\n _removeParticipant(handleId, rfid) {\n let removeHandle = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;\n let handle = this._getHandle(handleId, rfid);\n if (!handle) {\n return Promise.resolve();\n } else {\n handleId = handle.handleId;\n }\n return this._send({\n \"janus\": \"hangup\",\n \"handle_id\": handleId\n }, true).then(() => removeHandle ? this._send({\n \"janus\": \"detach\",\n \"handle_id\": handleId\n }, true) : Promise.resolve()).finally(() => {\n try {\n if (handle.webrtcStuff.stream) {\n if (!this.isRestarting) {\n var _handle$webrtcStuff$s;\n (_handle$webrtcStuff$s = handle.webrtcStuff.stream) === null || _handle$webrtcStuff$s === void 0 ? void 0 : _handle$webrtcStuff$s.getTracks().forEach(track => track.stop());\n } else {\n var _handle$webrtcStuff$s2;\n (_handle$webrtcStuff$s2 = handle.webrtcStuff.stream) === null || _handle$webrtcStuff$s2 === void 0 ? void 0 : _handle$webrtcStuff$s2.getTracks().forEach(track => track.onended = null);\n }\n }\n } catch (e) {\n // Do nothing\n }\n if (handle.webrtcStuff.stream) {\n handle.webrtcStuff.stream.onremovetrack = null;\n handle.webrtcStuff.stream = null;\n }\n if (handle.webrtcStuff.dataChannel) {\n Object.keys(handle.webrtcStuff.dataChannel).forEach(label => {\n handle.webrtcStuff.dataChannel[label].onmessage = null;\n handle.webrtcStuff.dataChannel[label].onopen = null;\n handle.webrtcStuff.dataChannel[label].onclose = null;\n handle.webrtcStuff.dataChannel[label].onerror = null;\n });\n }\n if (handle.webrtcStuff.pc) {\n handle.webrtcStuff.pc.onicecandidate = null;\n handle.webrtcStuff.pc.ontrack = null;\n handle.webrtcStuff.pc.ondatachannel = null;\n handle.webrtcStuff.pc.onconnectionstatechange = null;\n handle.webrtcStuff.pc.oniceconnectionstatechange = null;\n }\n try {\n handle.webrtcStuff.pc.close();\n } catch (e) {}\n handle.webrtcStuff = {\n stream: null,\n streamMap: {},\n tracksMap: [],\n transceiverMap: [],\n subscribeMap: [],\n mySdp: null,\n mediaConstraints: null,\n pc: null,\n dataChannelOpen: false,\n dataChannel: null,\n dtmfSender: null,\n trickle: true,\n iceDone: false,\n isIceRestarting: false,\n stats: {},\n selectedSubstream: {},\n initialSimulcastSubstreamBeenSet: {},\n overriddenSimulcastMode: {}\n };\n if (handleId === this.handleId) {\n var _decodeJanusDisplay7, _decodeJanusDisplay8;\n this._isDataChannelOpen = false;\n this.isPublished = false;\n this.emit('published', {\n status: false,\n hasStream: false\n });\n this.emit('removeLocalParticipant', {\n id: handleId,\n userId: (_decodeJanusDisplay7 = Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"decodeJanusDisplay\"])(handle.userId)) === null || _decodeJanusDisplay7 === void 0 ? void 0 : _decodeJanusDisplay7.userId,\n role: (_decodeJanusDisplay8 = Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"decodeJanusDisplay\"])(handle.userId)) === null || _decodeJanusDisplay8 === void 0 ? void 0 : _decodeJanusDisplay8.role,\n fullUserId: handle.userId\n });\n } else {\n var _decodeJanusDisplay9, _decodeJanusDisplay10;\n this.emit(this._getRemoveParticipantEventName(handleId), {\n id: handleId,\n userId: (_decodeJanusDisplay9 = Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"decodeJanusDisplay\"])(handle.userId)) === null || _decodeJanusDisplay9 === void 0 ? void 0 : _decodeJanusDisplay9.userId,\n role: (_decodeJanusDisplay10 = Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"decodeJanusDisplay\"])(handle.userId)) === null || _decodeJanusDisplay10 === void 0 ? void 0 : _decodeJanusDisplay10.role,\n fullUserId: handle.userId\n });\n }\n if (removeHandle) {\n let handleIndex = this._participants.findIndex(p => p.handleId === handleId);\n this._participants.splice(handleIndex, 1);\n }\n return true;\n });\n }\n _createParticipant() {\n let userId = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;\n let rfid = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;\n return this._send({\n \"janus\": \"attach\",\n \"plugin\": this.pluginName\n }).then(json => {\n let handleId = json.data[\"id\"];\n let handle = {\n handleId,\n rfid,\n userId,\n webrtcStuff: {\n stream: null,\n streamMap: {},\n tracksMap: [],\n transceiverMap: [],\n subscribeMap: [],\n mySdp: null,\n mediaConstraints: null,\n pc: null,\n dataChannelOpen: false,\n dataChannel: null,\n dtmfSender: null,\n trickle: true,\n iceDone: false,\n isIceRestarting: false,\n stats: {},\n selectedSubstream: {},\n initialSimulcastSubstreamBeenSet: {},\n overriddenSimulcastMode: {}\n }\n };\n this._participants.push(handle);\n return handle;\n });\n }\n _updateSubscribeMap(handleId, subscribe, unsubscribe) {\n let handle = this._getHandle(handleId);\n if (!handle) {\n this.emit('error', {\n type: 'warning',\n id: 15,\n message: 'id non-existent',\n data: [handleId, 'updateSubscribeMap']\n });\n return;\n }\n let currentSubscribeMap = handle.webrtcStuff.subscribeMap;\n subscribe.forEach(s => {\n if (!currentSubscribeMap.find(c => c.feed === s.feed && c.mid === s.mid)) {\n currentSubscribeMap.push(s);\n }\n });\n unsubscribe.forEach(s => {\n let index = currentSubscribeMap.findIndex(c => c.feed === s.feed && c.mid === s.mid);\n if (index > -1) {\n currentSubscribeMap.splice(index, 1);\n }\n });\n }\n _isAlreadySubscribed(handleId, feed, mid) {\n let handle = this._getHandle(handleId);\n if (!handle) {\n this.emit('error', {\n type: 'warning',\n id: 15,\n message: 'id non-existent',\n data: [handleId, 'isAlreadySubscribed']\n });\n return false;\n }\n return handle.webrtcStuff.subscribeMap.findIndex(t => t.mid === mid && t.feed === feed) > -1;\n }\n _updateTransceiverMap(handleId, streams) {\n this._log('Updating current transceiver map', handleId, streams);\n let handle = this._getHandle(handleId);\n if (!handle) {\n this.emit('error', {\n type: 'warning',\n id: 15,\n message: 'id non-existent',\n data: [handleId, 'updateTransceiverMap']\n });\n return;\n }\n let config = handle.webrtcStuff;\n config.transceiverMap = structuredClone(streams);\n }\n _updateParticipantsTrackData(handleId, streams) {\n this._log('Updating participants track data', handleId, streams);\n let handle = this._getHandle(handleId);\n if (!handle) {\n this.emit('error', {\n type: 'warning',\n id: 15,\n message: 'id non-existent',\n data: [handleId, 'updateParticipantsTrackData']\n });\n return;\n }\n let config = handle.webrtcStuff;\n config.tracksMap = structuredClone(streams.map(s => {\n let source = null;\n let simulcastBitrates = null;\n try {\n const description = JSON.parse(s.description);\n source = description === null || description === void 0 ? void 0 : description.source;\n simulcastBitrates = description === null || description === void 0 ? void 0 : description.simulcastBitrates;\n } catch (e) {}\n return {\n active: !s.disabled,\n description: s.description,\n source: source,\n simulcastBitrates: simulcastBitrates,\n display: s.display,\n id: s.id,\n mid: s.mid,\n mindex: s.mindex,\n codec: s.codec,\n type: s.type\n };\n }));\n }\n _updateRemoteParticipantStreamMap(handleId) {\n this._log('Updating participants stream map', handleId);\n let handle = this._getHandle(handleId);\n if (!handle) {\n this.emit('error', {\n type: 'warning',\n id: 15,\n message: 'id non-existent',\n data: [handleId, 'updateRemoteParticipantStreamMap']\n });\n return;\n }\n let config = handle.webrtcStuff;\n config.streamMap = {};\n config.transceiverMap.forEach(tItem => {\n var _JSON$parse2, _config$pc$getTransce, _config$pc$getTransce2, _config$pc$getTransce3;\n if (tItem.type === 'data') {\n return;\n }\n if (tItem.active === false) {\n return;\n }\n const source = (_JSON$parse2 = JSON.parse(tItem.feed_description)) === null || _JSON$parse2 === void 0 ? void 0 : _JSON$parse2.source;\n if (!config.streamMap[source]) {\n config.streamMap[source] = [];\n }\n let trackId = (_config$pc$getTransce = config.pc.getTransceivers().find(t => t.mid === tItem.mid)) === null || _config$pc$getTransce === void 0 ? void 0 : (_config$pc$getTransce2 = _config$pc$getTransce.receiver) === null || _config$pc$getTransce2 === void 0 ? void 0 : (_config$pc$getTransce3 = _config$pc$getTransce2.track) === null || _config$pc$getTransce3 === void 0 ? void 0 : _config$pc$getTransce3.id;\n if (trackId) {\n config.streamMap[source].push(trackId);\n }\n });\n }\n _joinRoom(roomId, pin, userId, display) {\n return this.sendMessage(this.handleId, {\n body: {\n \"request\": \"join\",\n \"room\": roomId,\n \"pin\": pin,\n \"ptype\": \"publisher\",\n \"display\": display,\n ...(this.webrtcVersion > 1000 ? {\n id: userId\n } : {})\n }\n }, false, true);\n }\n _watchStream(id) {\n return this.sendMessage(this.handleId, {\n body: {\n \"request\": \"watch\",\n id: String(id)\n }\n }, false, true);\n }\n _leaveRoom() {\n let dontWait = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;\n return this.isConnected ? this.sendMessage(this.handleId, {\n body: {\n \"request\": \"leave\"\n }\n }, dontWait).finally(() => {\n this.isConnected = false;\n this.emit('joined', false);\n }) : Promise.resolve();\n }\n\n // internal reconnect\n\n _reconnect() {\n if (this.isReclaiming) {\n return Promise.resolve();\n }\n if (this.ws) {\n this._wipeListeners();\n if (this.ws.readyState === 1) {\n this.ws.close();\n }\n }\n this._stopKeepAlive();\n this.isReclaiming = true;\n this.emit('joining', true);\n return new Promise((resolve, reject) => {\n let abortReconnect = () => {\n this._abortController.signal.removeEventListener('abort', abortReconnect);\n this.ws.removeEventListener('close', this.__connectionClosedBoundFn);\n this.ws.removeEventListener('message', this.__handleWsEventsBoundFn);\n this.ws.onopen = null;\n this.ws.onerror = null;\n this.isReclaiming = false;\n this.emit('joining', false);\n reject({\n type: 'warning',\n id: 17,\n message: 'Connection cancelled',\n data: e\n });\n };\n this.ws = new WebSocket(this.server, 'janus-protocol');\n this.__connectionClosedBoundFn = this._connectionClosed.bind(this);\n this.__handleWsEventsBoundFn = this._handleWsEvents.bind(this);\n this.ws.addEventListener('close', this.__connectionClosedBoundFn);\n this.ws.addEventListener('message', this.__handleWsEventsBoundFn);\n this.ws.onopen = () => {\n this._abortController.signal.removeEventListener('abort', abortReconnect);\n this._send({\n \"janus\": \"claim\"\n }).then(json => {\n this.sessionId = json[\"session_id\"] ? json[\"session_id\"] : json.data[\"id\"];\n this._startKeepAlive();\n this.isReclaiming = false;\n this.emit('joining', false);\n this._retries = 0;\n resolve(json);\n }).catch(error => {\n this.isReclaiming = false;\n this.emit('joining', false);\n reject({\n type: 'error',\n id: 11,\n message: 'reconnection error',\n data: error\n });\n });\n };\n\n // this is called before 'close' event callback so it doesn't break reconnect loop\n this.ws.onerror = e => {\n this._abortController.signal.removeEventListener('abort', abortReconnect);\n this.isReclaiming = false;\n this.emit('joining', false);\n reject({\n type: 'warning',\n id: 12,\n message: 'ws reconnection error',\n data: e\n });\n };\n this._abortController.signal.addEventListener('abort', abortReconnect);\n });\n }\n connect(roomId, pin, server, iceServers, token, display, userId) {\n let webrtcVersion = arguments.length > 7 && arguments[7] !== undefined ? arguments[7] : 0;\n let initialBitrate = arguments.length > 8 && arguments[8] !== undefined ? arguments[8] : 0;\n let recordingFilename = arguments.length > 9 ? arguments[9] : undefined;\n let simulcast = arguments.length > 10 && arguments[10] !== undefined ? arguments[10] : false;\n let simulcastSettings = arguments.length > 11 && arguments[11] !== undefined ? arguments[11] : this.defaultSimulcastSettings;\n let enableDtx = arguments.length > 12 && arguments[12] !== undefined ? arguments[12] : false;\n if (this.isConnecting) {\n return Promise.reject({\n type: 'warning',\n id: 16,\n message: 'connection already in progress'\n });\n }\n if (this.ws) {\n this._wipeListeners();\n }\n this._stopKeepAlive();\n this._abortController = new AbortController();\n this.sessionId = null;\n this.server = server;\n this.iceServers = iceServers;\n this.token = token;\n this.roomId = roomId;\n this.pin = pin;\n this.display = display;\n this.userId = userId;\n this.webrtcVersion = webrtcVersion;\n this.initialBitrate = initialBitrate;\n this.recordingFilename = recordingFilename;\n this.isConnecting = true;\n this.enableDtx = enableDtx;\n this.simulcast = simulcast;\n this.simulcastSettings = structuredClone(simulcastSettings);\n\n // sort simulcast bitrates\n if (this.simulcastSettings && typeof this.simulcastSettings === 'object' && Object.keys(this.simulcastSettings).length) {\n Object.keys(this.simulcastSettings).forEach(k => {\n this.simulcastSettings[k].bitrates = this.simulcastSettings[k].bitrates.sort((a, b) => {\n if (a.maxBitrate === b.maxBitrate) {\n return a.maxFramerate - b.maxFramerate;\n }\n return a.maxBitrate - b.maxBitrate;\n });\n });\n }\n this.emit('joining', true);\n return new Promise((resolve, reject) => {\n this.ws = new WebSocket(this.server, 'janus-protocol');\n this.__connectionClosedBoundFn = this._connectionClosed.bind(this);\n this.__handleWsEventsBoundFn = this._handleWsEvents.bind(this);\n let abortConnect = () => {\n this._abortController.signal.removeEventListener('abort', abortConnect);\n this.ws.removeEventListener('close', this.__connectionClosedBoundFn);\n this.ws.removeEventListener('message', this.__handleWsEventsBoundFn);\n this.ws.onopen = null;\n this.ws.onerror = null;\n this.isConnecting = false;\n this.emit('joining', false);\n reject({\n type: 'warning',\n id: 17,\n message: 'Connection cancelled'\n });\n };\n this.ws.addEventListener('close', this.__connectionClosedBoundFn);\n this.ws.addEventListener('message', this.__handleWsEventsBoundFn);\n this.ws.onopen = () => {\n this._abortController.signal.removeEventListener('abort', abortConnect);\n this._retries = 0;\n this._send({\n \"janus\": \"create\"\n }).then(json => {\n this.sessionId = json[\"session_id\"] ? json[\"session_id\"] : json.data[\"id\"];\n this._startKeepAlive();\n return 1;\n }).then(() => this._createParticipant(userId)).then(handle => {\n this.handleId = handle.handleId;\n return 1;\n }).then(() => this._joinRoom(roomId, pin, userId, display)).then(() => {\n this._enableStatsWatch();\n this._enableSubstreamAutoSelect();\n this.isConnecting = false;\n this.emit('joining', false);\n resolve(this);\n }).catch(error => {\n this.isConnecting = false;\n this.emit('joining', false);\n reject({\n type: (error === null || error === void 0 ? void 0 : error.type) === 'warning' ? 'warning' : 'error',\n id: 13,\n message: 'connection error',\n data: error\n });\n });\n };\n this.ws.onerror = e => {\n this._abortController.signal.removeEventListener('abort', abortConnect);\n this.isConnecting = false;\n this.emit('joining', false);\n reject({\n type: 'error',\n id: 14,\n message: 'ws connection error',\n data: e\n });\n };\n this._abortController.signal.addEventListener('abort', abortConnect);\n });\n }\n disconnect() {\n var _this$_abortControlle, _this$_abortControlle2;\n if (this.isDisconnecting) {\n return Promise.resolve();\n }\n (_this$_abortControlle = this._abortController) === null || _this$_abortControlle === void 0 ? void 0 : (_this$_abortControlle2 = _this$_abortControlle.abort) === null || _this$_abortControlle2 === void 0 ? void 0 : _this$_abortControlle2.call(_this$_abortControlle);\n this.isDisconnecting = true;\n this._stopKeepAlive();\n this._disableStatsWatch();\n this._disableSubstreamAutoSelect();\n let isConnected = this.isConnected;\n return Promise.all(this._participants.map(p => this._removeParticipant(p.handleId))).finally(() => {\n this._wipeListeners();\n if (this.ws && this.ws.readyState === 1) {\n this._send({\n \"janus\": \"destroy\"\n }, true);\n this.ws.close();\n }\n this.sessionId = null;\n this.isPublished = false;\n this.isConnected = false;\n this.isDisconnecting = false;\n this.emit('publishing', false);\n this.emit('published', {\n status: false,\n hasStream: false\n });\n this.emit('joining', false);\n this.emit('joined', false);\n this.emit('disconnect', isConnected);\n return Promise.resolve('Disconnected');\n });\n }\n startStream(streamId, server, iceServers, token, userId) {\n if (this.isConnecting) {\n return Promise.reject({\n type: 'warning',\n id: 16,\n message: 'connection error',\n data: 'Connection is in progress'\n });\n }\n if (this.ws) {\n this._wipeListeners();\n }\n this._stopKeepAlive();\n this._abortController = new AbortController();\n this.server = server;\n this.iceServers = iceServers;\n this.token = token;\n this.streamId = streamId;\n this.userId = userId;\n this.sessionId = null;\n this.isConnecting = true;\n this.emit('streamStarting', true);\n return new Promise((resolve, reject) => {\n this.ws = new WebSocket(this.server, 'janus-protocol');\n this.__connectionClosedBoundFn = this._connectionClosed.bind(this);\n this.__handleWsEventsBoundFn = this._handleWsEvents.bind(this);\n let abortConnect = () => {\n this._abortController.signal.removeEventListener('abort', abortConnect);\n this.ws.removeEventListener('close', this.__connectionClosedBoundFn);\n this.ws.removeEventListener('message', this.__handleWsEventsBoundFn);\n this.ws.onopen = null;\n this.ws.onerror = null;\n this.isConnecting = false;\n this.emit('streamStarting', false);\n reject({\n type: 'warning',\n id: 17,\n message: 'Connection cancelled'\n });\n };\n this.ws.addEventListener('close', this.__connectionClosedBoundFn);\n this.ws.addEventListener('message', this.__handleWsEventsBoundFn);\n this.ws.onopen = () => {\n this._abortController.signal.removeEventListener('abort', abortConnect);\n this._retries = 0;\n this._send({\n \"janus\": \"create\"\n }).then(json => {\n this.sessionId = json[\"session_id\"] ? json[\"session_id\"] : json.data[\"id\"];\n this._startKeepAlive();\n return 1;\n }).then(() => this._createParticipant(userId)).then(handle => {\n this.handleId = handle.handleId;\n return 1;\n }).then(() => this._watchStream(streamId)).then(() => {\n this.isConnecting = false;\n this.emit('streamStarting', false);\n resolve(this);\n }).catch(error => {\n this.isConnecting = false;\n this.emit('streamStarting', false);\n reject({\n type: 'error',\n id: 13,\n message: 'connection error',\n data: error\n });\n });\n };\n this.ws.onerror = e => {\n this._abortController.signal.removeEventListener('abort', abortConnect);\n this.isConnecting = false;\n this.emit('streamStarting', false);\n reject({\n type: 'error',\n id: 14,\n message: 'ws connection error',\n data: e\n });\n };\n this._abortController.signal.addEventListener('abort', abortConnect);\n });\n }\n stopStream() {\n var _this$_abortControlle3, _this$_abortControlle4;\n if (this.isDisconnecting) {\n return Promise.resolve();\n }\n (_this$_abortControlle3 = this._abortController) === null || _this$_abortControlle3 === void 0 ? void 0 : (_this$_abortControlle4 = _this$_abortControlle3.abort) === null || _this$_abortControlle4 === void 0 ? void 0 : _this$_abortControlle4.call(_this$_abortControlle3);\n this._stopKeepAlive();\n let isStreaming = this.isStreaming;\n this.isDisconnecting = true;\n return this._removeParticipant(this.handleId).finally(() => {\n this.sendMessage(this.handleId, {\n body: {\n \"request\": \"stop\"\n }\n }, true);\n this._wipeListeners();\n this._send({\n \"janus\": \"destroy\"\n }, true);\n if (this.ws && this.ws.readyState === 1) {\n this.ws.close();\n }\n this.sessionId = null;\n this.isDisconnecting = false;\n this.isStreaming = false;\n this.emit('streamStarting', false);\n this.emit('streaming', false);\n this.emit('disconnect', isStreaming);\n return Promise.resolve('Disconnected');\n });\n }\n destroy() {\n if (this.sessiontype === 'reactooroom') {\n return this.disconnect().then(() => {\n this.clear();\n return true;\n });\n } else if (this.sessiontype === 'streaming') {\n return this.stopStream().then(() => {\n this.clear();\n return true;\n });\n }\n }\n _enableDebug() {\n this._log = console.log.bind(console);\n }\n _getHandle(handleId) {\n let rfid = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;\n let userId = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;\n let fullUserId = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null;\n return this._participants.find(p => {\n var _decodeJanusDisplay11;\n return p.handleId === handleId || rfid && p.rfid === rfid || userId && ((_decodeJanusDisplay11 = Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"decodeJanusDisplay\"])(p.userId)) === null || _decodeJanusDisplay11 === void 0 ? void 0 : _decodeJanusDisplay11.userId) === userId || fullUserId && p.userId === fullUserId;\n });\n }\n _findSimulcastConfig(source, settings) {\n return Object.keys(settings).reduce((acc, key) => {\n var _key$match;\n if (settings[source]) {\n return settings[source];\n } else if (source.indexOf((_key$match = key.match(/\\*(.*?)\\*/)) === null || _key$match === void 0 ? void 0 : _key$match[1]) > -1) {\n return settings[key];\n } else return acc;\n }, settings['default']);\n }\n _disableStatsWatch() {\n if (this._statsTimeoutId) {\n clearInterval(this._statsTimeout);\n this._statsTimeoutStopped = true;\n this._statsTimeoutId = null;\n }\n }\n _enableStatsWatch() {\n if (this._statsTimeoutId) {\n clearTimeout(this._statsTimeoutId);\n this._statsTimeoutId = null;\n }\n this._statsTimeoutStopped = false;\n const loop = () => {\n let startTime = performance.now();\n let endTime = null;\n this._getStats('video').then(participantsStats => {\n endTime = performance.now();\n this._parseVideoStats(participantsStats);\n }).finally(() => {\n if (!this._statsTimeoutStopped) {\n this._statsTimeoutId = setTimeout(loop, this._statsInterval - Math.min(endTime - startTime, this._statsInterval));\n }\n });\n };\n loop();\n }\n\n // This method completely ignores temporal layers\n // We prefer higher fps and lower resolution so if the fps in not in the range of 0.7 of the max fps we go to the next lower resolution\n\n _enableSubstreamAutoSelect() {\n if (!this.simulcast) {\n return;\n }\n if (this._aqTimeoutId) {\n clearTimeout(this._aqTimeoutId);\n this._aqTimeoutId = null;\n this._aqIntervalCounter = 0;\n }\n const checkStats = () => {\n this._participants.forEach(p => {\n if (p.handleId !== this.handleId) {\n var _p$webrtcStuff, _p$webrtcStuff$pc, _transceivers$filter;\n const transceivers = (_p$webrtcStuff = p.webrtcStuff) === null || _p$webrtcStuff === void 0 ? void 0 : (_p$webrtcStuff$pc = _p$webrtcStuff.pc) === null || _p$webrtcStuff$pc === void 0 ? void 0 : _p$webrtcStuff$pc.getTransceivers();\n const mids = (transceivers === null || transceivers === void 0 ? void 0 : (_transceivers$filter = transceivers.filter(t => t.receiver.track.kind === \"video\")) === null || _transceivers$filter === void 0 ? void 0 : _transceivers$filter.map(t => t.mid)) || [];\n mids.forEach(mid => {\n var _p$webrtcStuff2, _p$webrtcStuff2$overr, _p$webrtcStuff3, _p$webrtcStuff3$overr;\n const {\n source,\n simulcastBitrates\n } = p.webrtcStuff.tracksMap.find(t => t.mid === mid) || {};\n\n // track is gone\n if (!simulcastBitrates) {\n return;\n }\n const simulcastConfigForSource = this._findSimulcastConfig(source, this.simulcastSettings);\n const initialSubstreamBeenSet = !!p.webrtcStuff.initialSimulcastSubstreamBeenSet[mid];\n const defaultSelectedSubstream = ((_p$webrtcStuff2 = p.webrtcStuff) === null || _p$webrtcStuff2 === void 0 ? void 0 : (_p$webrtcStuff2$overr = _p$webrtcStuff2.overriddenSimulcastMode[mid]) === null || _p$webrtcStuff2$overr === void 0 ? void 0 : _p$webrtcStuff2$overr.defaultSubstream) || (simulcastConfigForSource === null || simulcastConfigForSource === void 0 ? void 0 : simulcastConfigForSource.defaultSubstream);\n const simulcastMode = ((_p$webrtcStuff3 = p.webrtcStuff) === null || _p$webrtcStuff3 === void 0 ? void 0 : (_p$webrtcStuff3$overr = _p$webrtcStuff3.overriddenSimulcastMode[mid]) === null || _p$webrtcStuff3$overr === void 0 ? void 0 : _p$webrtcStuff3$overr.mode) || (simulcastConfigForSource === null || simulcastConfigForSource === void 0 ? void 0 : simulcastConfigForSource.mode);\n if (simulcastMode === 'browserControlled') {\n // do nothing\n } else if (simulcastMode === 'manual' && this._aqIntervalCounter % this._aqIntervalDivisor === 0 || !initialSubstreamBeenSet) {\n p.webrtcStuff.initialSimulcastSubstreamBeenSet[mid] = true;\n const currentSubstream = p.webrtcStuff.selectedSubstream[mid];\n if (defaultSelectedSubstream !== undefined && defaultSelectedSubstream !== null && defaultSelectedSubstream !== currentSubstream) {\n this._log('Attempting to force substream quality', defaultSelectedSubstream);\n this.selectSubStream(p.handleId, defaultSelectedSubstream, undefined, mid, false).catch(reason => this._log(`Changing substream for mid: ${mid} failed. Reason: ${reason}`));\n }\n } else if (simulcastMode === 'controlled') {\n var _p$webrtcStuff4, _p$webrtcStuff4$stats, _p$webrtcStuff4$stats2, _p$webrtcStuff5, _p$webrtcStuff5$stats, _p$webrtcStuff5$stats2;\n const currentSubstream = p.webrtcStuff.selectedSubstream[mid];\n const settingsForCurrentSubstream = simulcastBitrates === null || simulcastBitrates === void 0 ? void 0 : simulcastBitrates[simulcastBitrates.length - 1 - currentSubstream];\n let directionDecision = 0;\n if (((_p$webrtcStuff4 = p.webrtcStuff) === null || _p$webrtcStuff4 === void 0 ? void 0 : (_p$webrtcStuff4$stats = _p$webrtcStuff4.stats) === null || _p$webrtcStuff4$stats === void 0 ? void 0 : (_p$webrtcStuff4$stats2 = _p$webrtcStuff4$stats[mid]) === null || _p$webrtcStuff4$stats2 === void 0 ? void 0 : _p$webrtcStuff4$stats2.length) > this._upStatsLength) {\n const upMedianStats = this._calculateMedianStats(p.webrtcStuff.stats[mid].slice(this._upStatsLength * -1));\n if ((upMedianStats === null || upMedianStats === void 0 ? void 0 : upMedianStats.framesPerSecond) >= Math.floor(((settingsForCurrentSubstream === null || settingsForCurrentSubstream === void 0 ? void 0 : settingsForCurrentSubstream.maxFramerate) || 30) * 0.7) && (upMedianStats === null || upMedianStats === void 0 ? void 0 : upMedianStats.freezeDurationSinceLast) < this._upStatsLength * this._statsInterval * 0.33 / 1000 /* && upMedianStats?.freezeCountSinceLast < 3 */) {\n directionDecision = 1;\n }\n }\n if (((_p$webrtcStuff5 = p.webrtcStuff) === null || _p$webrtcStuff5 === void 0 ? void 0 : (_p$webrtcStuff5$stats = _p$webrtcStuff5.stats) === null || _p$webrtcStuff5$stats === void 0 ? void 0 : (_p$webrtcStuff5$stats2 = _p$webrtcStuff5$stats[mid]) === null || _p$webrtcStuff5$stats2 === void 0 ? void 0 : _p$webrtcStuff5$stats2.length) > this._downStatsLength) {\n const downMedianStats = this._calculateMedianStats(p.webrtcStuff.stats[mid].slice(this._downStatsLength * -1));\n if ((downMedianStats === null || downMedianStats === void 0 ? void 0 : downMedianStats.framesPerSecond) < Math.floor(((settingsForCurrentSubstream === null || settingsForCurrentSubstream === void 0 ? void 0 : settingsForCurrentSubstream.maxFramerate) || 30) * 0.7) || (downMedianStats === null || downMedianStats === void 0 ? void 0 : downMedianStats.freezeDurationSinceLast) > this._downStatsLength * this._statsInterval * 0.33 / 1000 /* || downMedianStats?.freezeCountSinceLast > 5 || downMedianStats?.jitter > maxJitter(settingsForCurrentSubstream.maxFramerate) */) {\n directionDecision = -1;\n }\n }\n if (directionDecision === -1) {\n if (currentSubstream < simulcastBitrates.length - 1) {\n this._log('Attempting to down the quality for mid: ', mid, ' quality:', currentSubstream + 1);\n this._resetStats(p.handleId, mid);\n this.selectSubStream(p.handleId, currentSubstream + 1, undefined, mid, false).catch(reason => this._log(`Changing substream for mid: ${mid} failed. Reason: ${reason}`));\n }\n } else if (directionDecision === 1) {\n if (currentSubstream > 0) {\n this._log('Attempting to up the quality for mid: ', mid, ' quality:', currentSubstream - 1);\n this._resetStats(p.handleId, mid);\n this.selectSubStream(p.handleId, currentSubstream - 1, undefined, mid, false).catch(reason => this._log(`Changing substream for mid: ${mid} failed. Reason: ${reason}`));\n }\n } else {\n this._log('No quality change for mid: ', mid);\n }\n }\n });\n }\n });\n this._aqIntervalCounter++;\n this._aqTimeoutId = setTimeout(checkStats, this._aqInterval);\n };\n checkStats();\n }\n _disableSubstreamAutoSelect() {\n if (this._aqTimeoutId) {\n clearTimeout(this._aqTimeoutId);\n this._aqTimeoutId = null;\n this._aqIntervalCounter = 0;\n }\n }\n _calculateMedianStats(stats) {\n let medianStats = {\n framesPerSecond: null,\n jitter: null,\n roundTripTime: null,\n freezeDurationSinceLast: null,\n freezeCountSinceLast: null\n };\n let keys = Object.keys(medianStats);\n keys.forEach(key => {\n if (key === 'freezeDurationSinceLast' || key === 'freezeCountSinceLast') {\n medianStats[key] = stats.reduce((acc, cur) => acc + cur[key], 0);\n }\n // median but ignore first value of stats array\n else {\n let values = stats.map(s => s[key]);\n medianStats[key] = Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"median\"])(values);\n }\n });\n medianStats.statsLength = stats.length;\n return medianStats;\n }\n _parseVideoStats(participantsStats) {\n participantsStats.forEach(sourceStats => {\n sourceStats.forEach(participantStats => {\n var _participantStats$han;\n if (participantStats !== null && (participantStats === null || participantStats === void 0 ? void 0 : (_participantStats$han = participantStats.handle) === null || _participantStats$han === void 0 ? void 0 : _participantStats$han.handleId) !== this.handleId) {\n let handle = this._getHandle(participantStats.handle.handleId);\n if (handle) {\n var _decodeJanusDisplay12;\n if (!handle.webrtcStuff.stats[participantStats.mid]) {\n handle.webrtcStuff.stats[participantStats.mid] = [];\n }\n const stats = {\n framesPerSecond: null,\n framesDropped: null,\n totalFreezesDuration: null,\n freezeDurationSinceLast: null,\n freezeCount: null,\n jitter: null,\n packetsLost: null,\n nackCount: null,\n roundTripTime: null,\n width: null,\n height: null,\n networkType: null,\n powerEfficientDecoder: null\n };\n participantStats.stats.forEach(report => {\n var _handle$webrtcStuff4, _handle$webrtcStuff4$;\n const simulcastConfigForSource = this._findSimulcastConfig(participantStats.source, this.simulcastSettings);\n const simulcastMode = ((_handle$webrtcStuff4 = handle.webrtcStuff) === null || _handle$webrtcStuff4 === void 0 ? void 0 : (_handle$webrtcStuff4$ = _handle$webrtcStuff4.overriddenSimulcastMode[participantStats.mid]) === null || _handle$webrtcStuff4$ === void 0 ? void 0 : _handle$webrtcStuff4$.mode) || (simulcastConfigForSource === null || simulcastConfigForSource === void 0 ? void 0 : simulcastConfigForSource.mode);\n if (report.type === 'inbound-rtp' && report.kind === 'video') {\n var _handle$webrtcStuff$s3, _handle$webrtcStuff$s4, _handle$webrtcStuff$s5, _handle$webrtcStuff$s6, _handle$webrtcStuff$s7, _handle$webrtcStuff$s8, _handle$webrtcStuff$s9, _handle$webrtcStuff$s10, _handle$webrtcStuff$s11, _handle$webrtcStuff$s12;\n stats.framesPerSecond = report.framesPerSecond || 0;\n stats.framesDropped = report.framesDropped || 0;\n stats.totalFreezesDuration = report.totalFreezesDuration || 0;\n stats.freezeDurationSinceLast = (report.totalFreezesDuration || 0) - (((_handle$webrtcStuff$s3 = handle.webrtcStuff.stats) === null || _handle$webrtcStuff$s3 === void 0 ? void 0 : (_handle$webrtcStuff$s4 = _handle$webrtcStuff$s3[participantStats.mid]) === null || _handle$webrtcStuff$s4 === void 0 ? void 0 : (_handle$webrtcStuff$s5 = _handle$webrtcStuff$s4[((_handle$webrtcStuff$s6 = handle.webrtcStuff.stats) === null || _handle$webrtcStuff$s6 === void 0 ? void 0 : (_handle$webrtcStuff$s7 = _handle$webrtcStuff$s6[participantStats.mid]) === null || _handle$webrtcStuff$s7 === void 0 ? void 0 : _handle$webrtcStuff$s7.length) - 1]) === null || _handle$webrtcStuff$s5 === void 0 ? void 0 : _handle$webrtcStuff$s5.totalFreezesDuration) || 0);\n stats.freezeCount = report.freezeCount || 0;\n stats.freezeCountSinceLast = (report.freezeCount || 0) - (((_handle$webrtcStuff$s8 = handle.webrtcStuff.stats) === null || _handle$webrtcStuff$s8 === void 0 ? void 0 : (_handle$webrtcStuff$s9 = _handle$webrtcStuff$s8[participantStats.mid]) === null || _handle$webrtcStuff$s9 === void 0 ? void 0 : (_handle$webrtcStuff$s10 = _handle$webrtcStuff$s9[((_handle$webrtcStuff$s11 = handle.webrtcStuff.stats) === null || _handle$webrtcStuff$s11 === void 0 ? void 0 : (_handle$webrtcStuff$s12 = _handle$webrtcStuff$s11[participantStats.mid]) === null || _handle$webrtcStuff$s12 === void 0 ? void 0 : _handle$webrtcStuff$s12.length) - 1]) === null || _handle$webrtcStuff$s10 === void 0 ? void 0 : _handle$webrtcStuff$s10.freezeCount) || 0);\n stats.jitter = report.jitter;\n stats.packetsLost = report.packetsLost;\n stats.nackCount = report.nackCount;\n stats.width = report.frameWidth;\n stats.height = report.frameHeight;\n stats.powerEfficientDecoder = report.powerEfficientDecoder;\n }\n if (report.type === 'candidate-pair') {\n stats.roundTripTime = report.currentRoundTripTime;\n }\n if (report.type === 'local-candidate') {\n stats.networkType = report.networkType;\n }\n stats.selectedSubstream = handle.webrtcStuff.selectedSubstream[participantStats.mid];\n stats.simulcastMode = simulcastMode;\n });\n\n // pushing stats into handle stats array but keeping only 6 last stats\n handle.webrtcStuff.stats[participantStats.mid].push(stats);\n if (handle.webrtcStuff.stats[participantStats.mid].length > this._statsMaxLength) {\n handle.webrtcStuff.stats[participantStats.mid].shift();\n }\n this.emit('rtcStats', {\n handleId: participantStats.handle.handleId,\n stats,\n userId: (_decodeJanusDisplay12 = Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"decodeJanusDisplay\"])(participantStats.handle.userId)) === null || _decodeJanusDisplay12 === void 0 ? void 0 : _decodeJanusDisplay12.userId,\n source: participantStats.source,\n mid: participantStats.mid\n });\n }\n }\n });\n });\n }\n _getStats() {\n let type = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;\n return Promise.all(this._participants.map(participant => {\n let mediaTrack = [];\n if (type === 'video') {\n var _participant$webrtcSt, _participant$webrtcSt2;\n mediaTrack = (participant === null || participant === void 0 ? void 0 : (_participant$webrtcSt = participant.webrtcStuff) === null || _participant$webrtcSt === void 0 ? void 0 : (_participant$webrtcSt2 = _participant$webrtcSt.stream) === null || _participant$webrtcSt2 === void 0 ? void 0 : _participant$webrtcSt2.getVideoTracks()) || [];\n } else if (type === 'audio') {\n var _participant$webrtcSt3, _participant$webrtcSt4;\n mediaTrack = (participant === null || participant === void 0 ? void 0 : (_participant$webrtcSt3 = participant.webrtcStuff) === null || _participant$webrtcSt3 === void 0 ? void 0 : (_participant$webrtcSt4 = _participant$webrtcSt3.stream) === null || _participant$webrtcSt4 === void 0 ? void 0 : _participant$webrtcSt4.getAudioTracks()) || [];\n }\n if (type !== null) {\n var _participant$webrtcSt5, _participant$webrtcSt6;\n const transceivers = participant === null || participant === void 0 ? void 0 : (_participant$webrtcSt5 = participant.webrtcStuff) === null || _participant$webrtcSt5 === void 0 ? void 0 : (_participant$webrtcSt6 = _participant$webrtcSt5.pc) === null || _participant$webrtcSt6 === void 0 ? void 0 : _participant$webrtcSt6.getTransceivers();\n return Promise.all(mediaTrack.map(track => {\n var _transceivers$find;\n const source = Object.keys(participant.webrtcStuff.streamMap).find(s => participant.webrtcStuff.streamMap[s].find(t => t === track.id));\n const mid = (_transceivers$find = transceivers.find(t => {\n var _t$receiver, _t$receiver$track, _t$sender, _t$sender$track;\n return ((_t$receiver = t.receiver) === null || _t$receiver === void 0 ? void 0 : (_t$receiver$track = _t$receiver.track) === null || _t$receiver$track === void 0 ? void 0 : _t$receiver$track.id) === track.id || ((_t$sender = t.sender) === null || _t$sender === void 0 ? void 0 : (_t$sender$track = _t$sender.track) === null || _t$sender$track === void 0 ? void 0 : _t$sender$track.id) === track.id;\n })) === null || _transceivers$find === void 0 ? void 0 : _transceivers$find.mid;\n return participant.webrtcStuff.pc.getStats(track).then(r => ({\n stats: r,\n source,\n mid,\n handle: participant\n })).catch(e => Promise.reject({\n stats: null,\n error: e,\n handle: participant,\n source,\n mid\n }));\n }));\n } else {\n var _participant$webrtcSt7, _participant$webrtcSt8;\n return participant === null || participant === void 0 ? void 0 : (_participant$webrtcSt7 = participant.webrtcStuff) === null || _participant$webrtcSt7 === void 0 ? void 0 : (_participant$webrtcSt8 = _participant$webrtcSt7.pc) === null || _participant$webrtcSt8 === void 0 ? void 0 : _participant$webrtcSt8.getStats(null).then(r => ({\n handle: participant,\n stats: r\n })).catch(e => Promise.reject({\n handle: participant,\n error: e\n }));\n }\n }));\n }\n _resetStats(handleId, mid) {\n let handle = this._getHandle(handleId);\n if (handle) {\n let config = handle.webrtcStuff;\n if (!mid) {\n Object.keys(config.stats).forEach(mid => {\n config.stats[mid] = [config.stats[mid][config.stats[mid].length - 1]];\n });\n } else {\n // clearing stats for the new substream\n if (config.stats[mid]) {\n config.stats[mid] = [config.stats[mid][config.stats[mid].length - 1]];\n }\n }\n }\n }\n _sendTrickleCandidate(handleId, candidate) {\n return this._send({\n \"janus\": \"trickle\",\n \"candidate\": candidate,\n \"handle_id\": handleId\n }, false, false, 5);\n }\n _webrtc(handleId) {\n let enableOntrack = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;\n let handle = this._getHandle(handleId);\n if (!handle) {\n this.emit('error', {\n type: 'error',\n id: 15,\n message: 'id non-existent',\n data: [handleId, 'create rtc connection']\n });\n return;\n }\n let config = handle.webrtcStuff;\n if (!config.pc) {\n let pc_config = {\n \"iceServers\": this.iceServers,\n \"iceTransportPolicy\": 'all',\n \"bundlePolicy\": undefined\n };\n pc_config[\"sdpSemantics\"] = \"unified-plan\";\n let pc_constraints = {};\n if (webrtc_adapter__WEBPACK_IMPORTED_MODULE_0__[\"default\"].browserDetails.browser === \"edge\") {\n // This is Edge, enable BUNDLE explicitly\n pc_config.bundlePolicy = \"max-bundle\";\n }\n\n // pc_config.bundlePolicy = 'balanced';\n // pc_config.iceTransportPolicy = 'relay';\n // pc_config.rtcpMuxPolicy = \"negotiate\";\n\n this._log('new RTCPeerConnection', pc_config, pc_constraints);\n config.pc = new RTCPeerConnection(pc_config, pc_constraints);\n config.pc.onnegotiationneeded = () => {\n this._log('onnegotiationneeded');\n };\n config.pc.onconnectionstatechange = () => {\n if (config.pc.connectionState === 'failed') {\n this._log('connectionState failed');\n this._iceRestart(handleId);\n }\n this.emit('connectionState', [handleId, handleId === this.handleId, config.pc.connectionState]);\n if (handleId !== this.handleId && config.pc.connectionState === 'connected') {\n var _decodeJanusDisplay13, _decodeJanusDisplay14;\n this.emit(this._getAddParticipantEventName(handle.handleId), {\n tid: Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"generateUUID\"])(),\n id: handle.handleId,\n userId: (_decodeJanusDisplay13 = Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"decodeJanusDisplay\"])(handle.userId)) === null || _decodeJanusDisplay13 === void 0 ? void 0 : _decodeJanusDisplay13.userId,\n role: (_decodeJanusDisplay14 = Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"decodeJanusDisplay\"])(handle.userId)) === null || _decodeJanusDisplay14 === void 0 ? void 0 : _decodeJanusDisplay14.role,\n fullUserId: handle.userId,\n stream: config.stream,\n streamMap: structuredClone(config.streamMap),\n tracksMap: structuredClone(config.tracksMap),\n transceiverMap: structuredClone(config.transceiverMap),\n track: null,\n source: null,\n constructId: this.constructId,\n adding: false,\n removing: false,\n hasAudioTrack: !!(config.stream && config.stream.getAudioTracks().length),\n hasVideoTrack: !!(config.stream && config.stream.getVideoTracks().length)\n });\n }\n };\n config.pc.oniceconnectionstatechange = () => {\n if (config.pc.iceConnectionState === 'failed') {\n this._log('iceConnectionState failed');\n this._iceRestart(handleId);\n }\n this.emit('iceState', [handleId, handleId === this.handleId, config.pc.iceConnectionState]);\n if (handleId !== this.handleId && (config.pc.iceConnectionState === 'completed' || config.pc.iceConnectionState === 'connected')) {\n var _decodeJanusDisplay15, _decodeJanusDisplay16;\n this.emit(this._getAddParticipantEventName(handle.handleId), {\n tid: Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"generateUUID\"])(),\n id: handle.handleId,\n userId: (_decodeJanusDisplay15 = Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"decodeJanusDisplay\"])(handle.userId)) === null || _decodeJanusDisplay15 === void 0 ? void 0 : _decodeJanusDisplay15.userId,\n role: (_decodeJanusDisplay16 = Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"decodeJanusDisplay\"])(handle.userId)) === null || _decodeJanusDisplay16 === void 0 ? void 0 : _decodeJanusDisplay16.role,\n fullUserId: handle.userId,\n stream: config.stream,\n streamMap: structuredClone(config.streamMap),\n tracksMap: structuredClone(config.tracksMap),\n transceiverMap: structuredClone(config.transceiverMap),\n track: null,\n source: null,\n constructId: this.constructId,\n adding: false,\n hasAudioTrack: !!(config.stream && config.stream.getAudioTracks().length),\n hasVideoTrack: !!(config.stream && config.stream.getVideoTracks().length)\n });\n }\n };\n config.pc.onicecandidate = event => {\n if (event.candidate == null || webrtc_adapter__WEBPACK_IMPORTED_MODULE_0__[\"default\"].browserDetails.browser === 'edge' && event.candidate.candidate.indexOf('endOfCandidates') > 0) {\n config.iceDone = true;\n this._sendTrickleCandidate(handleId, {\n \"completed\": true\n }).catch(e => {\n this.emit('error', e);\n });\n } else {\n // JSON.stringify doesn't work on some WebRTC objects anymore\n // See https://code.google.com/p/chromium/issues/detail?id=467366\n var candidate = {\n \"candidate\": event.candidate.candidate,\n \"sdpMid\": event.candidate.sdpMid,\n \"sdpMLineIndex\": event.candidate.sdpMLineIndex\n };\n this._sendTrickleCandidate(handleId, candidate).catch(e => {\n this.emit('error', e);\n });\n }\n };\n if (enableOntrack) {\n config.pc.ontrack = event => {\n var _event$streams, _event$streams$;\n if (!event.streams) return;\n if (!config.stream) {\n config.stream = new MediaStream();\n }\n if (!((_event$streams = event.streams) !== null && _event$streams !== void 0 && (_event$streams$ = _event$streams[0]) !== null && _event$streams$ !== void 0 && _event$streams$.onremovetrack)) {\n event.streams[0].onremovetrack = ev => {\n var _config$stream, _config$pc, _config$pc$getTransce4, _decodeJanusDisplay17, _decodeJanusDisplay18;\n this._log('Remote track removed', ev);\n (_config$stream = config.stream) === null || _config$stream === void 0 ? void 0 : _config$stream.removeTrack(ev.track);\n\n // check if handle still exists\n if (!this._getHandle(handle.handleId)) {\n return;\n }\n this._updateRemoteParticipantStreamMap(handle.handleId);\n let transceiver = (_config$pc = config.pc) === null || _config$pc === void 0 ? void 0 : (_config$pc$getTransce4 = _config$pc.getTransceivers()) === null || _config$pc$getTransce4 === void 0 ? void 0 : _config$pc$getTransce4.find(t => t.receiver.track === ev.track);\n let mid = (transceiver === null || transceiver === void 0 ? void 0 : transceiver.mid) || ev.track.id;\n let source = Object.keys(config.streamMap).find(key => config.streamMap[key].includes(ev.track.id));\n this.emit(this._getAddParticipantEventName(handle.handleId), {\n tid: Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"generateUUID\"])(),\n id: handle.handleId,\n mid,\n constructId: this.constructId,\n userId: (_decodeJanusDisplay17 = Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"decodeJanusDisplay\"])(handle.userId)) === null || _decodeJanusDisplay17 === void 0 ? void 0 : _decodeJanusDisplay17.userId,\n role: (_decodeJanusDisplay18 = Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"decodeJanusDisplay\"])(handle.userId)) === null || _decodeJanusDisplay18 === void 0 ? void 0 : _decodeJanusDisplay18.role,\n fullUserId: handle.userId,\n stream: config.stream,\n streamMap: structuredClone(config.streamMap),\n tracksMap: structuredClone(config.tracksMap),\n transceiverMap: structuredClone(config.transceiverMap),\n source,\n track: ev.track,\n adding: false,\n removing: true,\n hasAudioTrack: !!(config.stream && config.stream.getAudioTracks().length),\n hasVideoTrack: !!(config.stream && config.stream.getVideoTracks().length)\n });\n };\n }\n if (event.track) {\n var _config$stream2, _decodeJanusDisplay19, _decodeJanusDisplay20;\n (_config$stream2 = config.stream) === null || _config$stream2 === void 0 ? void 0 : _config$stream2.addTrack(event.track);\n this._updateRemoteParticipantStreamMap(handle.handleId);\n let mid = event.transceiver ? event.transceiver.mid : event.track.id;\n let source = Object.keys(config.streamMap).find(key => config.streamMap[key].includes(event.track.id));\n if (event.track.kind === 'video') {\n this.requestKeyFrame(handle.handleId, mid);\n }\n this.emit(this._getAddParticipantEventName(handle.handleId), {\n tid: Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"generateUUID\"])(),\n mid,\n id: handle.handleId,\n userId: (_decodeJanusDisplay19 = Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"decodeJanusDisplay\"])(handle.userId)) === null || _decodeJanusDisplay19 === void 0 ? void 0 : _decodeJanusDisplay19.userId,\n role: (_decodeJanusDisplay20 = Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"decodeJanusDisplay\"])(handle.userId)) === null || _decodeJanusDisplay20 === void 0 ? void 0 : _decodeJanusDisplay20.role,\n fullUserId: handle.userId,\n stream: config.stream,\n streamMap: structuredClone(config.streamMap),\n tracksMap: structuredClone(config.tracksMap),\n transceiverMap: structuredClone(config.transceiverMap),\n source,\n track: event.track,\n constructId: this.constructId,\n adding: true,\n removing: false,\n hasAudioTrack: !!(config.stream && config.stream.getAudioTracks().length),\n hasVideoTrack: !!(config.stream && config.stream.getVideoTracks().length)\n });\n if (event.track.onended) return;\n event.track.onended = ev => {\n var _config$stream3, _config$pc2, _config$pc2$getTransc, _decodeJanusDisplay21, _decodeJanusDisplay22;\n this._log('Remote track ended');\n (_config$stream3 = config.stream) === null || _config$stream3 === void 0 ? void 0 : _config$stream3.removeTrack(ev.target);\n // check if handle still exists\n if (!this._getHandle(handle.handleId)) {\n return;\n }\n this._updateRemoteParticipantStreamMap(handle.handleId);\n let transceiver = (_config$pc2 = config.pc) === null || _config$pc2 === void 0 ? void 0 : (_config$pc2$getTransc = _config$pc2.getTransceivers()) === null || _config$pc2$getTransc === void 0 ? void 0 : _config$pc2$getTransc.find(t => t.receiver.track === ev.target);\n let mid = (transceiver === null || transceiver === void 0 ? void 0 : transceiver.mid) || ev.target.id;\n let source = Object.keys(config.streamMap).find(key => config.streamMap[key].includes(ev.target.id));\n this.emit(this._getAddParticipantEventName(handle.handleId), {\n tid: Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"generateUUID\"])(),\n id: handle.handleId,\n mid,\n constructId: this.constructId,\n userId: (_decodeJanusDisplay21 = Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"decodeJanusDisplay\"])(handle.userId)) === null || _decodeJanusDisplay21 === void 0 ? void 0 : _decodeJanusDisplay21.userId,\n role: (_decodeJanusDisplay22 = Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"decodeJanusDisplay\"])(handle.userId)) === null || _decodeJanusDisplay22 === void 0 ? void 0 : _decodeJanusDisplay22.role,\n fullUserId: handle.userId,\n stream: config.stream,\n streamMap: structuredClone(config.streamMap),\n tracksMap: structuredClone(config.tracksMap),\n transceiverMap: structuredClone(config.transceiverMap),\n source,\n track: ev.target,\n adding: false,\n removing: true,\n hasAudioTrack: !!(config.stream && config.stream.getAudioTracks().length),\n hasVideoTrack: !!(config.stream && config.stream.getVideoTracks().length)\n });\n };\n let mutedTimerId = {};\n let waitPeriod = 300; // ms\n let screenShareWaitPeriod = 5000; // ms\n\n event.track.onmute = ev => {\n var _decodeJanusDisplay23, _decodeJanusDisplay24;\n this._log('Remote track muted');\n let transceiver = config.pc.getTransceivers().find(t => t.receiver.track === ev.target);\n let mid = transceiver.mid || ev.target.id;\n let source = Object.keys(config.streamMap).find(key => config.streamMap[key].includes(ev.target.id));\n this.emit('remoteTrackMuted', {\n id: handle.handleId,\n mid,\n constructId: this.constructId,\n userId: (_decodeJanusDisplay23 = Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"decodeJanusDisplay\"])(handle.userId)) === null || _decodeJanusDisplay23 === void 0 ? void 0 : _decodeJanusDisplay23.userId,\n role: (_decodeJanusDisplay24 = Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"decodeJanusDisplay\"])(handle.userId)) === null || _decodeJanusDisplay24 === void 0 ? void 0 : _decodeJanusDisplay24.role,\n fullUserId: handle.userId,\n stream: config.stream,\n streamMap: structuredClone(config.streamMap),\n source,\n kind: ev.target.kind,\n track: ev.target,\n muted: true\n });\n\n // when a track is muted, we try to switch to lower quality substream, but not for screen sharing\n\n if (!this.simulcast) {\n return;\n }\n const wPeriod = source.indexOf('screen') > -1 ? screenShareWaitPeriod : waitPeriod;\n if (!mutedTimerId[mid]) {\n mutedTimerId[mid] = setTimeout(() => {\n var _handle$webrtcStuff5, _handle$webrtcStuff5$;\n mutedTimerId[mid] = null;\n const simulcastConfigForSource = this._findSimulcastConfig(source, this.simulcastSettings);\n const simulcastMode = ((_handle$webrtcStuff5 = handle.webrtcStuff) === null || _handle$webrtcStuff5 === void 0 ? void 0 : (_handle$webrtcStuff5$ = _handle$webrtcStuff5.overriddenSimulcastMode[mid]) === null || _handle$webrtcStuff5$ === void 0 ? void 0 : _handle$webrtcStuff5$.mode) || (simulcastConfigForSource === null || simulcastConfigForSource === void 0 ? void 0 : simulcastConfigForSource.mode);\n const {\n simulcastBitrates\n } = handle.webrtcStuff.tracksMap.find(t => t.mid === mid) || {};\n\n // track is gone\n if (!simulcastBitrates) {\n return;\n }\n const currentSubstream = handle.webrtcStuff.selectedSubstream[mid];\n if (!(simulcastMode === 'browserControlled') && ev.target.kind === 'video' && currentSubstream < simulcastBitrates.length - 1) {\n this._log('Attempting to down the quality due to track muted');\n this.selectSubStream(handle.handleId, currentSubstream + 1, undefined, mid, false).catch(reason => this._log(`Changing substream for mid: ${mid} failed. Reason: ${reason}`));\n }\n }, wPeriod);\n }\n };\n event.track.onunmute = ev => {\n var _decodeJanusDisplay25, _decodeJanusDisplay26;\n this._log('Remote track unmuted');\n let transceiver = config.pc.getTransceivers().find(t => t.receiver.track === ev.target);\n let mid = transceiver.mid || ev.target.id;\n let source = Object.keys(config.streamMap).find(key => config.streamMap[key].includes(ev.target.id));\n if (mutedTimerId[mid]) {\n clearTimeout(mutedTimerId[mid]);\n mutedTimerId[mid] = null;\n }\n this.emit('remoteTrackMuted', {\n id: handle.handleId,\n mid,\n constructId: this.constructId,\n userId: (_decodeJanusDisplay25 = Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"decodeJanusDisplay\"])(handle.userId)) === null || _decodeJanusDisplay25 === void 0 ? void 0 : _decodeJanusDisplay25.userId,\n role: (_decodeJanusDisplay26 = Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"decodeJanusDisplay\"])(handle.userId)) === null || _decodeJanusDisplay26 === void 0 ? void 0 : _decodeJanusDisplay26.role,\n fullUserId: handle.userId,\n stream: config.stream,\n streamMap: structuredClone(config.streamMap),\n source,\n kind: ev.target.kind,\n track: ev.target,\n muted: false\n });\n };\n }\n };\n }\n }\n if (!config.dataChannel || !config.dataChannelOpen) {\n config.dataChannel = {};\n var onDataChannelMessage = event => {\n this._handleDataEvents(handleId, 'message', event.data);\n };\n var onDataChannelStateChange = event => {\n let label = event.target.label;\n let protocol = event.target.protocol;\n let state = config.dataChannel[label] ? config.dataChannel[label].readyState : \"null\";\n this._handleDataEvents(handleId, 'state', {\n state,\n label\n });\n };\n var onDataChannelError = error => {\n var _error$channel;\n this._handleDataEvents(handleId, 'error', {\n label: error === null || error === void 0 ? void 0 : (_error$channel = error.channel) === null || _error$channel === void 0 ? void 0 : _error$channel.label,\n error\n });\n };\n const createDataChannel = (label, protocol, incoming) => {\n let options = {\n ordered: true\n };\n if (!incoming) {\n if (protocol) {\n options = {\n ...options,\n protocol\n };\n }\n config.dataChannel[label] = config.pc.createDataChannel(label, options);\n } else {\n config.dataChannel[label] = incoming;\n }\n config.dataChannel[label].onmessage = onDataChannelMessage;\n config.dataChannel[label].onopen = onDataChannelStateChange;\n config.dataChannel[label].onclose = onDataChannelStateChange;\n config.dataChannel[label].onerror = onDataChannelError;\n };\n createDataChannel(this.defaultDataChannelLabel, null, null);\n config.pc.ondatachannel = function (event) {\n createDataChannel(event.channel.label, event.channel.protocol, event.channel);\n };\n }\n }\n _webrtcPeer(handleId, jsep) {\n let handle = this._getHandle(handleId);\n if (!handle) {\n return Promise.reject({\n type: 'warning',\n id: 15,\n message: 'id non-existent',\n data: [handleId, 'rtc peer']\n });\n }\n var config = handle.webrtcStuff;\n if (jsep !== undefined && jsep !== null) {\n if (config.pc === null) {\n this._log(\"No PeerConnection: if this is an answer, use createAnswer and not _webrtcPeer\");\n return Promise.resolve(null);\n }\n return config.pc.setRemoteDescription(jsep).then(() => {\n config.remoteSdp = jsep.sdp;\n // Any trickle candidate we cached?\n if (config.candidates && config.candidates.length > 0) {\n for (var i = 0; i < config.candidates.length; i++) {\n var candidate = config.candidates[i];\n if (!candidate || candidate.completed === true) {\n config.pc.addIceCandidate(null);\n } else {\n config.pc.addIceCandidate(candidate);\n }\n }\n config.candidates = [];\n }\n // Done\n return true;\n }).catch(e => {\n return Promise.reject({\n type: 'warning',\n id: 32,\n message: 'rtc peer',\n data: [handleId, e]\n });\n });\n } else {\n return Promise.reject({\n type: 'warning',\n id: 22,\n message: 'rtc peer',\n data: [handleId, 'invalid jsep']\n });\n }\n }\n _iceRestart(handleId) {\n let handle = this._getHandle(handleId);\n if (!handle) {\n return;\n }\n var config = handle.webrtcStuff;\n\n // Already restarting;\n if (config.isIceRestarting) {\n return;\n }\n config.isIceRestarting = true;\n if (this.handleId === handleId) {\n this._log('Performing local ICE restart');\n let hasAudio = !!(config.stream && config.stream.getAudioTracks().length > 0);\n let hasVideo = !!(config.stream && config.stream.getVideoTracks().length > 0);\n this._createAO('offer', handleId, true).then(jsep => {\n if (!jsep) {\n return null;\n }\n return this.sendMessage(handleId, {\n body: {\n \"request\": \"configure\",\n \"keyframe\": true,\n \"audio\": hasAudio,\n \"video\": hasVideo,\n \"data\": true,\n ...(this.recordingFilename ? {\n filename: this.recordingFilename\n } : {})\n },\n jsep\n }, false, false, 5);\n }).then(r => {\n config.isIceRestarting = false;\n this._log('ICE restart success');\n }).catch(e => {\n config.isIceRestarting = false;\n this.emit('error', {\n type: 'warning',\n id: 28,\n message: 'iceRestart failed',\n data: e\n });\n });\n } else {\n this._log('Performing remote ICE restart', handleId);\n return this.sendMessage(handleId, {\n body: {\n \"request\": \"configure\",\n \"restart\": true\n }\n }, false, false, 5).then(() => {}).then(() => {\n config.isIceRestarting = false;\n this._log('ICE restart success');\n }).catch(() => {\n config.isIceRestarting = false;\n });\n }\n }\n _setupTransceivers(handleId, _ref) {\n let [audioSend, audioRecv, videoSend, videoRecv, audioTransceiver = null, videoTransceiver = null] = _ref;\n //TODO: this should be refactored to use handle's trackMap so we dont have to pass any parameters\n\n let handle = this._getHandle(handleId);\n if (!handle) {\n return null;\n }\n let config = handle.webrtcStuff;\n const setTransceiver = function (transceiver, send, recv) {\n let kind = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 'audio';\n if (!send && !recv) {\n // disabled: have we removed it?\n if (transceiver) {\n if (transceiver.setDirection) {\n transceiver.setDirection(\"inactive\");\n } else {\n transceiver.direction = \"inactive\";\n }\n }\n } else {\n if (send && recv) {\n if (transceiver) {\n if (transceiver.setDirection) {\n transceiver.setDirection(\"sendrecv\");\n } else {\n transceiver.direction = \"sendrecv\";\n }\n }\n } else if (send && !recv) {\n if (transceiver) {\n if (transceiver.setDirection) {\n transceiver.setDirection(\"sendonly\");\n } else {\n transceiver.direction = \"sendonly\";\n }\n }\n } else if (!send && recv) {\n if (transceiver) {\n if (transceiver.setDirection) {\n transceiver.setDirection(\"recvonly\");\n } else {\n transceiver.direction = \"recvonly\";\n }\n } else {\n // In theory, this is the only case where we might not have a transceiver yet\n config.pc.addTransceiver(kind, {\n direction: \"recvonly\"\n });\n }\n }\n }\n };\n\n // if we're passing any transceivers, we work only on them, doesn't matter if one of them is null\n if (audioTransceiver || videoTransceiver) {\n if (audioTransceiver) {\n setTransceiver(audioTransceiver, audioSend, audioRecv, 'audio');\n }\n if (videoTransceiver) {\n setTransceiver(videoTransceiver, videoSend, videoRecv, 'video');\n }\n }\n // else we work on all transceivers\n else {\n let transceivers = config.pc.getTransceivers();\n if (transceivers && transceivers.length > 0) {\n for (let i in transceivers) {\n let t = transceivers[i];\n if (t.sender && t.sender.track && t.sender.track.kind === \"audio\" || t.receiver && t.receiver.track && t.receiver.track.kind === \"audio\") {\n setTransceiver(t, audioSend, audioRecv, 'audio');\n }\n if (t.sender && t.sender.track && t.sender.track.kind === \"video\" || t.receiver && t.receiver.track && t.receiver.track.kind === \"video\") {\n setTransceiver(t, videoSend, videoRecv, 'video');\n }\n }\n }\n }\n }\n _createAO() {\n let type = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'offer';\n let handleId = arguments.length > 1 ? arguments[1] : undefined;\n let iceRestart = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;\n let handle = this._getHandle(handleId);\n if (!handle) {\n return Promise.reject({\n type: 'warning',\n id: 15,\n message: 'id non-existent',\n data: [handleId, 'createAO', type]\n });\n }\n let methodName = null;\n if (type === 'offer') {\n methodName = 'createOffer';\n } else {\n methodName = 'createAnswer';\n }\n let config = handle.webrtcStuff;\n let mediaConstraints = {};\n if (iceRestart) {\n mediaConstraints[\"iceRestart\"] = true;\n }\n return config.pc[methodName](mediaConstraints).then(response => {\n // if type offer and its me and we want dtx we mungle the sdp\n if (handleId === this.handleId && type === 'offer' && this.enableDtx) {\n // enable DTX\n response.sdp = response.sdp.replace(\"useinbandfec=1\", \"useinbandfec=1;usedtx=1\");\n }\n config.mySdp = response.sdp;\n let _p = config.pc.setLocalDescription(response).catch(e => {\n return Promise.reject({\n type: 'warning',\n id: 24,\n message: 'setLocalDescription',\n data: [handleId, e]\n });\n });\n config.mediaConstraints = mediaConstraints;\n if (!config.iceDone && !config.trickle) {\n // Don't do anything until we have all candidates\n return Promise.resolve(null);\n }\n\n // JSON.stringify doesn't work on some WebRTC objects anymore\n // See https://code.google.com/p/chromium/issues/detail?id=467366\n var jsep = {\n \"type\": response.type,\n \"sdp\": response.sdp\n };\n if (response.e2ee) jsep.e2ee = true;\n if (response.rid_order === \"hml\" || response.rid_order === \"lmh\") jsep.rid_order = response.rid_order;\n if (response.force_relay) jsep.force_relay = true;\n return _p.then(() => jsep);\n }, e => {\n return Promise.reject({\n type: 'warning',\n id: 25,\n message: methodName,\n data: [handleId, e]\n });\n });\n }\n _publishRemote(handleId, jsep) {\n let handle = this._getHandle(handleId);\n if (!handle) {\n return Promise.reject({\n type: 'warning',\n id: 15,\n message: 'id non-existent',\n data: [handleId, 'publish remote participant']\n });\n }\n this._webrtc(handleId, true);\n let config = handle.webrtcStuff;\n if (jsep) {\n return config.pc.setRemoteDescription(jsep).then(() => {\n config.remoteSdp = jsep.sdp;\n // Any trickle candidate we cached?\n if (config.candidates && config.candidates.length > 0) {\n for (var i = 0; i < config.candidates.length; i++) {\n var candidate = config.candidates[i];\n if (!candidate || candidate.completed === true) {\n // end-of-candidates\n config.pc.addIceCandidate(null);\n } else {\n // New candidate\n config.pc.addIceCandidate(candidate);\n }\n }\n config.candidates = [];\n }\n this._setupTransceivers(handleId, [false, true, false, true]);\n\n // Create the answer now\n return this._createAO('answer', handleId, false).then(_jsep => {\n if (!_jsep) {\n this.emit('error', {\n type: 'warning',\n id: 19,\n message: 'publish remote participant',\n data: [handleId, 'no jsep']\n });\n return Promise.resolve();\n }\n return this.sendMessage(handleId, {\n \"body\": {\n \"request\": \"start\",\n ...(this.roomId && {\n \"room\": this.roomId\n }),\n ...(this.pin && {\n pin: this.pin\n })\n },\n \"jsep\": _jsep\n });\n });\n }, e => Promise.reject({\n type: 'warning',\n id: 23,\n message: 'setRemoteDescription',\n data: [handleId, e]\n }));\n } else {\n return Promise.resolve();\n }\n }\n\n //Public methods\n _republishOnTrackEnded(source) {\n let handle = this._getHandle(this.handleId);\n if (!handle) {\n return;\n }\n let config = handle.webrtcStuff;\n if (!config.stream) {\n return;\n }\n let sourceTrackIds = config.streamMap[source] || [];\n let remainingTracks = [];\n for (let i = 0; i < sourceTrackIds.length; i++) {\n let foundTrack = config.stream.getTracks().find(t => t.id === sourceTrackIds[i]);\n if (foundTrack) {\n remainingTracks.push(foundTrack);\n }\n }\n if (remainingTracks.length) {\n let stream = new MediaStream();\n remainingTracks.forEach(track => stream.addTrack(track));\n return this.publishLocal(stream, source);\n } else {\n return this.publishLocal(null, source);\n }\n }\n publishLocal() {\n var _stream$getVideoTrack, _stream$getAudioTrack, _config$stream4, _config$stream4$getAu, _config$stream5, _config$stream5$getVi, _stream$getAudioTrack2, _stream$getVideoTrack2;\n let stream = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;\n let source = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'camera0';\n if (this.isDisconnecting || !this.isConnected) {\n return Promise.reject({\n type: 'warning',\n id: 18,\n message: 'Either not connected or disconnecting'\n });\n }\n if ((stream === null || stream === void 0 ? void 0 : (_stream$getVideoTrack = stream.getVideoTracks()) === null || _stream$getVideoTrack === void 0 ? void 0 : _stream$getVideoTrack.length) > 1) {\n return Promise.reject({\n type: 'warning',\n id: 30,\n message: 'multiple video tracks not supported',\n data: null\n });\n }\n if ((stream === null || stream === void 0 ? void 0 : (_stream$getAudioTrack = stream.getAudioTracks()) === null || _stream$getAudioTrack === void 0 ? void 0 : _stream$getAudioTrack.length) > 1) {\n return Promise.reject({\n type: 'warning',\n id: 30,\n message: 'multiple audio tracks not supported',\n data: null\n });\n }\n let handle = this._getHandle(this.handleId);\n if (!handle) {\n return Promise.reject({\n type: 'error',\n id: 31,\n message: 'no local id, connect before publishing',\n data: null\n });\n }\n this.emit('publishing', true);\n this._webrtc(this.handleId);\n let config = handle.webrtcStuff;\n if (!config.stream) {\n config.stream = new MediaStream();\n }\n let needsNegotiation = !this.isPublished;\n let transceivers = config.pc.getTransceivers();\n let existingTracks = [...(config.streamMap[source] || [])];\n if (stream !== null && stream !== void 0 && stream.getTracks().length) {\n var _stream$getTracks;\n config.streamMap[source] = (stream === null || stream === void 0 ? void 0 : (_stream$getTracks = stream.getTracks()) === null || _stream$getTracks === void 0 ? void 0 : _stream$getTracks.map(track => track.id)) || [];\n } else {\n delete config.streamMap[source];\n }\n\n // remove old audio track related to this source\n let oldAudioStream = config === null || config === void 0 ? void 0 : (_config$stream4 = config.stream) === null || _config$stream4 === void 0 ? void 0 : (_config$stream4$getAu = _config$stream4.getAudioTracks()) === null || _config$stream4$getAu === void 0 ? void 0 : _config$stream4$getAu.find(track => existingTracks.includes(track.id));\n if (oldAudioStream) {\n try {\n oldAudioStream.stop();\n } catch (e) {\n this._log(e);\n }\n config.stream.removeTrack(oldAudioStream);\n }\n\n // remove old video track related to this source\n let oldVideoStream = config === null || config === void 0 ? void 0 : (_config$stream5 = config.stream) === null || _config$stream5 === void 0 ? void 0 : (_config$stream5$getVi = _config$stream5.getVideoTracks()) === null || _config$stream5$getVi === void 0 ? void 0 : _config$stream5$getVi.find(track => existingTracks.includes(track.id));\n if (oldVideoStream) {\n try {\n oldVideoStream.stop();\n } catch (e) {\n this._log(e);\n }\n config.stream.removeTrack(oldVideoStream);\n }\n const simulcastConfigForSource = this._findSimulcastConfig(source, this.simulcastSettings);\n let audioTrackReplacePromise = Promise.resolve();\n let videoTrackReplacePromise = Promise.resolve();\n let audioTransceiver = null;\n let videoTransceiver = null;\n let replaceAudio = stream === null || stream === void 0 ? void 0 : (_stream$getAudioTrack2 = stream.getAudioTracks()) === null || _stream$getAudioTrack2 === void 0 ? void 0 : _stream$getAudioTrack2.length;\n let replaceVideo = stream === null || stream === void 0 ? void 0 : (_stream$getVideoTrack2 = stream.getVideoTracks()) === null || _stream$getVideoTrack2 === void 0 ? void 0 : _stream$getVideoTrack2.length;\n for (const transceiver of transceivers) {\n var _transceiver$sender, _transceiver$sender$t, _transceiver$sender2, _transceiver$sender2$, _transceiver$sender3, _transceiver$sender3$, _transceiver$sender4, _transceiver$sender4$, _transceiver$sender5, _transceiver$sender5$, _transceiver$sender5$2, _transceiver$sender6, _transceiver$sender6$, _transceiver$sender6$2;\n if (['sendonly', 'sendrecv'].includes(transceiver.currentDirection) && ((_transceiver$sender = transceiver.sender) === null || _transceiver$sender === void 0 ? void 0 : (_transceiver$sender$t = _transceiver$sender.track) === null || _transceiver$sender$t === void 0 ? void 0 : _transceiver$sender$t.kind) === 'audio' && existingTracks.includes((_transceiver$sender2 = transceiver.sender) === null || _transceiver$sender2 === void 0 ? void 0 : (_transceiver$sender2$ = _transceiver$sender2.track) === null || _transceiver$sender2$ === void 0 ? void 0 : _transceiver$sender2$.id)) {\n audioTransceiver = transceiver;\n } else if (['sendonly', 'sendrecv'].includes(transceiver.currentDirection) && ((_transceiver$sender3 = transceiver.sender) === null || _transceiver$sender3 === void 0 ? void 0 : (_transceiver$sender3$ = _transceiver$sender3.track) === null || _transceiver$sender3$ === void 0 ? void 0 : _transceiver$sender3$.kind) === 'video' && existingTracks.includes((_transceiver$sender4 = transceiver.sender) === null || _transceiver$sender4 === void 0 ? void 0 : (_transceiver$sender4$ = _transceiver$sender4.track) === null || _transceiver$sender4$ === void 0 ? void 0 : _transceiver$sender4$.id)) {\n videoTransceiver = transceiver;\n }\n\n // Reusing existing transceivers\n // TODO: if we start using different codecs for different sources, we need to check for that here\n else if (transceiver.currentDirection === 'inactive' && (_transceiver$sender5 = transceiver.sender) !== null && _transceiver$sender5 !== void 0 && (_transceiver$sender5$ = _transceiver$sender5.getParameters()) !== null && _transceiver$sender5$ !== void 0 && (_transceiver$sender5$2 = _transceiver$sender5$.codecs) !== null && _transceiver$sender5$2 !== void 0 && _transceiver$sender5$2.find(c => c.mimeType.indexOf('audio') > -1) && replaceAudio && !audioTransceiver) {\n audioTransceiver = transceiver;\n needsNegotiation = true;\n } else if (transceiver.currentDirection === 'inactive' && (_transceiver$sender6 = transceiver.sender) !== null && _transceiver$sender6 !== void 0 && (_transceiver$sender6$ = _transceiver$sender6.getParameters()) !== null && _transceiver$sender6$ !== void 0 && (_transceiver$sender6$2 = _transceiver$sender6$.codecs) !== null && _transceiver$sender6$2 !== void 0 && _transceiver$sender6$2.find(c => c.mimeType.indexOf('video') > -1) && replaceVideo && !videoTransceiver) {\n videoTransceiver = transceiver;\n needsNegotiation = true;\n }\n }\n if (replaceAudio) {\n config.stream.addTrack(stream.getAudioTracks()[0]);\n if (audioTransceiver && audioTransceiver.sender) {\n audioTrackReplacePromise = audioTransceiver.sender.replaceTrack(stream.getAudioTracks()[0]);\n } else {\n config.pc.addTrack(stream.getAudioTracks()[0], config.stream);\n needsNegotiation = true;\n }\n } else {\n if (audioTransceiver && audioTransceiver.sender) {\n audioTrackReplacePromise = audioTransceiver.sender.replaceTrack(null);\n needsNegotiation = true;\n }\n }\n if (replaceVideo) {\n config.stream.addTrack(stream.getVideoTracks()[0]);\n if (videoTransceiver && videoTransceiver.sender) {\n videoTrackReplacePromise = videoTransceiver.sender.replaceTrack(stream.getVideoTracks()[0]);\n } else {\n if (!this.simulcast) {\n config.pc.addTrack(stream.getVideoTracks()[0], config.stream);\n } else {\n if (webrtc_adapter__WEBPACK_IMPORTED_MODULE_0__[\"default\"].browserDetails.browser !== 'firefox') {\n // standard\n config.pc.addTransceiver(stream.getVideoTracks()[0], {\n direction: 'sendonly',\n streams: [config.stream],\n sendEncodings: structuredClone(simulcastConfigForSource === null || simulcastConfigForSource === void 0 ? void 0 : simulcastConfigForSource.bitrates)\n });\n } else {\n // firefox\n let transceiver = config.pc.addTransceiver(stream.getVideoTracks()[0], {\n direction: 'sendonly',\n streams: [config.stream]\n });\n let sender = transceiver ? transceiver.sender : null;\n if (sender) {\n let parameters = sender.getParameters() || {};\n parameters.encodings = stream.getVideoTracks()[0].sendEncodings || structuredClone(simulcastConfigForSource === null || simulcastConfigForSource === void 0 ? void 0 : simulcastConfigForSource.bitrates);\n sender.setParameters(parameters);\n }\n }\n }\n needsNegotiation = true;\n }\n } else {\n if (videoTransceiver && videoTransceiver.sender) {\n videoTrackReplacePromise = videoTransceiver.sender.replaceTrack(null);\n needsNegotiation = true;\n }\n }\n this.isAudioEnabed = !!(config.stream && config.stream.getAudioTracks().length > 0);\n this.isVideoEnabled = !!(config.stream && config.stream.getVideoTracks().length > 0);\n\n // we possibly created new transceivers, so we need to get them again\n transceivers = config.pc.getTransceivers();\n existingTracks = [...(config.streamMap[source] || [])];\n if (!audioTransceiver) {\n audioTransceiver = transceivers.find(transceiver => transceiver.sender.track && transceiver.sender.track.kind === 'audio' && existingTracks.includes(transceiver.sender.track.id));\n }\n if (!videoTransceiver) {\n videoTransceiver = transceivers.find(transceiver => transceiver.sender.track && transceiver.sender.track.kind === 'video' && existingTracks.includes(transceiver.sender.track.id));\n }\n let hasAudio = !!(stream && stream.getAudioTracks().length > 0);\n let hasVideo = !!(stream && stream.getVideoTracks().length > 0);\n this._setupTransceivers(this.handleId, [hasAudio, false, hasVideo, false, audioTransceiver, videoTransceiver]);\n const emitEvents = () => {\n var _decodeJanusDisplay27, _decodeJanusDisplay28;\n this.isPublished = true;\n if (!config.stream.onremovetrack) {\n config.stream.onremovetrack = ev => {};\n }\n let republishTimeoutId = null;\n let tracks = config.stream.getTracks();\n this.emit('addLocalParticipant', {\n tid: Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"generateUUID\"])(),\n id: handle.handleId,\n userId: (_decodeJanusDisplay27 = Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"decodeJanusDisplay\"])(handle.userId)) === null || _decodeJanusDisplay27 === void 0 ? void 0 : _decodeJanusDisplay27.userId,\n role: (_decodeJanusDisplay28 = Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"decodeJanusDisplay\"])(this.display)) === null || _decodeJanusDisplay28 === void 0 ? void 0 : _decodeJanusDisplay28.role,\n stream: tracks.length ? config.stream : null,\n // that null is there due to backward compatibility\n track: null,\n streamMap: structuredClone(config.streamMap),\n source,\n adding: false,\n removing: false,\n constructId: this.constructId,\n hasAudioTrack: hasAudio,\n hasVideoTrack: hasVideo\n });\n if (tracks.length) {\n tracks.forEach(track => {\n // used as a flag to not emit tracks that been already emitted\n if (!track.onended) {\n var _decodeJanusDisplay29, _decodeJanusDisplay30;\n this.emit('addLocalParticipant', {\n tid: Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"generateUUID\"])(),\n id: handle.handleId,\n userId: (_decodeJanusDisplay29 = Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"decodeJanusDisplay\"])(handle.userId)) === null || _decodeJanusDisplay29 === void 0 ? void 0 : _decodeJanusDisplay29.userId,\n role: (_decodeJanusDisplay30 = Object(_wt_utils__WEBPACK_IMPORTED_MODULE_2__[\"decodeJanusDisplay\"])(this.display)) === null || _decodeJanusDisplay30 === void 0 ? void 0 : _decodeJanusDisplay30.role,\n track,\n stream: config.stream,\n streamMap: structuredClone(config.streamMap),\n source,\n adding: true,\n removing: false,\n constructId: this.constructId,\n hasAudioTrack: hasAudio,\n hasVideoTrack: hasVideo\n });\n track.onended = ev => {\n config.stream.removeTrack(track);\n clearTimeout(republishTimeoutId);\n republishTimeoutId = setTimeout(() => {\n this._republishOnTrackEnded(source);\n }, 100);\n };\n }\n });\n }\n this.isMuted = [];\n for (const source of Object.keys(config.streamMap)) {\n var _config$stream6, _config$stream6$getAu, _config$stream7, _config$stream7$getVi, _transceivers$find2, _transceivers$find3;\n const audioTrack = (_config$stream6 = config.stream) === null || _config$stream6 === void 0 ? void 0 : (_config$stream6$getAu = _config$stream6.getAudioTracks()) === null || _config$stream6$getAu === void 0 ? void 0 : _config$stream6$getAu.find(track => config.streamMap[source].includes(track.id));\n const videoTrack = (_config$stream7 = config.stream) === null || _config$stream7 === void 0 ? void 0 : (_config$stream7$getVi = _config$stream7.getVideoTracks()) === null || _config$stream7$getVi === void 0 ? void 0 : _config$stream7$getVi.find(track => config.streamMap[source].includes(track.id));\n this.isMuted.push({\n type: 'audio',\n value: !audioTrack || !audioTrack.enabled,\n source,\n mid: (_transceivers$find2 = transceivers.find(transceiver => {\n var _transceiver$sender7, _transceiver$sender7$, _transceiver$sender8, _transceiver$sender8$;\n return ((_transceiver$sender7 = transceiver.sender) === null || _transceiver$sender7 === void 0 ? void 0 : (_transceiver$sender7$ = _transceiver$sender7.track) === null || _transceiver$sender7$ === void 0 ? void 0 : _transceiver$sender7$.kind) === 'audio' && ((_transceiver$sender8 = transceiver.sender) === null || _transceiver$sender8 === void 0 ? void 0 : (_transceiver$sender8$ = _transceiver$sender8.track) === null || _transceiver$sender8$ === void 0 ? void 0 : _transceiver$sender8$.id) === (audioTrack === null || audioTrack === void 0 ? void 0 : audioTrack.id);\n })) === null || _transceivers$find2 === void 0 ? void 0 : _transceivers$find2.mid\n });\n this.isMuted.push({\n type: 'video',\n value: !videoTrack || !videoTrack.enabled,\n source,\n mid: (_transceivers$find3 = transceivers.find(transceiver => {\n var _transceiver$sender9, _transceiver$sender9$, _transceiver$sender10, _transceiver$sender11;\n return ((_transceiver$sender9 = transceiver.sender) === null || _transceiver$sender9 === void 0 ? void 0 : (_transceiver$sender9$ = _transceiver$sender9.track) === null || _transceiver$sender9$ === void 0 ? void 0 : _transceiver$sender9$.kind) === 'video' && ((_transceiver$sender10 = transceiver.sender) === null || _transceiver$sender10 === void 0 ? void 0 : (_transceiver$sender11 = _transceiver$sender10.track) === null || _transceiver$sender11 === void 0 ? void 0 : _transceiver$sender11.id) === (videoTrack === null || videoTrack === void 0 ? void 0 : videoTrack.id);\n })) === null || _transceivers$find3 === void 0 ? void 0 : _transceivers$find3.mid\n });\n this.emit('localHasVideo', !!videoTrack, source);\n this.emit('localHasAudio', !!audioTrack, source);\n }\n for (const val of this.isMuted) {\n this.emit('localMuted', {\n ...val\n });\n }\n this.emit('published', {\n status: true,\n hasStream: tracks.length > 0\n });\n this.emit('publishing', false);\n return this;\n };\n\n // this should be enough\n if (!needsNegotiation) {\n return Promise.all([audioTrackReplacePromise, videoTrackReplacePromise]).then(() => emitEvents());\n }\n this._log('Starting negotiation');\n return Promise.all([audioTrackReplacePromise, videoTrackReplacePromise]).then(() => this._createAO('offer', this.handleId, false)).then(jsep => {\n if (!jsep) {\n return null;\n }\n //HOTFIX: Temporary fix for Safari 13\n if (jsep.sdp && jsep.sdp.indexOf(\"\\r\\na=ice-ufrag\") === -1) {\n jsep.sdp = jsep.sdp.replace(\"\\na=ice-ufrag\", \"\\r\\na=ice-ufrag\");\n }\n let descriptions = [];\n Object.keys(config.streamMap).forEach(source => {\n const simulcastConfigForSource = this._findSimulcastConfig(source, this.simulcastSettings);\n config.streamMap[source].forEach(trackId => {\n let t = transceivers.find(transceiver => transceiver.sender.track && transceiver.sender.track.id === trackId);\n if (t) {\n descriptions.push({\n mid: t.mid,\n description: JSON.stringify({\n source,\n simulcastBitrates: simulcastConfigForSource === null || simulcastConfigForSource === void 0 ? void 0 : simulcastConfigForSource.bitrates,\n intercomGroups: this._talkIntercomChannels\n })\n });\n }\n });\n });\n return this.sendMessage(this.handleId, {\n body: {\n \"request\": \"configure\",\n \"audio\": this.isAudioEnabed,\n \"video\": this.isVideoEnabled,\n \"data\": true,\n ...(this.recordingFilename ? {\n filename: this.recordingFilename\n } : {}),\n descriptions: descriptions\n },\n jsep\n });\n }).then(r => {\n if (this._isDataChannelOpen === true) {\n return Promise.resolve(r);\n } else {\n return new Promise((resolve, reject) => {\n let dataChannelTimeoutId = null;\n let _resolve = val => {\n if (val) {\n clearTimeout(dataChannelTimeoutId);\n this._abortController.signal.removeEventListener('abort', _rejectAbort);\n this.off('dataChannel', _resolve, this);\n resolve(this);\n }\n };\n let _rejectTimeout = () => {\n this.off('dataChannel', _resolve, this);\n this._abortController.signal.removeEventListener('abort', _rejectAbort);\n reject({\n type: 'error',\n id: 27,\n message: 'Data channel did not open',\n data: null\n });\n };\n let _rejectAbort = () => {\n this._abortController.signal.removeEventListener('abort', _rejectAbort);\n clearTimeout(dataChannelTimeoutId);\n this.off('dataChannel', _resolve, this);\n reject({\n type: 'warning',\n id: 17,\n message: 'Connection cancelled'\n });\n };\n dataChannelTimeoutId = setTimeout(_rejectTimeout, 10000);\n this._abortController.signal.addEventListener('abort', _rejectAbort);\n this.on('dataChannel', _resolve, this);\n });\n }\n }).then(() => emitEvents()).catch(e => {\n this.emit('publishing', false);\n return Promise.reject(e);\n });\n }\n unpublishLocal() {\n let dontWait = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;\n return this.isPublished ? this.sendMessage(this.handleId, {\n body: {\n \"request\": \"unpublish\"\n }\n }, dontWait).finally(r => {\n this.isPublished = false;\n this.emit('published', {\n status: false,\n hasStream: false\n });\n return r;\n }) : Promise.resolve();\n }\n getUserTalkIntercomChannels(userId) {\n let talkIntercomChannels = [];\n // TODO: check if we don't need full userId\n let handle = this._getHandle(null, null, userId);\n if (handle) {\n let config = handle.webrtcStuff;\n talkIntercomChannels = config.tracksMap.reduce((acc, val) => {\n if (val.description) {\n try {\n let description = JSON.parse(val.description);\n if (description.intercomGroups) {\n description.intercomGroups.forEach(group => {\n if (!acc.includes(group)) {\n acc.push(group);\n }\n });\n }\n } catch (e) {}\n }\n return acc;\n }, []);\n }\n return talkIntercomChannels;\n }\n toggleAudio() {\n var _config$pc3;\n let value = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;\n let source = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'camera0';\n let mid = arguments.length > 2 ? arguments[2] : undefined;\n let handle = this._getHandle(this.handleId);\n if (!handle) {\n this.emit('error', {\n type: 'warning',\n id: 21,\n message: 'no local id, connect first',\n data: null\n });\n return;\n }\n let config = handle.webrtcStuff;\n let transceivers = (_config$pc3 = config.pc) === null || _config$pc3 === void 0 ? void 0 : _config$pc3.getTransceivers();\n let transceiver = null;\n if (source) {\n transceiver = transceivers === null || transceivers === void 0 ? void 0 : transceivers.find(t => t.sender && t.sender.track && t.receiver.track.kind === \"audio\" && (config.streamMap[source] || []).includes(t.sender.track.id));\n } else {\n transceiver = transceivers === null || transceivers === void 0 ? void 0 : transceivers.find(t => t.sender && t.sender.track && t.receiver.track.kind === \"audio\" && (mid ? t.mid === mid : true));\n }\n if (transceiver) {\n transceiver.sender.track.enabled = value !== null ? !!value : !transceiver.sender.track.enabled;\n }\n this.isMuted = [];\n for (const source of Object.keys(config.streamMap)) {\n var _config$stream8, _config$stream8$getAu, _config$stream9, _config$stream9$getVi, _audioTransceiver$sen, _audioTransceiver$sen2, _videoTransceiver$sen, _videoTransceiver$sen2;\n const audioTrack = (_config$stream8 = config.stream) === null || _config$stream8 === void 0 ? void 0 : (_config$stream8$getAu = _config$stream8.getAudioTracks()) === null || _config$stream8$getAu === void 0 ? void 0 : _config$stream8$getAu.find(track => config.streamMap[source].includes(track.id));\n const audioTransceiver = transceivers === null || transceivers === void 0 ? void 0 : transceivers.find(transceiver => transceiver.sender.track && transceiver.sender.track.kind === 'audio' && transceiver.sender.track.id === (audioTrack === null || audioTrack === void 0 ? void 0 : audioTrack.id));\n const videoTrack = (_config$stream9 = config.stream) === null || _config$stream9 === void 0 ? void 0 : (_config$stream9$getVi = _config$stream9.getVideoTracks()) === null || _config$stream9$getVi === void 0 ? void 0 : _config$stream9$getVi.find(track => config.streamMap[source].includes(track.id));\n const videoTransceiver = transceivers === null || transceivers === void 0 ? void 0 : transceivers.find(transceiver => transceiver.sender.track && transceiver.sender.track.kind === 'video' && transceiver.sender.track.id === (videoTrack === null || videoTrack === void 0 ? void 0 : videoTrack.id));\n this.isMuted.push({\n type: 'audio',\n value: !audioTrack || !audioTransceiver || !(audioTransceiver !== null && audioTransceiver !== void 0 && (_audioTransceiver$sen = audioTransceiver.sender) !== null && _audioTransceiver$sen !== void 0 && (_audioTransceiver$sen2 = _audioTransceiver$sen.track) !== null && _audioTransceiver$sen2 !== void 0 && _audioTransceiver$sen2.enabled),\n source,\n mid: audioTransceiver === null || audioTransceiver === void 0 ? void 0 : audioTransceiver.mid\n });\n this.isMuted.push({\n type: 'video',\n value: !videoTrack || !videoTransceiver || !(videoTransceiver !== null && videoTransceiver !== void 0 && (_videoTransceiver$sen = videoTransceiver.sender) !== null && _videoTransceiver$sen !== void 0 && (_videoTransceiver$sen2 = _videoTransceiver$sen.track) !== null && _videoTransceiver$sen2 !== void 0 && _videoTransceiver$sen2.enabled),\n source,\n mid: videoTransceiver === null || videoTransceiver === void 0 ? void 0 : videoTransceiver.mid\n });\n }\n for (let val of this.isMuted) {\n this.emit('localMuted', {\n ...val\n });\n }\n }\n toggleVideo() {\n var _config$pc4;\n let value = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;\n let source = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'camera0';\n let mid = arguments.length > 2 ? arguments[2] : undefined;\n let handle = this._getHandle(this.handleId);\n if (!handle) {\n this.emit('error', {\n type: 'warning',\n id: 21,\n message: 'no local id, connect first',\n data: null\n });\n return;\n }\n let config = handle.webrtcStuff;\n let transceivers = (_config$pc4 = config.pc) === null || _config$pc4 === void 0 ? void 0 : _config$pc4.getTransceivers();\n let transceiver = null;\n if (source) {\n transceiver = transceivers === null || transceivers === void 0 ? void 0 : transceivers.find(t => t.sender && t.sender.track && t.receiver.track.kind === \"video\" && (config.streamMap[source] || []).includes(t.sender.track.id));\n } else {\n transceiver = transceivers === null || transceivers === void 0 ? void 0 : transceivers.find(t => t.sender && t.sender.track && t.receiver.track.kind === \"video\" && (mid ? t.mid === mid : true));\n }\n if (transceiver) {\n transceiver.sender.track.enabled = value !== null ? !!value : !transceiver.sender.track.enabled;\n }\n this.isMuted = [];\n for (const source of Object.keys(config.streamMap)) {\n var _config$stream10, _config$stream10$getA, _config$stream11, _config$stream11$getV, _audioTransceiver$sen3, _audioTransceiver$sen4, _videoTransceiver$sen3, _videoTransceiver$sen4;\n const audioTrack = (_config$stream10 = config.stream) === null || _config$stream10 === void 0 ? void 0 : (_config$stream10$getA = _config$stream10.getAudioTracks()) === null || _config$stream10$getA === void 0 ? void 0 : _config$stream10$getA.find(track => config.streamMap[source].includes(track.id));\n const audioTransceiver = transceivers === null || transceivers === void 0 ? void 0 : transceivers.find(transceiver => transceiver.sender.track && transceiver.sender.track.kind === 'audio' && transceiver.sender.track.id === (audioTrack === null || audioTrack === void 0 ? void 0 : audioTrack.id));\n const videoTrack = (_config$stream11 = config.stream) === null || _config$stream11 === void 0 ? void 0 : (_config$stream11$getV = _config$stream11.getVideoTracks()) === null || _config$stream11$getV === void 0 ? void 0 : _config$stream11$getV.find(track => config.streamMap[source].includes(track.id));\n const videoTransceiver = transceivers === null || transceivers === void 0 ? void 0 : transceivers.find(transceiver => transceiver.sender.track && transceiver.sender.track.kind === 'video' && transceiver.sender.track.id === (videoTrack === null || videoTrack === void 0 ? void 0 : videoTrack.id));\n this.isMuted.push({\n type: 'audio',\n value: !audioTrack || !audioTransceiver || !(audioTransceiver !== null && audioTransceiver !== void 0 && (_audioTransceiver$sen3 = audioTransceiver.sender) !== null && _audioTransceiver$sen3 !== void 0 && (_audioTransceiver$sen4 = _audioTransceiver$sen3.track) !== null && _audioTransceiver$sen4 !== void 0 && _audioTransceiver$sen4.enabled),\n source,\n mid: audioTransceiver === null || audioTransceiver === void 0 ? void 0 : audioTransceiver.mid\n });\n this.isMuted.push({\n type: 'video',\n value: !videoTrack || !videoTransceiver || !(videoTransceiver !== null && videoTransceiver !== void 0 && (_videoTransceiver$sen3 = videoTransceiver.sender) !== null && _videoTransceiver$sen3 !== void 0 && (_videoTransceiver$sen4 = _videoTransceiver$sen3.track) !== null && _videoTransceiver$sen4 !== void 0 && _videoTransceiver$sen4.enabled),\n source,\n mid: videoTransceiver === null || videoTransceiver === void 0 ? void 0 : videoTransceiver.mid\n });\n }\n for (let val of this.isMuted) {\n this.emit('localMuted', {\n ...val\n });\n }\n }\n requestKeyFrame(handleId, mid) {\n this.sendMessage(handleId, {\n \"body\": {\n \"request\": \"configure\",\n \"keyframe\": true,\n ...(mid !== undefined ? {\n streams: [{\n mid,\n keyframe: true\n }]\n } : {})\n }\n }).catch(() => null);\n }\n _setSelectedSubstream(handleId, mid, substream) {\n let handle = this._getHandle(handleId);\n if (handle) {\n let config = handle.webrtcStuff;\n if (!mid) {\n Object.keys(config.selectedSubstream).forEach(mid => {\n config.selectedSubstream[mid] = substream;\n });\n } else {\n config.selectedSubstream[mid] = substream;\n }\n }\n }\n overrideSimulcastSettings(handleId, mid, source) {\n let settings = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};\n const {\n mode,\n defaultSubstream\n } = settings;\n let handle = this._getHandle(handleId);\n if (!handle) {\n return Promise.resolve();\n }\n let config = handle.webrtcStuff;\n if (source !== undefined || mid !== undefined) {\n if (mid === undefined) {\n let transceivers = config.pc.getTransceivers();\n for (let trackId of config.streamMap[source]) {\n let transceiver = transceivers.find(transceiver => transceiver.receiver.track && transceiver.receiver.track.kind === 'video' && transceiver.receiver.track.id === trackId);\n if (transceiver) {\n mid = transceiver.mid;\n break;\n }\n }\n }\n if (mid !== undefined) {\n if (!config.overriddenSimulcastMode[mid]) {\n config.overriddenSimulcastMode[mid] = {};\n }\n config.overriddenSimulcastMode[mid]['defaultSubstream'] = defaultSubstream;\n config.overriddenSimulcastMode[mid]['mode'] = mode;\n return true;\n } else {\n return false;\n }\n }\n }\n selectSubStream(handleId) {\n let substream = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 2;\n let source = arguments.length > 2 ? arguments[2] : undefined;\n let mid = arguments.length > 3 ? arguments[3] : undefined;\n let manual = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false;\n this._log('Select substream called for handle:', handleId, 'Source or mid:', source ? source : mid, 'Substream:', substream);\n let handle = this._getHandle(handleId);\n if (!handle) {\n return Promise.resolve();\n }\n let config = handle.webrtcStuff;\n return new Promise((resolve, reject) => {\n let messageTimeoutId;\n let abortResponse = () => {\n clearTimeout(messageTimeoutId);\n this._abortController.signal.removeEventListener('abort', abortResponse);\n this.ws.removeEventListener('message', parseResponse);\n reject('aborted');\n };\n let parseResponse = event => {\n let json = JSON.parse(event.data);\n var sender = json[\"sender\"];\n if (sender === handleId) {\n let plugindata = json[\"plugindata\"] || {};\n let msg = plugindata[\"data\"] || {};\n let substream = msg[\"substream\"];\n if (substream !== undefined && substream !== null && (mid !== undefined ? msg[\"mid\"] === mid : true)) {\n clearTimeout(messageTimeoutId);\n this._abortController.signal.removeEventListener('abort', abortResponse);\n this.ws.removeEventListener('message', parseResponse);\n resolve({\n substream,\n sender\n });\n }\n }\n };\n if (source !== undefined || mid !== undefined) {\n if (mid === undefined) {\n let transceivers = config.pc.getTransceivers();\n for (let trackId of config.streamMap[source]) {\n let transceiver = transceivers.find(transceiver => transceiver.receiver.track && transceiver.receiver.track.kind === 'video' && transceiver.receiver.track.id === trackId);\n if (transceiver) {\n mid = transceiver.mid;\n break;\n }\n }\n }\n if (mid !== undefined) {\n if (!config.overriddenSimulcastMode[mid]) {\n config.overriddenSimulcastMode[mid] = {};\n }\n if (substream === null) {\n if (manual) {\n // reset to previous state\n config.overriddenSimulcastMode[mid]['defaultSubstream'] = null;\n config.overriddenSimulcastMode[mid]['mode'] = null;\n }\n resolve({\n substream,\n sender: handleId\n });\n return;\n }\n if (manual) {\n config.overriddenSimulcastMode[mid]['defaultSubstream'] = substream;\n config.overriddenSimulcastMode[mid]['mode'] = \"manual\";\n }\n this.ws.addEventListener('message', parseResponse);\n this._abortController.signal.addEventListener('abort', abortResponse);\n messageTimeoutId = setTimeout(() => {\n this._abortController.signal.removeEventListener('abort', abortResponse);\n this.ws.removeEventListener('message', parseResponse);\n reject('timeout');\n }, 10000);\n this.sendMessage(handleId, {\n \"body\": {\n \"request\": \"configure\",\n \"streams\": [{\n mid,\n substream: parseInt(substream)\n }]\n }\n });\n } else {\n reject('no mid found');\n }\n } else {\n reject('no source or mid');\n }\n });\n }\n setTalkIntercomChannels() {\n let groups = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ['participants'];\n if (typeof groups !== 'object' || !(\"length\" in groups)) {\n this._log('setTalkIntercomChannels: groups must be an array');\n groups = [groups];\n }\n this._talkIntercomChannels = structuredClone(groups);\n let handle = this._getHandle(this.handleId);\n if (!handle) {\n return Promise.resolve();\n }\n let config = handle.webrtcStuff;\n let transceivers = config.pc.getTransceivers();\n let descriptions = [];\n Object.keys(config.streamMap).forEach(source => {\n const simulcastConfigForSource = this._findSimulcastConfig(source, this.simulcastSettings);\n config.streamMap[source].forEach(trackId => {\n let t = transceivers.find(transceiver => transceiver.sender.track && transceiver.sender.track.id === trackId);\n if (t) {\n descriptions.push({\n mid: t.mid,\n description: JSON.stringify({\n simulcastBitrates: simulcastConfigForSource === null || simulcastConfigForSource === void 0 ? void 0 : simulcastConfigForSource.bitrates,\n intercomGroups: groups,\n source: source\n })\n });\n }\n });\n });\n return this.sendMessage(this.handleId, {\n body: {\n \"request\": \"configure\",\n descriptions: descriptions\n }\n });\n }\n setListenIntercomChannels() {\n let groups = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ['participants'];\n if (typeof groups !== 'object' || !(\"length\" in groups)) {\n this._log('setListenIntercomChannels: groups must be an array');\n groups = [groups];\n }\n this._listenIntercomChannels = structuredClone(groups);\n let handle = this._getHandle(this.handleId);\n if (!handle) {\n return Promise.resolve();\n }\n let promises = [];\n this._participants.forEach(participant => {\n if (participant.handleId === this.handleId) {\n return;\n }\n let handle = this._getHandle(participant.handleId);\n if (handle) {\n const tracksMap = handle.webrtcStuff.tracksMap.filter(t => t.active);\n const subscribe = [];\n const unsubscribe = [];\n tracksMap.forEach(track => {\n if (track.type === 'data') {\n return;\n }\n const description = JSON.parse(track.description);\n const intercomGroups = (description === null || description === void 0 ? void 0 : description.intercomGroups) || [];\n if (this._listenIntercomChannels.some(g => intercomGroups.includes(g))) {\n if (!this._isAlreadySubscribed(participant.handleId, track.id, track.mid)) {\n subscribe.push({\n feed: track.id,\n mid: track.mid\n });\n }\n } else {\n unsubscribe.push({\n feed: track.id,\n mid: track.mid\n });\n }\n });\n this._updateSubscribeMap(participant.handleId, subscribe, unsubscribe);\n if (subscribe.length || unsubscribe.length) {\n promises.push(this.sendMessage(handle.handleId, {\n body: {\n \"request\": \"update\",\n ...(subscribe.length ? {\n subscribe\n } : {}),\n ...(unsubscribe.length ? {\n unsubscribe\n } : {})\n }\n }));\n }\n }\n });\n return Promise.all(promises);\n }\n setRoomType() {\n let type = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'watchparty';\n this._roomType = type;\n return this._roomType;\n }\n setRestrictSubscribeToUserIds() {\n let userIds = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];\n this._restrictSubscribeToUserIds = structuredClone(userIds);\n if (!this.isConnected) {\n return Promise.resolve(this._restrictSubscribeToUserIds);\n }\n // sanity check by getting listparticipants and comparing it to _remoteUsersCache\n this.sendMessage(this.handleId, {\n body: {\n request: 'listparticipants',\n room: this.roomId\n }\n }).then(r => {\n let participants = r.participants;\n let remoteUsersCache = this._remoteUsersCache;\n let handle = this._getHandle(this.handleId);\n // filter out my user id from response and compare it to remoteUsersCache\n participants = participants.filter(p => p.id !== handle.userId);\n // get rid of participants that are in participants but not in remoteUsersCache\n remoteUsersCache = remoteUsersCache.filter(r => participants.find(p => p.id === r.id));\n remoteUsersCache.forEach(r => {\n const handle = this._getHandle(null, null, null, r.userId);\n if (this._participantShouldSubscribe(r.userId)) {\n var _handle$webrtcStuff6;\n // todo: do a nicer flag to indicate inactive handle than just checking for pc\n if (!handle || !((_handle$webrtcStuff6 = handle.webrtcStuff) !== null && _handle$webrtcStuff6 !== void 0 && _handle$webrtcStuff6.pc)) {\n this._log('Subscribing to ', r.userId);\n this._createParticipant(r.userId, r.id).then(handle => {\n this._updateParticipantsTrackData(handle.handleId, r.streams);\n const subscribe = r.streams.filter(s => !s.disabled && this._intercomSubscribe(s)).map(stream => ({\n feed: stream.id,\n mid: stream.mid\n }));\n handle.webrtcStuff.subscribeMap = structuredClone(subscribe);\n return this.sendMessage(handle.handleId, {\n body: {\n \"request\": \"join\",\n \"room\": this.roomId,\n \"ptype\": \"subscriber\",\n \"private_id\": this.privateId,\n streams: subscribe,\n //\"feed\": id,\n ...(this.webrtcVersion > 1000 ? {\n id: this.userId\n } : {}),\n pin: this.pin\n }\n });\n }).catch(err => {\n this.emit('error', err);\n });\n } else {\n this._log('Already subscribed to ', r.userId);\n }\n } else if (handle) {\n this._log('Unsubscribing from ', r.userId);\n this._removeParticipant(handle.handleId);\n }\n });\n });\n }\n}\n_defineProperty(RoomSession, \"sessionTypes\", {\n 'reactooroom': 'janus.plugin.reactooroom',\n 'streaming': 'janus.plugin.streaming'\n});\n_defineProperty(RoomSession, \"userRoleSubscriptionRules\", {\n participant: {\n \"watchparty\": ['participant', 'talkback'],\n \"studio\": ['participant', 'talkback', 'host', 'observer'],\n \"commentary\": ['participant', 'talkback', 'host'],\n \"intercom\": ['host', 'talkback', 'observer', 'observerSolo1', 'observerSolo2', 'observerSolo3'],\n \"videowall\": ['host', 'talkback', 'observer', 'observerSolo1', 'observerSolo2', 'observerSolo3'],\n \"videowall-queue\": ['host', 'talkback', 'observer', 'observerSolo1', 'observerSolo2', 'observerSolo3'],\n \"videowall-queue-video\": ['host', 'talkback', 'observer', 'observerSolo1', 'observerSolo2', 'observerSolo3']\n },\n monitor: {\n \"watchparty\": ['participant', 'host'],\n \"studio\": ['participant', 'host', 'observer'],\n \"commentary\": ['participant', 'host'],\n \"intercom\": ['host', 'participant'],\n \"videowall\": ['host', 'participant'],\n \"videowall-queue\": ['host', 'participant'],\n \"videowall-queue-video\": ['host', 'participant']\n },\n talkback: {\n \"watchparty\": ['participant', 'host', 'talkback'],\n \"studio\": ['participant', 'host', 'observer', 'talkback'],\n \"commentary\": ['host', 'participant', 'talkback'],\n \"intercom\": ['host', 'participant', 'talkback'],\n \"videowall\": ['host', 'participant', 'talkback'],\n \"videowall-queue\": ['host', 'participant', 'talkback'],\n \"videowall-queue-video\": ['host', 'participant', 'talkback']\n },\n observer: {\n \"watchparty\": ['participant'],\n \"studio\": ['participant'],\n \"commentary\": ['participant'],\n \"intercom\": ['participant'],\n \"videowall\": ['participant'],\n \"videowall-queue\": ['participant'],\n \"videowall-queue-video\": ['participant']\n },\n observerSolo1: {\n \"watchparty\": ['participant'],\n \"studio\": ['participant'],\n \"commentary\": ['participant'],\n \"intercom\": ['participant'],\n \"videowall\": ['participant'],\n \"videowall-queue\": ['participant'],\n \"videowall-queue-video\": ['participant']\n },\n observerSolo2: {\n \"watchparty\": ['participant'],\n \"studio\": ['participant'],\n \"commentary\": ['participant'],\n \"intercom\": ['participant'],\n \"videowall\": ['participant'],\n \"videowall-queue\": ['participant'],\n \"videowall-queue-video\": ['participant']\n },\n observerSolo3: {\n \"watchparty\": ['participant'],\n \"studio\": ['participant'],\n \"commentary\": ['participant'],\n \"intercom\": ['participant'],\n \"videowall\": ['participant'],\n \"videowall-queue\": ['participant'],\n \"videowall-queue-video\": ['participant']\n },\n host: {\n \"watchparty\": [],\n \"studio\": [],\n \"commentary\": [],\n \"intercom\": [],\n \"videowall\": [],\n \"videowall-queue\": [],\n \"videowall-queue-video\": []\n },\n companionTV: {},\n companionPhone: {}\n});\n/* harmony default export */ __webpack_exports__[\"default\"] = (Room);\n\n//# sourceURL=webpack://WatchTogetherSDK/./src/modules/wt-room.js?");
|
|
10514
10514
|
|
|
10515
10515
|
/***/ }),
|
|
10516
10516
|
|