{"version":3,"file":"lib-jitsi-meet.min.js","mappings":"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","sources":["webpack://JitsiMeetJS/webpack/universalModuleDefinition","webpack://JitsiMeetJS/./node_modules/@jitsi/logger/lib/LogCollector.js","webpack://JitsiMeetJS/./node_modules/@jitsi/logger/lib/Logger.js","webpack://JitsiMeetJS/./node_modules/@jitsi/logger/lib/index.js","webpack://JitsiMeetJS/./node_modules/@jitsi/sdp-interop/node_modules/sdp-transform/lib/grammar.js","webpack://JitsiMeetJS/./node_modules/@jitsi/sdp-interop/node_modules/sdp-transform/lib/index.js","webpack://JitsiMeetJS/./node_modules/@jitsi/sdp-interop/node_modules/sdp-transform/lib/parser.js","webpack://JitsiMeetJS/./node_modules/@jitsi/sdp-interop/node_modules/sdp-transform/lib/writer.js","webpack://JitsiMeetJS/./node_modules/@jitsi/sdp-simulcast/lib/index.js","webpack://JitsiMeetJS/./node_modules/@jitsi/sdp-simulcast/lib/transform-utils.js","webpack://JitsiMeetJS/./node_modules/async/lib/async.js","webpack://JitsiMeetJS/./index.js","webpack://JitsiMeetJS/./modules/statistics/SpeakerStats.js","webpack://JitsiMeetJS/./modules/util/AuthUtil.js","webpack://JitsiMeetJS/./modules/util/EventEmitterForwarder.js","webpack://JitsiMeetJS/./modules/util/GlobalOnErrorHandler.js","webpack://JitsiMeetJS/./modules/util/RandomUtil.js","webpack://JitsiMeetJS/./modules/util/ScriptUtil.js","webpack://JitsiMeetJS/./modules/util/StringUtils.js","webpack://JitsiMeetJS/./modules/util/UsernameGenerator.js","webpack://JitsiMeetJS/./service/RTC/BridgeVideoType.js","webpack://JitsiMeetJS/./service/RTC/CameraFacingMode.js","webpack://JitsiMeetJS/./service/RTC/CodecMimeType.js","webpack://JitsiMeetJS/./service/RTC/MediaDirection.js","webpack://JitsiMeetJS/./service/RTC/RTCEvents.js","webpack://JitsiMeetJS/./service/RTC/Resolutions.js","webpack://JitsiMeetJS/./service/RTC/VideoType.js","webpack://JitsiMeetJS/./service/authentication/AuthenticationEvents.js","webpack://JitsiMeetJS/./service/statistics/constants.js","webpack://JitsiMeetJS/./service/xmpp/XMPPEvents.js","webpack://JitsiMeetJS/./node_modules/base64-js/index.js","webpack://JitsiMeetJS/./node_modules/bowser/es5.js","webpack://JitsiMeetJS/./node_modules/current-executing-script/dist/currentExecutingScript.js","webpack://JitsiMeetJS/./node_modules/events/events.js","webpack://JitsiMeetJS/./node_modules/js-md5/src/md5.js","webpack://JitsiMeetJS/./node_modules/lodash.clonedeep/index.js","webpack://JitsiMeetJS/./node_modules/lodash.debounce/index.js","webpack://JitsiMeetJS/./node_modules/lodash.isequal/index.js","webpack://JitsiMeetJS/./node_modules/sdp-transform/lib/grammar.js","webpack://JitsiMeetJS/./node_modules/sdp-transform/lib/index.js","webpack://JitsiMeetJS/./node_modules/sdp-transform/lib/parser.js","webpack://JitsiMeetJS/./node_modules/sdp-transform/lib/writer.js","webpack://JitsiMeetJS/./node_modules/sdp/sdp.js","webpack://JitsiMeetJS/./JitsiConferenceErrors.ts","webpack://JitsiMeetJS/./JitsiConferenceEvents.ts","webpack://JitsiMeetJS/./modules/statistics/constants.js","webpack://JitsiMeetJS/./JitsiTrackErrors.ts","webpack://JitsiMeetJS/./JitsiTrackError.js","webpack://JitsiMeetJS/./service/statistics/AnalyticsEvents.js","webpack://JitsiMeetJS/./service/statistics/Events.ts","webpack://JitsiMeetJS/./node_modules/@jitsi/js-utils/browser-detection/browsers.js","webpack://JitsiMeetJS/./node_modules/@jitsi/js-utils/browser-detection/BrowserDetection.js","webpack://JitsiMeetJS/./node_modules/@jitsi/js-utils/jitsi-local-storage/index.js","webpack://JitsiMeetJS/./modules/browser/BrowserCapabilities.js","webpack://JitsiMeetJS/./modules/browser/index.js","webpack://JitsiMeetJS/./modules/statistics/AnalyticsAdapter.js","webpack://JitsiMeetJS/./modules/statistics/CallStats.js","webpack://JitsiMeetJS/./modules/statistics/LocalStatsCollector.js","webpack://JitsiMeetJS/./modules/util/MathUtil.js","webpack://JitsiMeetJS/./modules/statistics/PerformanceObserverStats.js","webpack://JitsiMeetJS/./service/RTC/MediaType.js","webpack://JitsiMeetJS/./modules/statistics/RTPStatsCollector.js","webpack://JitsiMeetJS/./modules/statistics/statistics.js","webpack://JitsiMeetJS/./JitsiConferenceEventManager.js","webpack://JitsiMeetJS/./JitsiTrackEvents.js","webpack://JitsiMeetJS/./modules/connectivity/ParticipantConnectionStatus.js","webpack://JitsiMeetJS/./JitsiParticipant.js","webpack://JitsiMeetJS/./JitsiConnectionEvents.ts","webpack://JitsiMeetJS/./JitsiConnectionErrors.ts","webpack://JitsiMeetJS/./modules/util/Deferred.js","webpack://JitsiMeetJS/./modules/util/Listenable.js","webpack://JitsiMeetJS/./modules/e2ee/E2EEContext.js","webpack://JitsiMeetJS/./modules/e2ee/KeyHandler.js","webpack://JitsiMeetJS/./modules/e2ee/ExternallyManagedKeyHandler.js","webpack://JitsiMeetJS/./node_modules/uuid/wrapper.mjs","webpack://JitsiMeetJS/./modules/e2ee/OlmAdapter.js","webpack://JitsiMeetJS/./modules/e2ee/ManagedKeyHandler.js","webpack://JitsiMeetJS/./modules/e2ee/crypto-utils.js","webpack://JitsiMeetJS/./modules/e2ee/E2EEncryption.js","webpack://JitsiMeetJS/./modules/xmpp/Caps.js","webpack://JitsiMeetJS/./modules/connectivity/NetworkInfo.js","webpack://JitsiMeetJS/./modules/xmpp/ResumeTask.js","webpack://JitsiMeetJS/./modules/util/Retry.js","webpack://JitsiMeetJS/./modules/xmpp/StropheLastSuccess.js","webpack://JitsiMeetJS/./modules/xmpp/ConnectionPlugin.js","webpack://JitsiMeetJS/./modules/xmpp/strophe.ping.js","webpack://JitsiMeetJS/./modules/xmpp/XmppConnection.js","webpack://JitsiMeetJS/./JitsiTranscriptionStatus.ts","webpack://JitsiMeetJS/./modules/xmpp/AVModeration.js","webpack://JitsiMeetJS/./modules/xmpp/BreakoutRooms.js","webpack://JitsiMeetJS/./modules/xmpp/Lobby.js","webpack://JitsiMeetJS/./modules/settings/Settings.js","webpack://JitsiMeetJS/./modules/xmpp/moderator.js","webpack://JitsiMeetJS/./modules/xmpp/ChatRoom.js","webpack://JitsiMeetJS/./modules/xmpp/strophe.emuc.js","webpack://JitsiMeetJS/./modules/flags/FeatureFlags.js","webpack://JitsiMeetJS/./modules/xmpp/JingleHelperFunctions.js","webpack://JitsiMeetJS/./modules/RTC/ScreenObtainer.js","webpack://JitsiMeetJS/./modules/sdp/SDPUtil.js","webpack://JitsiMeetJS/./modules/sdp/SDP.js","webpack://JitsiMeetJS/./modules/sdp/SDPDiffer.js","webpack://JitsiMeetJS/./modules/util/AsyncQueue.js","webpack://JitsiMeetJS/./modules/xmpp/JingleSessionState.js","webpack://JitsiMeetJS/./modules/xmpp/JingleSession.js","webpack://JitsiMeetJS/./modules/xmpp/MediaSessionEvents.ts","webpack://JitsiMeetJS/./modules/xmpp/JingleSessionPC.js","webpack://JitsiMeetJS/./modules/xmpp/strophe.jingle.js","webpack://JitsiMeetJS/./modules/xmpp/strophe.logger.js","webpack://JitsiMeetJS/./modules/xmpp/strophe.rayo.js","webpack://JitsiMeetJS/./modules/xmpp/strophe.util.js","webpack://JitsiMeetJS/./modules/xmpp/xmpp.js","webpack://JitsiMeetJS/./authenticateAndUpgradeRole.js","webpack://JitsiMeetJS/./modules/RTC/CodecSelection.js","webpack://JitsiMeetJS/./modules/RTC/BridgeChannel.js","webpack://JitsiMeetJS/./modules/RTC/RTCUtils.js","webpack://JitsiMeetJS/./modules/RTC/JitsiTrack.js","webpack://JitsiMeetJS/./modules/RTC/JitsiLocalTrack.js","webpack://JitsiMeetJS/./node_modules/@jitsi/sdp-interop/lib/transform.js","webpack://JitsiMeetJS/./node_modules/@jitsi/sdp-interop/lib/interop.js","webpack://JitsiMeetJS/./service/RTC/SignalingEvents.ts","webpack://JitsiMeetJS/./service/RTC/SignalingLayer.js","webpack://JitsiMeetJS/./modules/sdp/SdpTransformUtil.js","webpack://JitsiMeetJS/./modules/sdp/LocalSdpMunger.js","webpack://JitsiMeetJS/./modules/sdp/RtxModifier.js","webpack://JitsiMeetJS/./modules/sdp/SdpConsistency.js","webpack://JitsiMeetJS/./modules/sdp/SdpSimulcast.ts","webpack://JitsiMeetJS/./modules/connectivity/TrackStreamingStatus.ts","webpack://JitsiMeetJS/./modules/RTC/JitsiRemoteTrack.js","webpack://JitsiMeetJS/./modules/RTC/TPCUtils.js","webpack://JitsiMeetJS/./modules/RTC/TraceablePeerConnection.js","webpack://JitsiMeetJS/./modules/RTC/RTC.js","webpack://JitsiMeetJS/./service/connectivity/ConnectionQualityEvents.js","webpack://JitsiMeetJS/./modules/connectivity/ConnectionQuality.js","webpack://JitsiMeetJS/./modules/connectivity/IceFailedHandling.js","webpack://JitsiMeetJS/./modules/detection/DetectionEvents.js","webpack://JitsiMeetJS/./modules/detection/NoAudioSignalDetection.js","webpack://JitsiMeetJS/./modules/detection/P2PDominantSpeakerDetection.js","webpack://JitsiMeetJS/./modules/webaudio/WebAudioUtils.js","webpack://JitsiMeetJS/./modules/detection/TrackVADEmitter.js","webpack://JitsiMeetJS/./modules/detection/VADAudioAnalyser.js","webpack://JitsiMeetJS/./modules/detection/VADNoiseDetection.js","webpack://JitsiMeetJS/./modules/detection/VADTalkMutedDetection.js","webpack://JitsiMeetJS/./service/e2eping/E2ePingEvents.ts","webpack://JitsiMeetJS/./modules/e2eping/e2eping.js","webpack://JitsiMeetJS/./modules/event/Jvb121EventGenerator.js","webpack://JitsiMeetJS/./modules/qualitycontrol/ReceiveVideoController.js","webpack://JitsiMeetJS/./modules/qualitycontrol/SendVideoController.js","webpack://JitsiMeetJS/./modules/recording/recordingXMLUtils.js","webpack://JitsiMeetJS/./modules/recording/JibriSession.js","webpack://JitsiMeetJS/./modules/recording/RecordingManager.js","webpack://JitsiMeetJS/./modules/statistics/AudioOutputProblemDetector.js","webpack://JitsiMeetJS/./modules/statistics/AvgRTPStatsReporter.js","webpack://JitsiMeetJS/./modules/statistics/SpeakerStatsCollector.js","webpack://JitsiMeetJS/./modules/transcription/recordingResult.js","webpack://JitsiMeetJS/./modules/transcription/trackRecorder.js","webpack://JitsiMeetJS/./modules/transcription/audioRecorder.js","webpack://JitsiMeetJS/./modules/transcription/word.js","webpack://JitsiMeetJS/./modules/transcription/transcriptionServices/SphinxTranscriptionService.js","webpack://JitsiMeetJS/./modules/transcription/transcriptionServices/AbstractTranscriptionService.js","webpack://JitsiMeetJS/./modules/transcription/transcriber.js","webpack://JitsiMeetJS/./modules/version/ComponentsVersions.js","webpack://JitsiMeetJS/./modules/videosipgw/VideoSIPGWConstants.js","webpack://JitsiMeetJS/./modules/videosipgw/JitsiVideoSIPGWSession.js","webpack://JitsiMeetJS/./modules/videosipgw/VideoSIPGW.js","webpack://JitsiMeetJS/./modules/xmpp/SignalingLayerImpl.js","webpack://JitsiMeetJS/./JitsiConference.js","webpack://JitsiMeetJS/./JitsiConnection.js","webpack://JitsiMeetJS/./JitsiMediaDevicesEvents.ts","webpack://JitsiMeetJS/./JitsiMediaDevices.js","webpack://JitsiMeetJS/./modules/detection/ActiveDeviceDetector.js","webpack://JitsiMeetJS/./modules/proxyconnection/constants.js","webpack://JitsiMeetJS/./modules/proxyconnection/ProxyConnectionPC.js","webpack://JitsiMeetJS/./modules/proxyconnection/ProxyConnectionService.js","webpack://JitsiMeetJS/./modules/statistics/PrecallTest.js","webpack://JitsiMeetJS/./modules/webaudio/AudioMixer.js","webpack://JitsiMeetJS/./JitsiMeetJS.js","webpack://JitsiMeetJS/./modules/recording/recordingConstants.js","webpack://JitsiMeetJS/./node_modules/strophe.js/dist/strophe.umd.js","webpack://JitsiMeetJS/./node_modules/strophejs-plugin-disco/lib/strophe.disco.js","webpack://JitsiMeetJS/./node_modules/strophejs-plugin-stream-management/lib/strophe.stream-management.js","webpack://JitsiMeetJS/./node_modules/uuid/dist/bytesToUuid.js","webpack://JitsiMeetJS/./node_modules/uuid/dist/index.js","webpack://JitsiMeetJS/./node_modules/uuid/dist/md5-browser.js","webpack://JitsiMeetJS/./node_modules/uuid/dist/rng-browser.js","webpack://JitsiMeetJS/./node_modules/uuid/dist/sha1-browser.js","webpack://JitsiMeetJS/./node_modules/uuid/dist/v1.js","webpack://JitsiMeetJS/./node_modules/uuid/dist/v3.js","webpack://JitsiMeetJS/./node_modules/uuid/dist/v35.js","webpack://JitsiMeetJS/./node_modules/uuid/dist/v4.js","webpack://JitsiMeetJS/./node_modules/uuid/dist/v5.js","webpack://JitsiMeetJS/./node_modules/webrtc-adapter/src/js/utils.js","webpack://JitsiMeetJS/./node_modules/webrtc-adapter/src/js/chrome/getusermedia.js","webpack://JitsiMeetJS/./node_modules/webrtc-adapter/src/js/chrome/getdisplaymedia.js","webpack://JitsiMeetJS/./node_modules/webrtc-adapter/src/js/chrome/chrome_shim.js","webpack://JitsiMeetJS/./node_modules/webrtc-adapter/src/js/firefox/getusermedia.js","webpack://JitsiMeetJS/./node_modules/webrtc-adapter/src/js/firefox/getdisplaymedia.js","webpack://JitsiMeetJS/./node_modules/webrtc-adapter/src/js/firefox/firefox_shim.js","webpack://JitsiMeetJS/./node_modules/webrtc-adapter/src/js/safari/safari_shim.js","webpack://JitsiMeetJS/./node_modules/webrtc-adapter/src/js/common_shim.js","webpack://JitsiMeetJS/./node_modules/webrtc-adapter/src/js/adapter_core.js","webpack://JitsiMeetJS/./node_modules/webrtc-adapter/src/js/adapter_factory.js","webpack://JitsiMeetJS/webpack/bootstrap","webpack://JitsiMeetJS/webpack/runtime/amd options","webpack://JitsiMeetJS/webpack/runtime/compat get default export","webpack://JitsiMeetJS/webpack/runtime/define property getters","webpack://JitsiMeetJS/webpack/runtime/global","webpack://JitsiMeetJS/webpack/runtime/hasOwnProperty shorthand","webpack://JitsiMeetJS/webpack/runtime/make namespace object","webpack://JitsiMeetJS/webpack/runtime/node module decorator","webpack://JitsiMeetJS/webpack/startup"],"sourcesContent":["(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory();\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine([], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"JitsiMeetJS\"] = factory();\n\telse\n\t\troot[\"JitsiMeetJS\"] = factory();\n})(self, function() {\nreturn ","/* Copyright @ 2016-present 8x8, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nvar Logger = require('./Logger.js');\n\n/**\n * Creates new LogCollector. Class implements LoggerTransport\n * and thus can be added as global transport in order to capture all the logs.\n *\n * It captures subsequent log lines created whenever Logger logs\n * a message and stores them in a queue in order to batch log entries. There are\n * time and size limit constraints which determine how often batch entries are\n * stored. Whenever one of these limits is exceeded the LogCollector\n * will use the logStorage object given as an argument to save\n * the batch log entry.\n *\n * @param {Object} logStorage an object which allows to store the logs collected\n * @param {function(string|object[])} logStorage.storeLogs a method called when\n * this LogCollector requests log entry storage. The method's argument\n * is an array which can contain strings and objects. If given\n * item is an object it means that it's an aggregated message. That is a message\n * which is the same as the previous one and it's representation has\n * the following format:\n * {\n * {string} text: 'the text of some duplicated message'\n * {number} count: 3 // how many times the message appeared in a row\n * }\n * If a message \"B\" after an aggregated message \"A\" is different, then it breaks\n * the sequence of \"A\". Which means that even if the next message \"C\" is\n * the same as \"A\" it will start a new aggregated message \"C\".\n * @param {function()} logStorage.isReady a method which should return\n * a boolean to tell the collector that it's ready to store. During the\n * time storage is not ready log batches will be cached and stored on the next\n * occasion (flush or interval timeout).\n *\n * @param {Object} options the LogCollector configuration options.\n * @param {number} options.maxEntryLength the size limit for a single log entry\n * to be stored. The LogCollector will push the entry as soon as it\n * reaches or exceeds this limit given that logStorage.isReady\n * returns true. Otherwise the log entry will be cached until the log\n * storage becomes ready. Note that the \"is ready\" condition is checked every\n * options.storeInterval milliseconds.\n * @param {number} options.storeInterval how often the logs should be stored in\n * case maxEntryLength was not exceeded.\n * @param {boolean} options.stringifyObjects indicates whether or not object\n * arguments should be \"stringified\" with JSON.stringify when a log\n * message is composed. Note that objects logged on the error log level are\n * always stringified.\n *\n * @constructor\n */\nfunction LogCollector(logStorage, options) {\n this.logStorage = logStorage;\n this.stringifyObjects = options && options.stringifyObjects ? options.stringifyObjects : false;\n this.storeInterval = options && options.storeInterval ? options.storeInterval: 30000;\n this.maxEntryLength = options && options.maxEntryLength ? options.maxEntryLength : 10000;\n // Bind the log method for each level to the corresponding method name\n // in order to implement \"global log transport\" object.\n Object.keys(Logger.levels).forEach(\n function (logLevel) {\n var methodName = Logger.levels[logLevel];\n this[methodName] = function () {\n this._log.apply(this, arguments);\n }.bind(this, logLevel);\n }.bind(this));\n /**\n * The ID of store logs interval if one is currently scheduled or\n * null otherwise.\n * @type {number|null}\n */\n this.storeLogsIntervalID = null;\n /**\n * The log messages that are to be batched into log entry when\n * {@link LogCollector._flush} method is called.\n * @type {string[]}\n */\n this.queue = [];\n /**\n * The total length of all messages currently stored in the {@link queue}.\n * @type {number}\n */\n this.totalLen = 0;\n /**\n * An array used to temporarily store log batches, before the storage gets\n * ready.\n * @type {string[]}\n */\n this.outputCache = [];\n}\n\n/**\n * Method called inside of {@link formatLogMessage} in order to covert an\n * Object argument to string. The conversion will happen when either\n * 'stringifyObjects' option is enabled or on the {@link Logger.levels.ERROR}\n * log level. The default implementation uses JSON.stringify and\n * returns \"[object with circular refs?]\" instead of an object if it fails.\n *\n * @param {object} someObject the object to be stringified.\n *\n * @return {string} the result of JSON.stringify or\n * \"[object with circular refs?]\" if any error occurs during \"stringification\".\n *\n * @protected\n */\nLogCollector.prototype.stringify = function (someObject) {\n try {\n return JSON.stringify(someObject);\n } catch (error) {\n return '[object with circular refs?]';\n }\n};\n\n/**\n * Formats log entry for the given logging level and arguments passed to the\n * Logger's log method. The first argument is log level and the next\n * arguments have to be captured using JS built-in 'arguments' variable.\n *\n * @param {Logger.levels} logLevel provides the logging level of the message to\n * be logged.\n * @param {Date} timestamp - The {@code Date} when a message has been logged.\n *\n * @return {string|null} a non-empty string representation of the log entry\n * crafted from the log arguments. If the return value is null then\n * the message wil be discarded by this LogCollector.\n *\n * @protected\n */\nLogCollector.prototype.formatLogMessage = function (\nlogLevel /* timestamp, arg2, arg3, arg4... */) {\n var msg = '';\n for (var i = 1, len = arguments.length; i < len; i++) {\n var arg = arguments[i];\n // objects logged on error level are always converted to JSON\n if ((this.stringifyObjects || logLevel === Logger.levels.ERROR) &&\n typeof arg === 'object') {\n arg = this.stringify(arg);\n }\n msg += arg;\n if (i !== len - 1) {\n msg += ' ';\n }\n }\n return msg.length ? msg : null;\n};\n\n/**\n * The log method bound to each of the logging levels in order to implement\n * \"global log transport\" object.\n *\n * @private\n */\nLogCollector.prototype._log = function() {\n\n // var logLevel = arguments[0]; first argument is the log level\n var timestamp = arguments[1];\n var msg = this.formatLogMessage.apply(this, arguments);\n if (msg) {\n // The same as the previous message aggregation logic\n var prevMessage = this.queue[this.queue.length - 1];\n var prevMessageText = prevMessage && prevMessage.text;\n if (prevMessageText === msg) {\n prevMessage.count += 1;\n } else {\n this.queue.push({\n text: msg,\n timestamp: timestamp,\n count: 1\n });\n this.totalLen += msg.length;\n }\n }\n\n if (this.totalLen >= this.maxEntryLength) {\n this._flush(true /* force */, true /* reschedule */);\n }\n};\n\n/**\n * Starts periodical \"store logs\" task which will be triggered at the interval\n * specified in the constructor options.\n */\nLogCollector.prototype.start = function () {\n this._reschedulePublishInterval();\n};\n\n/**\n * Reschedules the periodical \"store logs\" task which will store the next batch\n * log entry in the storage.\n * @private\n */\nLogCollector.prototype._reschedulePublishInterval = function () {\n if (this.storeLogsIntervalID) {\n window.clearTimeout(this.storeLogsIntervalID);\n this.storeLogsIntervalID = null;\n }\n // It's actually a timeout, because it is rescheduled on every flush\n this.storeLogsIntervalID = window.setTimeout(\n this._flush.bind(\n this, false /* do not force */, true /* reschedule */),\n this.storeInterval);\n};\n\n/**\n * Call this method to flush the log entry buffer and store it in the log\n * storage immediately (given that the storage is ready).\n */\nLogCollector.prototype.flush = function() {\n this._flush(\n false /* do not force, as it will not be stored anyway */,\n true /* reschedule next update */ );\n};\n\n/**\n * Stores the next batch log entry in the log storage.\n * @param {boolean} force enforce current logs batch to be stored or cached if\n * there is anything to be logged, but the storage is not ready yet. One of\n * legitimate reasons to force is when the logs length exceeds size limit which\n * could result in truncation.\n * @param {boolean} reschedule true if the next periodic task should be\n * scheduled after the log entry is stored. false will end the periodic\n * task cycle.\n * @private\n */\nLogCollector.prototype._flush = function(force, reschedule) {\n // Publish only if there's anything to be logged\n if (this.totalLen > 0 && (this.logStorage.isReady() || force)) {\n //FIXME avoid truncating\n // right now we don't care if the message size is \"slightly\" exceeded\n if (this.logStorage.isReady()) {\n // Sends all cached logs\n if (this.outputCache.length) {\n this.outputCache.forEach(\n function (cachedQueue) {\n this.logStorage.storeLogs(cachedQueue);\n }.bind(this)\n );\n // Clear the cache\n this.outputCache = [];\n }\n // Send current batch\n this.logStorage.storeLogs(this.queue);\n } else {\n this.outputCache.push(this.queue);\n }\n\n this.queue = [];\n this.totalLen = 0;\n }\n\n if (reschedule) {\n this._reschedulePublishInterval();\n }\n};\n\n/**\n * Stops the periodical \"store logs\" task and immediately stores any pending\n * log entries as a batch.\n */\nLogCollector.prototype.stop = function() {\n // Flush and stop publishing logs\n this._flush(false /* do not force */, false /* do not reschedule */);\n};\n\nmodule.exports = LogCollector;\n","/* Copyright @ 2015-present 8x8, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*jslint latedef:false*/\n\n/**\n * Ordered log levels.\n */\nvar levels = {\n \"trace\": 0,\n \"debug\": 1,\n \"info\": 2,\n \"log\": 3,\n \"warn\": 4,\n \"error\": 5\n};\n\n/**\n * The default transport - console\n * @type LoggerTransport\n */\nLogger.consoleTransport = console;\n\n/**\n * The array which stores currently registered global transports.\n * @type {[LoggerTransport]}\n */\nvar globalTransports = [ Logger.consoleTransport ];\n\n/**\n * Adds given {@link LoggerTransport} instance to the list of global\n * transports which means that it'll be used by all {@link Logger}s\n * @param {LoggerTransport} transport\n */\nLogger.addGlobalTransport = function(transport) {\n if (globalTransports.indexOf(transport) === -1) {\n globalTransports.push(transport);\n }\n};\n\n/**\n * Removes given {@link LoggerTransport} instance from the list of global\n * transports\n * @param {LoggerTransport} transport\n */\nLogger.removeGlobalTransport = function(transport) {\n var transportIdx = globalTransports.indexOf(transport);\n if (transportIdx !== -1) {\n globalTransports.splice(transportIdx, 1);\n }\n};\n\n/**\n * The global configuration options.\n */\nvar globalOptions = {};\n\n/**\n * Sets global options which will be used by all loggers. Changing these works\n * even after other loggers are created.\n */\nLogger.setGlobalOptions = function(options) {\n globalOptions = options || {};\n};\n\n/**\n * Parses Error's object stack trace and extracts information about the last\n * caller before the log method was called.\n * @returns JS object with info about the caller - method name, file location,\n * line and column.\n */\nfunction getCallerInfo() {\n var callerInfo = {\n methodName: \"\",\n fileLocation: \"\",\n line: null,\n column: null\n };\n //gets the part of the stack without the logger wrappers\n var error = new Error();\n var stack = error.stack? error.stack.split(\"\\n\") : [];\n if(!stack || stack.length < 3) {\n return callerInfo;\n }\n var m = null;\n if(stack[3]) {\n m = stack[3].match(/\\s*at\\s*(.+?)\\s*\\((\\S*)\\s*:(\\d*)\\s*:(\\d*)\\)/);\n }\n if(!m || m.length <= 4) {\n //Firefox && Safari\n if(stack[2].indexOf(\"log@\") === 0){\n //Safari\n callerInfo.methodName = stack[3].substr(0, stack[3].indexOf(\"@\"));\n } else {\n //Firefox\n callerInfo.methodName = stack[2].substr(0, stack[2].indexOf(\"@\"));\n }\n return callerInfo;\n }\n\n callerInfo.methodName = m[1];\n callerInfo.fileLocation = m[2];\n callerInfo.line = m[3];\n callerInfo.column = m[4];\n return callerInfo;\n}\n\n/**\n * Logs messages using the transports and level from the logger.\n * @param logger a logger instance.\n * @param level the log level of the message. See the levels variable.\n * @param arguments array with arguments that will be logged.\n */\nfunction log() {\n var logger = arguments[0], level = arguments[1],\n args = Array.prototype.slice.call(arguments, 2);\n if(levels[level] < logger.level) {\n return;\n }\n\n var callerInfo\n = !(logger.options.disableCallerInfo || globalOptions.disableCallerInfo) &&\n getCallerInfo();\n var transports = globalTransports.concat(logger.transports);\n for(var i = 0; i < transports.length; i++) {\n var t = transports[i];\n var l = t[level];\n if(l && typeof(l) === \"function\") {\n var logPrefixes = [];\n\n logPrefixes.push(new Date().toISOString());\n\n if (logger.id) {\n logPrefixes.push(\"[\" + logger.id + \"]\");\n }\n\n if (callerInfo && callerInfo.methodName.length > 1) {\n logPrefixes.push(\"<\" + callerInfo.methodName + \">: \");\n }\n\n var fullLogParts = logPrefixes.concat(args);\n\n l.bind(t).apply(t, fullLogParts);\n }\n }\n}\n\n/**\n *\n * Constructs new logger object.\n * @param level the logging level for the new logger\n * @param id optional identifier for the logger instance.\n * @param {LoggerTransport} transports optional list of handlers(objects) for\n * the logs. The handlers must support - log, warn, error, debug, info, trace.\n * @param options optional configuration file for how the logger should behave.\n * @param {boolean} options.disableCallerInfo Whether the call site of a logger\n * method invocation should be included in the log. Defaults to false, so the\n * call site will be included.\n */\nfunction Logger(level, id, transports, options) {\n this.id = id;\n this.options = options || {};\n this.transports = transports;\n if(!this.transports) {\n this.transports = [];\n }\n this.level = levels[level];\n var methods = Object.keys(levels);\n for(var i = 0; i < methods.length; i++){\n this[methods[i]] =\n log.bind(null, this, methods[i]);\n }\n}\n\n/**\n * Sets the log level for the logger.\n * @param level the new log level.\n */\nLogger.prototype.setLevel = function (level) {\n this.level = levels[level];\n};\nmodule.exports = Logger;\n\n/**\n * Enum for the supported log levels.\n */\nLogger.levels = {\n TRACE: \"trace\",\n DEBUG: \"debug\",\n INFO: \"info\",\n LOG: \"log\",\n WARN: \"warn\",\n ERROR: \"error\"\n};\n","/* Copyright @ 2015-present 8x8, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nvar Logger = require(\"./Logger\");\nvar LogCollector = require(\"./LogCollector\");\n\n/**\n * Definition of the log method\n * @name log_method\n * @function\n * @param {...*} log_args the arguments to be logged\n */\n/**\n * The logger's transport type definition.\n *\n * @typedef {object} LoggerTransport\n *\n * @property {log_method} trace method called to log on {@link Logger.levels.TRACE} logging level\n * @property {log_method} debug method called to log on {@link Logger.levels.DEBUG} logging level\n * @property {log_method} info method called to log on {@link Logger.levels.INFO} logging level\n * @property {log_method} log method called to log on {@link Logger.levels.LOG} logging level\n * @property {log_method} warn method called to log on {@link Logger.levels.WARN} logging level\n * @property {log_method} error method called to log on {@link Logger.levels.ERROR} logging level\n */\n\n/**\n * Map with the created loggers with ID.\n */\nvar idLoggers = {};\n\n/**\n * Array with the loggers without id.\n */\nvar loggers = [];\n\n/**\n * Log level for the lbrary.\n */\nvar curLevel = Logger.levels.TRACE;\n\n\nmodule.exports = {\n /**\n * Adds given {@link LoggerTransport} instance to the list of global\n * transports which means that it'll be used by all {@link Logger}s\n * @param {LoggerTransport} transport\n */\n addGlobalTransport: function(transport) {\n Logger.addGlobalTransport(transport);\n },\n /**\n * Removes given {@link LoggerTransport} instance from the list of global\n * transports\n * @param {LoggerTransport} transport\n */\n removeGlobalTransport: function(transport) {\n Logger.removeGlobalTransport(transport);\n },\n /**\n * Sets global options which will be used by all loggers. Changing these\n * works even after other loggers are created.\n */\n setGlobalOptions: function(options) {\n Logger.setGlobalOptions(options);\n },\n /**\n * Creates new logger.\n * @arguments the same as Logger constructor\n */\n getLogger: function(id, transports, options) {\n var logger = new Logger(curLevel, id, transports, options);\n if(id) {\n idLoggers[id] = idLoggers[id] || [];\n idLoggers[id].push(logger);\n } else {\n loggers.push(logger);\n }\n return logger;\n },\n /**\n * Changes the log level for the existing loggers by id.\n * @param level the new log level.\n * @param id if specified the level will be changed only for loggers with the\n * same id. Otherwise the operation will affect all loggers that don't\n * have id.\n */\n setLogLevelById: function(level, id) {\n var l = id? (idLoggers[id] || []) : loggers;\n for(var i = 0; i < l.length; i++) {\n l[i].setLevel(level);\n }\n },\n /**\n * Changes the log level for all existing loggers.\n * @param level the new log level.\n */\n setLogLevel: function (level) {\n curLevel = level;\n var i = 0;\n for(; i < loggers.length; i++) {\n loggers[i].setLevel(level);\n }\n\n for(var id in idLoggers) {\n var l = idLoggers[id] || [];\n for(i = 0; i < l.length; i++) {\n l[i].setLevel(level);\n }\n }\n },\n /**\n * The supported log levels.\n */\n levels: Logger.levels,\n /**\n * Exports the LogCollector.\n */\n LogCollector: LogCollector\n};\n","var grammar = module.exports = {\n v: [{\n name: 'version',\n reg: /^(\\d*)$/\n }],\n o: [{\n // o=- 20518 0 IN IP4 203.0.113.1\n // NB: sessionId will be a String in most cases because it is huge\n name: 'origin',\n reg: /^(\\S*) (\\d*) (\\d*) (\\S*) IP(\\d) (\\S*)/,\n names: ['username', 'sessionId', 'sessionVersion', 'netType', 'ipVer', 'address'],\n format: '%s %s %d %s IP%d %s'\n }],\n // default parsing of these only (though some of these feel outdated)\n s: [{ name: 'name' }],\n i: [{ name: 'description' }],\n u: [{ name: 'uri' }],\n e: [{ name: 'email' }],\n p: [{ name: 'phone' }],\n z: [{ name: 'timezones' }], // TODO: this one can actually be parsed properly...\n r: [{ name: 'repeats' }], // TODO: this one can also be parsed properly\n // k: [{}], // outdated thing ignored\n t: [{\n // t=0 0\n name: 'timing',\n reg: /^(\\d*) (\\d*)/,\n names: ['start', 'stop'],\n format: '%d %d'\n }],\n c: [{\n // c=IN IP4 10.47.197.26\n name: 'connection',\n reg: /^IN IP(\\d) (\\S*)/,\n names: ['version', 'ip'],\n format: 'IN IP%d %s'\n }],\n b: [{\n // b=AS:4000\n push: 'bandwidth',\n reg: /^(TIAS|AS|CT|RR|RS):(\\d*)/,\n names: ['type', 'limit'],\n format: '%s:%s'\n }],\n m: [{\n // m=video 51744 RTP/AVP 126 97 98 34 31\n // NB: special - pushes to session\n // TODO: rtp/fmtp should be filtered by the payloads found here?\n reg: /^(\\w*) (\\d*) ([\\w/]*)(?: (.*))?/,\n names: ['type', 'port', 'protocol', 'payloads'],\n format: '%s %d %s %s'\n }],\n a: [\n {\n // a=rtpmap:110 opus/48000/2\n push: 'rtp',\n reg: /^rtpmap:(\\d*) ([\\w\\-.]*)(?:\\s*\\/(\\d*)(?:\\s*\\/(\\S*))?)?/,\n names: ['payload', 'codec', 'rate', 'encoding'],\n format: function (o) {\n return (o.encoding)\n ? 'rtpmap:%d %s/%s/%s'\n : o.rate\n ? 'rtpmap:%d %s/%s'\n : 'rtpmap:%d %s';\n }\n },\n {\n // a=fmtp:108 profile-level-id=24;object=23;bitrate=64000\n // a=fmtp:111 minptime=10; useinbandfec=1\n push: 'fmtp',\n reg: /^fmtp:(\\d*) ([\\S| ]*)/,\n names: ['payload', 'config'],\n format: 'fmtp:%d %s'\n },\n {\n // a=control:streamid=0\n name: 'control',\n reg: /^control:(.*)/,\n format: 'control:%s'\n },\n {\n // a=rtcp:65179 IN IP4 193.84.77.194\n name: 'rtcp',\n reg: /^rtcp:(\\d*)(?: (\\S*) IP(\\d) (\\S*))?/,\n names: ['port', 'netType', 'ipVer', 'address'],\n format: function (o) {\n return (o.address != null)\n ? 'rtcp:%d %s IP%d %s'\n : 'rtcp:%d';\n }\n },\n {\n // a=rtcp-fb:98 trr-int 100\n push: 'rtcpFbTrrInt',\n reg: /^rtcp-fb:(\\*|\\d*) trr-int (\\d*)/,\n names: ['payload', 'value'],\n format: 'rtcp-fb:%s trr-int %d'\n },\n {\n // a=rtcp-fb:98 nack rpsi\n push: 'rtcpFb',\n reg: /^rtcp-fb:(\\*|\\d*) ([\\w-_]*)(?: ([\\w-_]*))?/,\n names: ['payload', 'type', 'subtype'],\n format: function (o) {\n return (o.subtype != null)\n ? 'rtcp-fb:%s %s %s'\n : 'rtcp-fb:%s %s';\n }\n },\n {\n // a=extmap:2 urn:ietf:params:rtp-hdrext:toffset\n // a=extmap:1/recvonly URI-gps-string\n // a=extmap:3 urn:ietf:params:rtp-hdrext:encrypt urn:ietf:params:rtp-hdrext:smpte-tc 25@600/24\n push: 'ext',\n reg: /^extmap:(\\d+)(?:\\/(\\w+))?(?: (urn:ietf:params:rtp-hdrext:encrypt))? (\\S*)(?: (\\S*))?/,\n names: ['value', 'direction', 'encrypt-uri', 'uri', 'config'],\n format: function (o) {\n return (\n 'extmap:%d' +\n (o.direction ? '/%s' : '%v') +\n (o['encrypt-uri'] ? ' %s' : '%v') +\n ' %s' +\n (o.config ? ' %s' : '')\n );\n }\n },\n {\n // a=extmap-allow-mixed\n name: 'extmapAllowMixed',\n reg: /^(extmap-allow-mixed)/\n },\n {\n // a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:PS1uQCVeeCFCanVmcjkpPywjNWhcYD0mXXtxaVBR|2^20|1:32\n push: 'crypto',\n reg: /^crypto:(\\d*) ([\\w_]*) (\\S*)(?: (\\S*))?/,\n names: ['id', 'suite', 'config', 'sessionConfig'],\n format: function (o) {\n return (o.sessionConfig != null)\n ? 'crypto:%d %s %s %s'\n : 'crypto:%d %s %s';\n }\n },\n {\n // a=setup:actpass\n name: 'setup',\n reg: /^setup:(\\w*)/,\n format: 'setup:%s'\n },\n {\n // a=connection:new\n name: 'connectionType',\n reg: /^connection:(new|existing)/,\n format: 'connection:%s'\n },\n {\n // a=mid:1\n name: 'mid',\n reg: /^mid:([^\\s]*)/,\n format: 'mid:%s'\n },\n {\n // a=msid:0c8b064d-d807-43b4-b434-f92a889d8587 98178685-d409-46e0-8e16-7ef0db0db64a\n name: 'msid',\n reg: /^msid:(.*)/,\n format: 'msid:%s'\n },\n {\n // a=ptime:20\n name: 'ptime',\n reg: /^ptime:(\\d*(?:\\.\\d*)*)/,\n format: 'ptime:%d'\n },\n {\n // a=maxptime:60\n name: 'maxptime',\n reg: /^maxptime:(\\d*(?:\\.\\d*)*)/,\n format: 'maxptime:%d'\n },\n {\n // a=sendrecv\n name: 'direction',\n reg: /^(sendrecv|recvonly|sendonly|inactive)/\n },\n {\n // a=ice-lite\n name: 'icelite',\n reg: /^(ice-lite)/\n },\n {\n // a=ice-ufrag:F7gI\n name: 'iceUfrag',\n reg: /^ice-ufrag:(\\S*)/,\n format: 'ice-ufrag:%s'\n },\n {\n // a=ice-pwd:x9cml/YzichV2+XlhiMu8g\n name: 'icePwd',\n reg: /^ice-pwd:(\\S*)/,\n format: 'ice-pwd:%s'\n },\n {\n // a=fingerprint:SHA-1 00:11:22:33:44:55:66:77:88:99:AA:BB:CC:DD:EE:FF:00:11:22:33\n name: 'fingerprint',\n reg: /^fingerprint:(\\S*) (\\S*)/,\n names: ['type', 'hash'],\n format: 'fingerprint:%s %s'\n },\n {\n // a=candidate:0 1 UDP 2113667327 203.0.113.1 54400 typ host\n // a=candidate:1162875081 1 udp 2113937151 192.168.34.75 60017 typ host generation 0 network-id 3 network-cost 10\n // a=candidate:3289912957 2 udp 1845501695 193.84.77.194 60017 typ srflx raddr 192.168.34.75 rport 60017 generation 0 network-id 3 network-cost 10\n // a=candidate:229815620 1 tcp 1518280447 192.168.150.19 60017 typ host tcptype active generation 0 network-id 3 network-cost 10\n // a=candidate:3289912957 2 tcp 1845501695 193.84.77.194 60017 typ srflx raddr 192.168.34.75 rport 60017 tcptype passive generation 0 network-id 3 network-cost 10\n push:'candidates',\n reg: /^candidate:(\\S*) (\\d*) (\\S*) (\\d*) (\\S*) (\\d*) typ (\\S*)(?: raddr (\\S*) rport (\\d*))?(?: tcptype (\\S*))?(?: generation (\\d*))?(?: network-id (\\d*))?(?: network-cost (\\d*))?/,\n names: ['foundation', 'component', 'transport', 'priority', 'ip', 'port', 'type', 'raddr', 'rport', 'tcptype', 'generation', 'network-id', 'network-cost'],\n format: function (o) {\n var str = 'candidate:%s %d %s %d %s %d typ %s';\n\n str += (o.raddr != null) ? ' raddr %s rport %d' : '%v%v';\n\n // NB: candidate has three optional chunks, so %void middles one if it's missing\n str += (o.tcptype != null) ? ' tcptype %s' : '%v';\n\n if (o.generation != null) {\n str += ' generation %d';\n }\n\n str += (o['network-id'] != null) ? ' network-id %d' : '%v';\n str += (o['network-cost'] != null) ? ' network-cost %d' : '%v';\n return str;\n }\n },\n {\n // a=end-of-candidates (keep after the candidates line for readability)\n name: 'endOfCandidates',\n reg: /^(end-of-candidates)/\n },\n {\n // a=remote-candidates:1 203.0.113.1 54400 2 203.0.113.1 54401 ...\n name: 'remoteCandidates',\n reg: /^remote-candidates:(.*)/,\n format: 'remote-candidates:%s'\n },\n {\n // a=ice-options:google-ice\n name: 'iceOptions',\n reg: /^ice-options:(\\S*)/,\n format: 'ice-options:%s'\n },\n {\n // a=ssrc:2566107569 cname:t9YU8M1UxTF8Y1A1\n push: 'ssrcs',\n reg: /^ssrc:(\\d*) ([\\w_-]*)(?::(.*))?/,\n names: ['id', 'attribute', 'value'],\n format: function (o) {\n var str = 'ssrc:%d';\n if (o.attribute != null) {\n str += ' %s';\n if (o.value != null) {\n str += ':%s';\n }\n }\n return str;\n }\n },\n {\n // a=ssrc-group:FEC 1 2\n // a=ssrc-group:FEC-FR 3004364195 1080772241\n push: 'ssrcGroups',\n // token-char = %x21 / %x23-27 / %x2A-2B / %x2D-2E / %x30-39 / %x41-5A / %x5E-7E\n reg: /^ssrc-group:([\\x21\\x23\\x24\\x25\\x26\\x27\\x2A\\x2B\\x2D\\x2E\\w]*) (.*)/,\n names: ['semantics', 'ssrcs'],\n format: 'ssrc-group:%s %s'\n },\n {\n // a=msid-semantic: WMS Jvlam5X3SX1OP6pn20zWogvaKJz5Hjf9OnlV\n name: 'msidSemantic',\n reg: /^msid-semantic:\\s?(\\w*) (\\S*)/,\n names: ['semantic', 'token'],\n format: 'msid-semantic: %s %s' // space after ':' is not accidental\n },\n {\n // a=group:BUNDLE audio video\n push: 'groups',\n reg: /^group:(\\w*) (.*)/,\n names: ['type', 'mids'],\n format: 'group:%s %s'\n },\n {\n // a=rtcp-mux\n name: 'rtcpMux',\n reg: /^(rtcp-mux)/\n },\n {\n // a=rtcp-rsize\n name: 'rtcpRsize',\n reg: /^(rtcp-rsize)/\n },\n {\n // a=sctpmap:5000 webrtc-datachannel 1024\n name: 'sctpmap',\n reg: /^sctpmap:([\\w_/]*) (\\S*)(?: (\\S*))?/,\n names: ['sctpmapNumber', 'app', 'maxMessageSize'],\n format: function (o) {\n return (o.maxMessageSize != null)\n ? 'sctpmap:%s %s %s'\n : 'sctpmap:%s %s';\n }\n },\n {\n // a=x-google-flag:conference\n name: 'xGoogleFlag',\n reg: /^x-google-flag:([^\\s]*)/,\n format: 'x-google-flag:%s'\n },\n {\n // a=rid:1 send max-width=1280;max-height=720;max-fps=30;depend=0\n push: 'rids',\n reg: /^rid:([\\d\\w]+) (\\w+)(?: ([\\S| ]*))?/,\n names: ['id', 'direction', 'params'],\n format: function (o) {\n return (o.params) ? 'rid:%s %s %s' : 'rid:%s %s';\n }\n },\n {\n // a=imageattr:97 send [x=800,y=640,sar=1.1,q=0.6] [x=480,y=320] recv [x=330,y=250]\n // a=imageattr:* send [x=800,y=640] recv *\n // a=imageattr:100 recv [x=320,y=240]\n push: 'imageattrs',\n reg: new RegExp(\n // a=imageattr:97\n '^imageattr:(\\\\d+|\\\\*)' +\n // send [x=800,y=640,sar=1.1,q=0.6] [x=480,y=320]\n '[\\\\s\\\\t]+(send|recv)[\\\\s\\\\t]+(\\\\*|\\\\[\\\\S+\\\\](?:[\\\\s\\\\t]+\\\\[\\\\S+\\\\])*)' +\n // recv [x=330,y=250]\n '(?:[\\\\s\\\\t]+(recv|send)[\\\\s\\\\t]+(\\\\*|\\\\[\\\\S+\\\\](?:[\\\\s\\\\t]+\\\\[\\\\S+\\\\])*))?'\n ),\n names: ['pt', 'dir1', 'attrs1', 'dir2', 'attrs2'],\n format: function (o) {\n return 'imageattr:%s %s %s' + (o.dir2 ? ' %s %s' : '');\n }\n },\n {\n // a=simulcast:send 1,2,3;~4,~5 recv 6;~7,~8\n // a=simulcast:recv 1;4,5 send 6;7\n name: 'simulcast',\n reg: new RegExp(\n // a=simulcast:\n '^simulcast:' +\n // send 1,2,3;~4,~5\n '(send|recv) ([a-zA-Z0-9\\\\-_~;,]+)' +\n // space + recv 6;~7,~8\n '(?:\\\\s?(send|recv) ([a-zA-Z0-9\\\\-_~;,]+))?' +\n // end\n '$'\n ),\n names: ['dir1', 'list1', 'dir2', 'list2'],\n format: function (o) {\n return 'simulcast:%s %s' + (o.dir2 ? ' %s %s' : '');\n }\n },\n {\n // old simulcast draft 03 (implemented by Firefox)\n // https://tools.ietf.org/html/draft-ietf-mmusic-sdp-simulcast-03\n // a=simulcast: recv pt=97;98 send pt=97\n // a=simulcast: send rid=5;6;7 paused=6,7\n name: 'simulcast_03',\n reg: /^simulcast:[\\s\\t]+([\\S+\\s\\t]+)$/,\n names: ['value'],\n format: 'simulcast: %s'\n },\n {\n // a=framerate:25\n // a=framerate:29.97\n name: 'framerate',\n reg: /^framerate:(\\d+(?:$|\\.\\d+))/,\n format: 'framerate:%s'\n },\n {\n // RFC4570\n // a=source-filter: incl IN IP4 239.5.2.31 10.1.15.5\n name: 'sourceFilter',\n reg: /^source-filter: *(excl|incl) (\\S*) (IP4|IP6|\\*) (\\S*) (.*)/,\n names: ['filterMode', 'netType', 'addressTypes', 'destAddress', 'srcList'],\n format: 'source-filter: %s %s %s %s %s'\n },\n {\n // a=bundle-only\n name: 'bundleOnly',\n reg: /^(bundle-only)/\n },\n {\n // a=label:1\n name: 'label',\n reg: /^label:(.+)/,\n format: 'label:%s'\n },\n {\n // RFC version 26 for SCTP over DTLS\n // https://tools.ietf.org/html/draft-ietf-mmusic-sctp-sdp-26#section-5\n name: 'sctpPort',\n reg: /^sctp-port:(\\d+)$/,\n format: 'sctp-port:%s'\n },\n {\n // RFC version 26 for SCTP over DTLS\n // https://tools.ietf.org/html/draft-ietf-mmusic-sctp-sdp-26#section-6\n name: 'maxMessageSize',\n reg: /^max-message-size:(\\d+)$/,\n format: 'max-message-size:%s'\n },\n {\n // RFC7273\n // a=ts-refclk:ptp=IEEE1588-2008:39-A7-94-FF-FE-07-CB-D0:37\n push:'tsRefClocks',\n reg: /^ts-refclk:([^\\s=]*)(?:=(\\S*))?/,\n names: ['clksrc', 'clksrcExt'],\n format: function (o) {\n return 'ts-refclk:%s' + (o.clksrcExt != null ? '=%s' : '');\n }\n },\n {\n // RFC7273\n // a=mediaclk:direct=963214424\n name:'mediaClk',\n reg: /^mediaclk:(?:id=(\\S*))? *([^\\s=]*)(?:=(\\S*))?(?: *rate=(\\d+)\\/(\\d+))?/,\n names: ['id', 'mediaClockName', 'mediaClockValue', 'rateNumerator', 'rateDenominator'],\n format: function (o) {\n var str = 'mediaclk:';\n str += (o.id != null ? 'id=%s %s' : '%v%s');\n str += (o.mediaClockValue != null ? '=%s' : '');\n str += (o.rateNumerator != null ? ' rate=%s' : '');\n str += (o.rateDenominator != null ? '/%s' : '');\n return str;\n }\n },\n {\n // a=keywds:keywords\n name: 'keywords',\n reg: /^keywds:(.+)$/,\n format: 'keywds:%s'\n },\n {\n // a=content:main\n name: 'content',\n reg: /^content:(.+)/,\n format: 'content:%s'\n },\n // BFCP https://tools.ietf.org/html/rfc4583\n {\n // a=floorctrl:c-s\n name: 'bfcpFloorCtrl',\n reg: /^floorctrl:(c-only|s-only|c-s)/,\n format: 'floorctrl:%s'\n },\n {\n // a=confid:1\n name: 'bfcpConfId',\n reg: /^confid:(\\d+)/,\n format: 'confid:%s'\n },\n {\n // a=userid:1\n name: 'bfcpUserId',\n reg: /^userid:(\\d+)/,\n format: 'userid:%s'\n },\n {\n // a=floorid:1\n name: 'bfcpFloorId',\n reg: /^floorid:(.+) (?:m-stream|mstrm):(.+)/,\n names: ['id', 'mStream'],\n format: 'floorid:%s mstrm:%s'\n },\n {\n // any a= that we don't understand is kept verbatim on media.invalid\n push: 'invalid',\n names: ['value']\n }\n ]\n};\n\n// set sensible defaults to avoid polluting the grammar with boring details\nObject.keys(grammar).forEach(function (key) {\n var objs = grammar[key];\n objs.forEach(function (obj) {\n if (!obj.reg) {\n obj.reg = /(.*)/;\n }\n if (!obj.format) {\n obj.format = '%s';\n }\n });\n});\n","var parser = require('./parser');\nvar writer = require('./writer');\n\nexports.write = writer;\nexports.parse = parser.parse;\nexports.parseParams = parser.parseParams;\nexports.parseFmtpConfig = parser.parseFmtpConfig; // Alias of parseParams().\nexports.parsePayloads = parser.parsePayloads;\nexports.parseRemoteCandidates = parser.parseRemoteCandidates;\nexports.parseImageAttributes = parser.parseImageAttributes;\nexports.parseSimulcastStreamList = parser.parseSimulcastStreamList;\n","var toIntIfInt = function (v) {\n return String(Number(v)) === v ? Number(v) : v;\n};\n\nvar attachProperties = function (match, location, names, rawName) {\n if (rawName && !names) {\n location[rawName] = toIntIfInt(match[1]);\n }\n else {\n for (var i = 0; i < names.length; i += 1) {\n if (match[i+1] != null) {\n location[names[i]] = toIntIfInt(match[i+1]);\n }\n }\n }\n};\n\nvar parseReg = function (obj, location, content) {\n var needsBlank = obj.name && obj.names;\n if (obj.push && !location[obj.push]) {\n location[obj.push] = [];\n }\n else if (needsBlank && !location[obj.name]) {\n location[obj.name] = {};\n }\n var keyLocation = obj.push ?\n {} : // blank object that will be pushed\n needsBlank ? location[obj.name] : location; // otherwise, named location or root\n\n attachProperties(content.match(obj.reg), keyLocation, obj.names, obj.name);\n\n if (obj.push) {\n location[obj.push].push(keyLocation);\n }\n};\n\nvar grammar = require('./grammar');\nvar validLine = RegExp.prototype.test.bind(/^([a-z])=(.*)/);\n\nexports.parse = function (sdp) {\n var session = {}\n , media = []\n , location = session; // points at where properties go under (one of the above)\n\n // parse lines we understand\n sdp.split(/(\\r\\n|\\r|\\n)/).filter(validLine).forEach(function (l) {\n var type = l[0];\n var content = l.slice(2);\n if (type === 'm') {\n media.push({rtp: [], fmtp: []});\n location = media[media.length-1]; // point at latest media line\n }\n\n for (var j = 0; j < (grammar[type] || []).length; j += 1) {\n var obj = grammar[type][j];\n if (obj.reg.test(content)) {\n return parseReg(obj, location, content);\n }\n }\n });\n\n session.media = media; // link it up\n return session;\n};\n\nvar paramReducer = function (acc, expr) {\n var s = expr.split(/=(.+)/, 2);\n if (s.length === 2) {\n acc[s[0]] = toIntIfInt(s[1]);\n } else if (s.length === 1 && expr.length > 1) {\n acc[s[0]] = undefined;\n }\n return acc;\n};\n\nexports.parseParams = function (str) {\n return str.split(/;\\s?/).reduce(paramReducer, {});\n};\n\n// For backward compatibility - alias will be removed in 3.0.0\nexports.parseFmtpConfig = exports.parseParams;\n\nexports.parsePayloads = function (str) {\n return str.toString().split(' ').map(Number);\n};\n\nexports.parseRemoteCandidates = function (str) {\n var candidates = [];\n var parts = str.split(' ').map(toIntIfInt);\n for (var i = 0; i < parts.length; i += 3) {\n candidates.push({\n component: parts[i],\n ip: parts[i + 1],\n port: parts[i + 2]\n });\n }\n return candidates;\n};\n\nexports.parseImageAttributes = function (str) {\n return str.split(' ').map(function (item) {\n return item.substring(1, item.length-1).split(',').reduce(paramReducer, {});\n });\n};\n\nexports.parseSimulcastStreamList = function (str) {\n return str.split(';').map(function (stream) {\n return stream.split(',').map(function (format) {\n var scid, paused = false;\n\n if (format[0] !== '~') {\n scid = toIntIfInt(format);\n } else {\n scid = toIntIfInt(format.substring(1, format.length));\n paused = true;\n }\n\n return {\n scid: scid,\n paused: paused\n };\n });\n });\n};\n","var grammar = require('./grammar');\n\n// customized util.format - discards excess arguments and can void middle ones\nvar formatRegExp = /%[sdv%]/g;\nvar format = function (formatStr) {\n var i = 1;\n var args = arguments;\n var len = args.length;\n return formatStr.replace(formatRegExp, function (x) {\n if (i >= len) {\n return x; // missing argument\n }\n var arg = args[i];\n i += 1;\n switch (x) {\n case '%%':\n return '%';\n case '%s':\n return String(arg);\n case '%d':\n return Number(arg);\n case '%v':\n return '';\n }\n });\n // NB: we discard excess arguments - they are typically undefined from makeLine\n};\n\nvar makeLine = function (type, obj, location) {\n var str = obj.format instanceof Function ?\n (obj.format(obj.push ? location : location[obj.name])) :\n obj.format;\n\n var args = [type + '=' + str];\n if (obj.names) {\n for (var i = 0; i < obj.names.length; i += 1) {\n var n = obj.names[i];\n if (obj.name) {\n args.push(location[obj.name][n]);\n }\n else { // for mLine and push attributes\n args.push(location[obj.names[i]]);\n }\n }\n }\n else {\n args.push(location[obj.name]);\n }\n return format.apply(null, args);\n};\n\n// RFC specified order\n// TODO: extend this with all the rest\nvar defaultOuterOrder = [\n 'v', 'o', 's', 'i',\n 'u', 'e', 'p', 'c',\n 'b', 't', 'r', 'z', 'a'\n];\nvar defaultInnerOrder = ['i', 'c', 'b', 'a'];\n\n\nmodule.exports = function (session, opts) {\n opts = opts || {};\n // ensure certain properties exist\n if (session.version == null) {\n session.version = 0; // 'v=0' must be there (only defined version atm)\n }\n if (session.name == null) {\n session.name = ' '; // 's= ' must be there if no meaningful name set\n }\n session.media.forEach(function (mLine) {\n if (mLine.payloads == null) {\n mLine.payloads = '';\n }\n });\n\n var outerOrder = opts.outerOrder || defaultOuterOrder;\n var innerOrder = opts.innerOrder || defaultInnerOrder;\n var sdp = [];\n\n // loop through outerOrder for matching properties on session\n outerOrder.forEach(function (type) {\n grammar[type].forEach(function (obj) {\n if (obj.name in session && session[obj.name] != null) {\n sdp.push(makeLine(type, obj, session));\n }\n else if (obj.push in session && session[obj.push] != null) {\n session[obj.push].forEach(function (el) {\n sdp.push(makeLine(type, obj, el));\n });\n }\n });\n });\n\n // then for each media line, follow the innerOrder\n session.media.forEach(function (mLine) {\n sdp.push(makeLine('m', grammar.m[0], mLine));\n\n innerOrder.forEach(function (type) {\n grammar[type].forEach(function (obj) {\n if (obj.name in mLine && mLine[obj.name] != null) {\n sdp.push(makeLine(type, obj, mLine));\n }\n else if (obj.push in mLine && mLine[obj.push] != null) {\n mLine[obj.push].forEach(function (el) {\n sdp.push(makeLine(type, obj, el));\n });\n }\n });\n });\n });\n\n return sdp.join('\\r\\n') + '\\r\\n';\n};\n","/* Copyright @ 2016 Atlassian Pty Ltd\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nvar transform = require('sdp-transform');\nvar transformUtils = require('./transform-utils');\nvar parseSsrcs = transformUtils.parseSsrcs;\nvar writeSsrcs = transformUtils.writeSsrcs;\n\n//region Constants\n\nvar DEFAULT_NUM_OF_LAYERS = 3;\n\n//endregion\n\nfunction getSsrcAttribute (mLine, ssrc, attributeName) {\n return mLine\n .ssrcs\n .filter(function(ssrcInfo) { return ssrcInfo.id === ssrc; })\n .filter(function(ssrcInfo) { return ssrcInfo.attribute === attributeName; })\n .map(function(ssrcInfo) { return ssrcInfo.value; })[0];\n}\n\n//region Ctor\n\nfunction Simulcast(options) {\n\n this.options = options ? options : {};\n\n if (!this.options.numOfLayers) {\n this.options.numOfLayers = DEFAULT_NUM_OF_LAYERS;\n }\n console.log(\"SdpSimulcast: using \" + this.options.numOfLayers + \" layers\");\n\n /**\n * An IN-ORDER list of the simulcast ssrcs\n * @type {list}\n */\n this.ssrcCache = [];\n}\n\n//endregion\n\n//region Stateless private utility functions\n\n/**\n * Returns a random integer between min (included) and max (excluded)\n * Using Math.round() gives a non-uniform distribution!\n * @returns {number}\n */\nfunction generateSSRC() {\n var min = 0, max = 0xffffffff;\n return Math.floor(Math.random() * (max - min)) + min;\n};\n\nfunction processVideo(session, action) {\n if (session == null || !Array.isArray(session.media)) {\n return;\n }\n\n session.media.forEach(function (mLine) {\n if (mLine.type === 'video') {\n action(mLine);\n }\n });\n};\n\nfunction validateDescription(desc)\n{\n return desc && desc != null\n && desc.type && desc.type != ''\n && desc.sdp && desc.sdp != '';\n}\n\nfunction explodeRemoteSimulcast(mLine) {\n\n if (!mLine || !Array.isArray(mLine.ssrcGroups)) {\n return;\n }\n\n var sources = parseSsrcs(mLine);\n var order = [];\n\n // Find the SIM group and explode its sources.\n var j = mLine.ssrcGroups.length;\n while (j--) {\n\n if (mLine.ssrcGroups[j].semantics !== 'SIM') {\n continue;\n }\n\n var simulcastSsrcs = mLine.ssrcGroups[j].ssrcs.split(' ');\n\n for (var i = 0; i < simulcastSsrcs.length; i++) {\n\n var ssrc = simulcastSsrcs[i];\n order.push(ssrc);\n\n var parts = sources[ssrc].msid.split(' ');\n sources[ssrc].msid = [parts[0], '/', i, ' ', parts[1], '/', i].join('');\n sources[ssrc].cname = [sources[ssrc].cname, '/', i].join('');\n\n // Remove all the groups that this SSRC participates in.\n mLine.ssrcGroups.forEach(function (relatedGroup) {\n if (relatedGroup.semantics === 'SIM') {\n return;\n }\n\n var relatedSsrcs = relatedGroup.ssrcs.split(' ');\n if (relatedSsrcs.indexOf(ssrc) === -1) {\n return;\n }\n\n // Nuke all the related SSRCs.\n relatedSsrcs.forEach(function (relatedSSRC) {\n sources[relatedSSRC].msid = sources[ssrc].msid;\n sources[relatedSSRC].cname = sources[ssrc].cname;\n if (relatedSSRC !== ssrc) {\n order.push(relatedSSRC);\n }\n });\n\n // Schedule the related group for nuking.\n })\n }\n\n mLine.ssrcs = writeSsrcs(sources, order);\n mLine.ssrcGroups.splice(j, 1);\n };\n}\n\nfunction implodeRemoteSimulcast(mLine) {\n\n if (!mLine || !Array.isArray(mLine.ssrcGroups)) {\n console.info('Halt: There are no SSRC groups in the remote ' +\n 'description.');\n return;\n }\n\n var sources = parseSsrcs(mLine);\n\n // Find the SIM group and nuke it.\n mLine.ssrcGroups.forEach(function (simulcastGroup) {\n if (simulcastGroup.semantics !== 'SIM') {\n return;\n }\n\n console.info(\"Imploding SIM group: \" + simulcastGroup.ssrcs);\n // Schedule the SIM group for nuking.\n simulcastGroup.nuke = true;\n\n var simulcastSsrcs = simulcastGroup.ssrcs.split(' ');\n\n // Nuke all the higher layer SSRCs.\n for (var i = 1; i < simulcastSsrcs.length; i++) {\n\n var ssrc = simulcastSsrcs[i];\n delete sources[ssrc];\n\n // Remove all the groups that this SSRC participates in.\n mLine.ssrcGroups.forEach(function (relatedGroup) {\n if (relatedGroup.semantics === 'SIM') {\n return;\n }\n\n var relatedSsrcs = relatedGroup.ssrcs.split(' ');\n if (relatedSsrcs.indexOf(ssrc) === -1) {\n return;\n }\n\n // Nuke all the related SSRCs.\n relatedSsrcs.forEach(function (relatedSSRC) {\n delete sources[relatedSSRC];\n });\n\n // Schedule the related group for nuking.\n relatedGroup.nuke = true;\n })\n }\n\n return;\n });\n\n mLine.ssrcs = writeSsrcs(sources);\n\n // Nuke all the scheduled groups.\n var i = mLine.ssrcGroups.length;\n while (i--) {\n if (mLine.ssrcGroups[i].nuke) {\n mLine.ssrcGroups.splice(i, 1);\n }\n }\n}\n\nfunction removeGoogConference(mLine) {\n if (!mLine || typeof mLine.xGoogleFlag === 'undefined') {\n return;\n }\n\n mLine.xGoogleFlag = undefined;\n}\n\nfunction assertGoogConference(mLine) {\n if (!mLine) {\n return;\n }\n\n if (!Array.isArray(mLine.invalid)) {\n mLine.invalid = [];\n }\n\n if (!mLine.invalid.some(\n function (i) { return i.value === 'x-google-flag:conference' })) {\n mLine.invalid.push({'value': 'x-google-flag:conference'});\n }\n}\n\nSimulcast.prototype.clearSsrcCache = function() {\n this.ssrcCache = [];\n}\n\n/**\n * When we start as video muted, all of the video\n * ssrcs get generated so we can include them as part\n * of the original session-accept. That means we\n * need this library to restore to those same ssrcs\n * the first time we unmute, so we need the ability to\n * force its cache\n */\nSimulcast.prototype.setSsrcCache = function(ssrcs) {\n this.ssrcCache = ssrcs;\n}\n\n//endregion\n\n//region \"Private\" functions\n\n/**\n * Given a video mLine, return a list of the video ssrcs\n * in simulcast layer order (returns a list of just\n * the primary ssrc if there are no simulcast layers)\n */\nSimulcast.prototype._parseSimLayers = function (mLine) {\n var simGroup = mLine.ssrcGroups &&\n mLine.ssrcGroups.find(function(group) { return group.semantics === \"SIM\"; });\n if (simGroup) {\n return simGroup.ssrcs\n .split(\" \")\n .map(function(ssrcStr) { return parseInt(ssrcStr) });\n } else {\n return [mLine.ssrcs[0].id];\n }\n}\n\nSimulcast.prototype._buildNewToOldSsrcMap = function (newSsrcList, oldSsrcList) {\n var ssrcMap = {};\n for (var i = 0; i < newSsrcList.length; ++i) {\n var newSsrc = newSsrcList[i];\n var oldSsrc = oldSsrcList[i] || null;\n ssrcMap[newSsrc] = oldSsrc;\n }\n return ssrcMap;\n}\n\nSimulcast.prototype._fillInSourceDataFromCache = function(mLine) {\n console.log(\"SdpSimulcast restoring from cache: \", this.ssrcCache);\n var newSimSsrcs = this._parseSimLayers(mLine);\n console.log(\"SdpSimulcast Parsed new sim ssrcs: \", newSimSsrcs);\n var newMsid = getSsrcAttribute(mLine, newSimSsrcs[0], \"msid\");\n var newCname = getSsrcAttribute(mLine, newSimSsrcs[0], \"cname\");\n var ssrcsToReplace = this._buildNewToOldSsrcMap(newSimSsrcs, this.ssrcCache);\n console.log(\"SdpSimulcast built replacement map: \", ssrcsToReplace);\n // New sdp might only have 1 layer, so not every cached ssrc will have a new one\n // to replace directly\n var ssrcsToAdd = this.ssrcCache\n .filter(function(ssrc) { return Object.values(ssrcsToReplace).indexOf(ssrc) === -1; });\n console.log(\"SdpSimulcast built ssrcs to add: \", ssrcsToAdd);\n\n // First do the replacements\n mLine.ssrcs.forEach(function(ssrc) {\n if (ssrcsToReplace[ssrc.id]) {\n ssrc.id = ssrcsToReplace[ssrc.id];\n }\n });\n // Now the adds\n ssrcsToAdd.forEach(function(ssrc) {\n mLine.ssrcs.push({\n id: ssrc,\n attribute: \"msid\",\n value: newMsid\n });\n mLine.ssrcs.push({\n id: ssrc,\n attribute: \"cname\",\n value: newCname\n });\n });\n mLine.ssrcGroups = mLine.ssrcGroups || [];\n mLine.ssrcGroups.push({\n semantics: \"SIM\",\n ssrcs: this.ssrcCache.join(\" \")\n });\n return mLine;\n}\n\nSimulcast.prototype._generateSourceData = function(mLine, primarySsrc) {\n var addAssociatedStream = function(mLine, ssrc) {\n mLine.ssrcs.push({\n id: ssrc,\n attribute: \"cname\",\n value: primarySsrcCname\n });\n mLine.ssrcs.push({\n id: ssrc,\n attribute: \"msid\",\n value: primarySsrcMsid\n });\n }\n var primarySsrcMsid = getSsrcAttribute(mLine, primarySsrc, \"msid\");\n var primarySsrcCname = getSsrcAttribute(mLine, primarySsrc, \"cname\");\n\n // In Unified-plan mode, the a=ssrc lines with the msid attribute are not present\n // in the answers that Chrome and Safari generate for an offer received from Jicofo.\n // Generate these a=ssrc lines using the msid values from the a=msid line.\n if (this.options.usesUnifiedPlan && !primarySsrcMsid) {\n primarySsrcMsid = mLine.msid;\n var primarySsrcs = mLine.ssrcs;\n primarySsrcs.forEach(ssrc => {\n mLine.ssrcs.push({\n id: ssrc.id,\n attribute: \"msid\",\n value: primarySsrcMsid\n });\n });\n }\n\n // Generate sim layers\n var simSsrcs = [];\n for (var i = 0; i < this.options.numOfLayers - 1; ++i) {\n var simSsrc = generateSSRC();\n addAssociatedStream(mLine, simSsrc);\n simSsrcs.push(simSsrc);\n }\n mLine.ssrcGroups = mLine.ssrcGroups || [];\n mLine.ssrcGroups.push({\n semantics: \"SIM\",\n ssrcs: primarySsrc + \" \" + simSsrcs.join(\" \")\n });\n return mLine;\n}\n\n\n\n// Assumptions:\n// 1) 'mLine' contains only a single primary video source\n// (i.e. it will not already have simulcast streams inserted)\n// 2) 'mLine' MAY already contain an RTX stream for its video source\n// 3) 'mLine' is in sendrecv or sendonly state\n// Guarantees:\n// 1) return mLine will contain 2 additional simulcast layers\n// generated\n// 2) if the base video ssrc in mLine has been seen before,\n// then the same generated simulcast streams from before will\n// be used again\n// 3) if rtx is enabled for the mLine, all generated simulcast\n// streams will have rtx streams generated as well\n// 4) if rtx has been generated for a src before, we will generate\n// the same rtx stream again\nSimulcast.prototype._restoreSimulcast = function(mLine) {\n // First, find the primary video source in the given\n // mLine and see if we've seen it before.\n var primarySsrc;\n var numSsrcs = mLine.ssrcs && mLine.ssrcs\n .map(function(ssrcInfo) { return ssrcInfo.id; })\n .filter(function(ssrc, index, array) {\n return array.indexOf(ssrc) === index;\n })\n .length || 0;\n var numGroups = (mLine.ssrcGroups && mLine.ssrcGroups.length) || 0;\n\n if (numSsrcs === 0 || numSsrcs > 2) {\n // Unsupported scenario\n return mLine;\n }\n if (numSsrcs == 2 && numGroups === 0) {\n // Unsupported scenario\n return mLine;\n }\n\n if (numSsrcs === 1) {\n primarySsrc = mLine.ssrcs[0].id;\n } else {\n // There must be an FID group, so parse\n // that and pull the primary ssrc from there\n var fidGroup = mLine.ssrcGroups.filter(function(group) { return group.semantics === \"FID\"; })[0];\n if (fidGroup) {\n primarySsrc = parseInt(fidGroup.ssrcs.split(\" \")[0]);\n } else {\n // Unsupported scenario\n return mLine;\n }\n }\n console.log(\"SdpSimulcast: current ssrc cache: \", this.ssrcCache);\n console.log(\"SdpSimulcast: parsed primary ssrc \" + primarySsrc);\n\n var seenPrimarySsrc = this.ssrcCache.indexOf(primarySsrc) !== -1;\n\n if (seenPrimarySsrc) {\n console.log(\"SdpSimulcast: Have seen primary ssrc before, \" +\n \"filling in data from cache\");\n mLine = this._fillInSourceDataFromCache(mLine);\n } else {\n console.log(\"SdpSimulcast: Have not seen primary ssrc before, \" +\n \"generating source data\");\n mLine = this._generateSourceData(mLine, primarySsrc);\n }\n // Now update the cache to match whatever we've just put into this sdp\n this.ssrcCache = this._parseSimLayers(mLine);\n return mLine;\n}\n\n//endregion\n\n//region \"Public\" functions\n\n/**\n *\n * @param desc\n * @param enableConferenceFlag\n * @returns {RTCSessionDescription}\n */\nSimulcast.prototype.mungeRemoteDescription = function (desc, enableConferenceFlag) {\n\n if (!validateDescription(desc)) {\n return desc;\n }\n\n var session = transform.parse(desc.sdp);\n\n var self = this;\n processVideo(session, function (mLine) {\n\n // Handle simulcast reception.\n if (self.options.explodeRemoteSimulcast) {\n explodeRemoteSimulcast(mLine);\n } else {\n implodeRemoteSimulcast(mLine);\n }\n\n // Add or remove \"x-google-conference\" from the remote description based on whether the client\n // has enabled simulcast for the local video source. For cases where we disable simulcast for desktop share,\n // it is necessary to remove the flag so that Chrome stops sending T1 temporal layers. It also fixes other\n // issues related to screensharing like https://bugs.chromium.org/p/chromium/issues/detail?id=1093819.\n if (!self.options.usesUnifiedPlan && enableConferenceFlag) {\n assertGoogConference(mLine);\n } else {\n removeGoogConference(mLine);\n }\n });\n\n return new RTCSessionDescription({\n type: desc.type,\n sdp: transform.write(session)\n });\n};\n\n/**\n *\n * NOTE this method should be called only if simulcast is supported by\n * the current browser, otherwise local SDP should not be munged.\n * @param desc\n * @returns {RTCSessionDescription}\n */\nSimulcast.prototype.mungeLocalDescription = function (desc) {\n\n if (!validateDescription(desc)) {\n return desc;\n }\n\n var session = transform.parse(desc.sdp);\n\n var self = this;\n processVideo(session, function (mLine) {\n if (mLine.direction == 'recvonly' || mLine.direction == 'inactive')\n {\n return;\n }\n self._restoreSimulcast(mLine);\n });\n\n return new RTCSessionDescription({\n type: desc.type,\n sdp: transform.write(session)\n });\n};\n\n//endregion\n\nmodule.exports = Simulcast;\n","/* Copyright @ 2015 Atlassian Pty Ltd\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * FIXME\n * @param sources FIXME\n * @param order An array of SSRCs which will be used to order the entries in\n * the returned array. Sources whose SSRC appears in 'order' will be added first,\n * in the specified order, and all other sources will be added afterwards (in\n * no specific order).\n * @returns {Array} FIXME\n */\nexports.writeSsrcs = function(sources, order) {\n var ssrcs = [];\n\n // expand sources to ssrcs\n if (typeof sources !== 'undefined' &&\n Object.keys(sources).length !== 0) {\n\n if (!Array.isArray(order)) {\n order = []\n }\n\n // Add the sources that appear in 'order' first.\n for (var i = 0; i < order.length; i++) {\n var ssrc = order[i];\n var source = sources[ssrc];\n Object.keys(source).forEach(function (attribute) {\n ssrcs.push({\n id: ssrc,\n attribute: attribute,\n value: source[attribute]\n });\n });\n }\n\n // Now add the rest of the sources.\n Object.keys(sources).forEach(function (ssrc) {\n ssrc = parseInt(ssrc); // Object.keys() returns string\n if (order.indexOf(ssrc) >= 0) {\n // Already added.\n return;\n }\n\n var source = sources[ssrc];\n Object.keys(source).forEach(function (attribute) {\n ssrcs.push({\n id: ssrc,\n attribute: attribute,\n value: source[attribute]\n });\n });\n });\n }\n\n return ssrcs;\n};\n\nexports.parseSsrcs = function (mLine) {\n var sources = {};\n // group sources attributes by ssrc.\n if (typeof mLine.ssrcs !== 'undefined' && Array.isArray(mLine.ssrcs)) {\n mLine.ssrcs.forEach(function (ssrc) {\n if (!sources[ssrc.id])\n sources[ssrc.id] = {};\n sources[ssrc.id][ssrc.attribute] = ssrc.value;\n });\n }\n return sources;\n};\n\n","/*!\n * async\n * https://github.com/caolan/async\n *\n * Copyright 2010-2014 Caolan McMahon\n * Released under the MIT license\n */\n/*jshint onevar: false, indent:4 */\n/*global setImmediate: false, setTimeout: false, console: false */\n(function () {\n\n var async = {};\n\n // global on the server, window in the browser\n var root, previous_async;\n\n root = this;\n if (root != null) {\n previous_async = root.async;\n }\n\n async.noConflict = function () {\n root.async = previous_async;\n return async;\n };\n\n function only_once(fn) {\n var called = false;\n return function() {\n if (called) throw new Error(\"Callback was already called.\");\n called = true;\n fn.apply(root, arguments);\n }\n }\n\n //// cross-browser compatiblity functions ////\n\n var _toString = Object.prototype.toString;\n\n var _isArray = Array.isArray || function (obj) {\n return _toString.call(obj) === '[object Array]';\n };\n\n var _each = function (arr, iterator) {\n if (arr.forEach) {\n return arr.forEach(iterator);\n }\n for (var i = 0; i < arr.length; i += 1) {\n iterator(arr[i], i, arr);\n }\n };\n\n var _map = function (arr, iterator) {\n if (arr.map) {\n return arr.map(iterator);\n }\n var results = [];\n _each(arr, function (x, i, a) {\n results.push(iterator(x, i, a));\n });\n return results;\n };\n\n var _reduce = function (arr, iterator, memo) {\n if (arr.reduce) {\n return arr.reduce(iterator, memo);\n }\n _each(arr, function (x, i, a) {\n memo = iterator(memo, x, i, a);\n });\n return memo;\n };\n\n var _keys = function (obj) {\n if (Object.keys) {\n return Object.keys(obj);\n }\n var keys = [];\n for (var k in obj) {\n if (obj.hasOwnProperty(k)) {\n keys.push(k);\n }\n }\n return keys;\n };\n\n //// exported async module functions ////\n\n //// nextTick implementation with browser-compatible fallback ////\n if (typeof process === 'undefined' || !(process.nextTick)) {\n if (typeof setImmediate === 'function') {\n async.nextTick = function (fn) {\n // not a direct alias for IE10 compatibility\n setImmediate(fn);\n };\n async.setImmediate = async.nextTick;\n }\n else {\n async.nextTick = function (fn) {\n setTimeout(fn, 0);\n };\n async.setImmediate = async.nextTick;\n }\n }\n else {\n async.nextTick = process.nextTick;\n if (typeof setImmediate !== 'undefined') {\n async.setImmediate = function (fn) {\n // not a direct alias for IE10 compatibility\n setImmediate(fn);\n };\n }\n else {\n async.setImmediate = async.nextTick;\n }\n }\n\n async.each = function (arr, iterator, callback) {\n callback = callback || function () {};\n if (!arr.length) {\n return callback();\n }\n var completed = 0;\n _each(arr, function (x) {\n iterator(x, only_once(done) );\n });\n function done(err) {\n if (err) {\n callback(err);\n callback = function () {};\n }\n else {\n completed += 1;\n if (completed >= arr.length) {\n callback();\n }\n }\n }\n };\n async.forEach = async.each;\n\n async.eachSeries = function (arr, iterator, callback) {\n callback = callback || function () {};\n if (!arr.length) {\n return callback();\n }\n var completed = 0;\n var iterate = function () {\n iterator(arr[completed], function (err) {\n if (err) {\n callback(err);\n callback = function () {};\n }\n else {\n completed += 1;\n if (completed >= arr.length) {\n callback();\n }\n else {\n iterate();\n }\n }\n });\n };\n iterate();\n };\n async.forEachSeries = async.eachSeries;\n\n async.eachLimit = function (arr, limit, iterator, callback) {\n var fn = _eachLimit(limit);\n fn.apply(null, [arr, iterator, callback]);\n };\n async.forEachLimit = async.eachLimit;\n\n var _eachLimit = function (limit) {\n\n return function (arr, iterator, callback) {\n callback = callback || function () {};\n if (!arr.length || limit <= 0) {\n return callback();\n }\n var completed = 0;\n var started = 0;\n var running = 0;\n\n (function replenish () {\n if (completed >= arr.length) {\n return callback();\n }\n\n while (running < limit && started < arr.length) {\n started += 1;\n running += 1;\n iterator(arr[started - 1], function (err) {\n if (err) {\n callback(err);\n callback = function () {};\n }\n else {\n completed += 1;\n running -= 1;\n if (completed >= arr.length) {\n callback();\n }\n else {\n replenish();\n }\n }\n });\n }\n })();\n };\n };\n\n\n var doParallel = function (fn) {\n return function () {\n var args = Array.prototype.slice.call(arguments);\n return fn.apply(null, [async.each].concat(args));\n };\n };\n var doParallelLimit = function(limit, fn) {\n return function () {\n var args = Array.prototype.slice.call(arguments);\n return fn.apply(null, [_eachLimit(limit)].concat(args));\n };\n };\n var doSeries = function (fn) {\n return function () {\n var args = Array.prototype.slice.call(arguments);\n return fn.apply(null, [async.eachSeries].concat(args));\n };\n };\n\n\n var _asyncMap = function (eachfn, arr, iterator, callback) {\n arr = _map(arr, function (x, i) {\n return {index: i, value: x};\n });\n if (!callback) {\n eachfn(arr, function (x, callback) {\n iterator(x.value, function (err) {\n callback(err);\n });\n });\n } else {\n var results = [];\n eachfn(arr, function (x, callback) {\n iterator(x.value, function (err, v) {\n results[x.index] = v;\n callback(err);\n });\n }, function (err) {\n callback(err, results);\n });\n }\n };\n async.map = doParallel(_asyncMap);\n async.mapSeries = doSeries(_asyncMap);\n async.mapLimit = function (arr, limit, iterator, callback) {\n return _mapLimit(limit)(arr, iterator, callback);\n };\n\n var _mapLimit = function(limit) {\n return doParallelLimit(limit, _asyncMap);\n };\n\n // reduce only has a series version, as doing reduce in parallel won't\n // work in many situations.\n async.reduce = function (arr, memo, iterator, callback) {\n async.eachSeries(arr, function (x, callback) {\n iterator(memo, x, function (err, v) {\n memo = v;\n callback(err);\n });\n }, function (err) {\n callback(err, memo);\n });\n };\n // inject alias\n async.inject = async.reduce;\n // foldl alias\n async.foldl = async.reduce;\n\n async.reduceRight = function (arr, memo, iterator, callback) {\n var reversed = _map(arr, function (x) {\n return x;\n }).reverse();\n async.reduce(reversed, memo, iterator, callback);\n };\n // foldr alias\n async.foldr = async.reduceRight;\n\n var _filter = function (eachfn, arr, iterator, callback) {\n var results = [];\n arr = _map(arr, function (x, i) {\n return {index: i, value: x};\n });\n eachfn(arr, function (x, callback) {\n iterator(x.value, function (v) {\n if (v) {\n results.push(x);\n }\n callback();\n });\n }, function (err) {\n callback(_map(results.sort(function (a, b) {\n return a.index - b.index;\n }), function (x) {\n return x.value;\n }));\n });\n };\n async.filter = doParallel(_filter);\n async.filterSeries = doSeries(_filter);\n // select alias\n async.select = async.filter;\n async.selectSeries = async.filterSeries;\n\n var _reject = function (eachfn, arr, iterator, callback) {\n var results = [];\n arr = _map(arr, function (x, i) {\n return {index: i, value: x};\n });\n eachfn(arr, function (x, callback) {\n iterator(x.value, function (v) {\n if (!v) {\n results.push(x);\n }\n callback();\n });\n }, function (err) {\n callback(_map(results.sort(function (a, b) {\n return a.index - b.index;\n }), function (x) {\n return x.value;\n }));\n });\n };\n async.reject = doParallel(_reject);\n async.rejectSeries = doSeries(_reject);\n\n var _detect = function (eachfn, arr, iterator, main_callback) {\n eachfn(arr, function (x, callback) {\n iterator(x, function (result) {\n if (result) {\n main_callback(x);\n main_callback = function () {};\n }\n else {\n callback();\n }\n });\n }, function (err) {\n main_callback();\n });\n };\n async.detect = doParallel(_detect);\n async.detectSeries = doSeries(_detect);\n\n async.some = function (arr, iterator, main_callback) {\n async.each(arr, function (x, callback) {\n iterator(x, function (v) {\n if (v) {\n main_callback(true);\n main_callback = function () {};\n }\n callback();\n });\n }, function (err) {\n main_callback(false);\n });\n };\n // any alias\n async.any = async.some;\n\n async.every = function (arr, iterator, main_callback) {\n async.each(arr, function (x, callback) {\n iterator(x, function (v) {\n if (!v) {\n main_callback(false);\n main_callback = function () {};\n }\n callback();\n });\n }, function (err) {\n main_callback(true);\n });\n };\n // all alias\n async.all = async.every;\n\n async.sortBy = function (arr, iterator, callback) {\n async.map(arr, function (x, callback) {\n iterator(x, function (err, criteria) {\n if (err) {\n callback(err);\n }\n else {\n callback(null, {value: x, criteria: criteria});\n }\n });\n }, function (err, results) {\n if (err) {\n return callback(err);\n }\n else {\n var fn = function (left, right) {\n var a = left.criteria, b = right.criteria;\n return a < b ? -1 : a > b ? 1 : 0;\n };\n callback(null, _map(results.sort(fn), function (x) {\n return x.value;\n }));\n }\n });\n };\n\n async.auto = function (tasks, callback) {\n callback = callback || function () {};\n var keys = _keys(tasks);\n var remainingTasks = keys.length\n if (!remainingTasks) {\n return callback();\n }\n\n var results = {};\n\n var listeners = [];\n var addListener = function (fn) {\n listeners.unshift(fn);\n };\n var removeListener = function (fn) {\n for (var i = 0; i < listeners.length; i += 1) {\n if (listeners[i] === fn) {\n listeners.splice(i, 1);\n return;\n }\n }\n };\n var taskComplete = function () {\n remainingTasks--\n _each(listeners.slice(0), function (fn) {\n fn();\n });\n };\n\n addListener(function () {\n if (!remainingTasks) {\n var theCallback = callback;\n // prevent final callback from calling itself if it errors\n callback = function () {};\n\n theCallback(null, results);\n }\n });\n\n _each(keys, function (k) {\n var task = _isArray(tasks[k]) ? tasks[k]: [tasks[k]];\n var taskCallback = function (err) {\n var args = Array.prototype.slice.call(arguments, 1);\n if (args.length <= 1) {\n args = args[0];\n }\n if (err) {\n var safeResults = {};\n _each(_keys(results), function(rkey) {\n safeResults[rkey] = results[rkey];\n });\n safeResults[k] = args;\n callback(err, safeResults);\n // stop subsequent errors hitting callback multiple times\n callback = function () {};\n }\n else {\n results[k] = args;\n async.setImmediate(taskComplete);\n }\n };\n var requires = task.slice(0, Math.abs(task.length - 1)) || [];\n var ready = function () {\n return _reduce(requires, function (a, x) {\n return (a && results.hasOwnProperty(x));\n }, true) && !results.hasOwnProperty(k);\n };\n if (ready()) {\n task[task.length - 1](taskCallback, results);\n }\n else {\n var listener = function () {\n if (ready()) {\n removeListener(listener);\n task[task.length - 1](taskCallback, results);\n }\n };\n addListener(listener);\n }\n });\n };\n\n async.retry = function(times, task, callback) {\n var DEFAULT_TIMES = 5;\n var attempts = [];\n // Use defaults if times not passed\n if (typeof times === 'function') {\n callback = task;\n task = times;\n times = DEFAULT_TIMES;\n }\n // Make sure times is a number\n times = parseInt(times, 10) || DEFAULT_TIMES;\n var wrappedTask = function(wrappedCallback, wrappedResults) {\n var retryAttempt = function(task, finalAttempt) {\n return function(seriesCallback) {\n task(function(err, result){\n seriesCallback(!err || finalAttempt, {err: err, result: result});\n }, wrappedResults);\n };\n };\n while (times) {\n attempts.push(retryAttempt(task, !(times-=1)));\n }\n async.series(attempts, function(done, data){\n data = data[data.length - 1];\n (wrappedCallback || callback)(data.err, data.result);\n });\n }\n // If a callback is passed, run this as a controll flow\n return callback ? wrappedTask() : wrappedTask\n };\n\n async.waterfall = function (tasks, callback) {\n callback = callback || function () {};\n if (!_isArray(tasks)) {\n var err = new Error('First argument to waterfall must be an array of functions');\n return callback(err);\n }\n if (!tasks.length) {\n return callback();\n }\n var wrapIterator = function (iterator) {\n return function (err) {\n if (err) {\n callback.apply(null, arguments);\n callback = function () {};\n }\n else {\n var args = Array.prototype.slice.call(arguments, 1);\n var next = iterator.next();\n if (next) {\n args.push(wrapIterator(next));\n }\n else {\n args.push(callback);\n }\n async.setImmediate(function () {\n iterator.apply(null, args);\n });\n }\n };\n };\n wrapIterator(async.iterator(tasks))();\n };\n\n var _parallel = function(eachfn, tasks, callback) {\n callback = callback || function () {};\n if (_isArray(tasks)) {\n eachfn.map(tasks, function (fn, callback) {\n if (fn) {\n fn(function (err) {\n var args = Array.prototype.slice.call(arguments, 1);\n if (args.length <= 1) {\n args = args[0];\n }\n callback.call(null, err, args);\n });\n }\n }, callback);\n }\n else {\n var results = {};\n eachfn.each(_keys(tasks), function (k, callback) {\n tasks[k](function (err) {\n var args = Array.prototype.slice.call(arguments, 1);\n if (args.length <= 1) {\n args = args[0];\n }\n results[k] = args;\n callback(err);\n });\n }, function (err) {\n callback(err, results);\n });\n }\n };\n\n async.parallel = function (tasks, callback) {\n _parallel({ map: async.map, each: async.each }, tasks, callback);\n };\n\n async.parallelLimit = function(tasks, limit, callback) {\n _parallel({ map: _mapLimit(limit), each: _eachLimit(limit) }, tasks, callback);\n };\n\n async.series = function (tasks, callback) {\n callback = callback || function () {};\n if (_isArray(tasks)) {\n async.mapSeries(tasks, function (fn, callback) {\n if (fn) {\n fn(function (err) {\n var args = Array.prototype.slice.call(arguments, 1);\n if (args.length <= 1) {\n args = args[0];\n }\n callback.call(null, err, args);\n });\n }\n }, callback);\n }\n else {\n var results = {};\n async.eachSeries(_keys(tasks), function (k, callback) {\n tasks[k](function (err) {\n var args = Array.prototype.slice.call(arguments, 1);\n if (args.length <= 1) {\n args = args[0];\n }\n results[k] = args;\n callback(err);\n });\n }, function (err) {\n callback(err, results);\n });\n }\n };\n\n async.iterator = function (tasks) {\n var makeCallback = function (index) {\n var fn = function () {\n if (tasks.length) {\n tasks[index].apply(null, arguments);\n }\n return fn.next();\n };\n fn.next = function () {\n return (index < tasks.length - 1) ? makeCallback(index + 1): null;\n };\n return fn;\n };\n return makeCallback(0);\n };\n\n async.apply = function (fn) {\n var args = Array.prototype.slice.call(arguments, 1);\n return function () {\n return fn.apply(\n null, args.concat(Array.prototype.slice.call(arguments))\n );\n };\n };\n\n var _concat = function (eachfn, arr, fn, callback) {\n var r = [];\n eachfn(arr, function (x, cb) {\n fn(x, function (err, y) {\n r = r.concat(y || []);\n cb(err);\n });\n }, function (err) {\n callback(err, r);\n });\n };\n async.concat = doParallel(_concat);\n async.concatSeries = doSeries(_concat);\n\n async.whilst = function (test, iterator, callback) {\n if (test()) {\n iterator(function (err) {\n if (err) {\n return callback(err);\n }\n async.whilst(test, iterator, callback);\n });\n }\n else {\n callback();\n }\n };\n\n async.doWhilst = function (iterator, test, callback) {\n iterator(function (err) {\n if (err) {\n return callback(err);\n }\n var args = Array.prototype.slice.call(arguments, 1);\n if (test.apply(null, args)) {\n async.doWhilst(iterator, test, callback);\n }\n else {\n callback();\n }\n });\n };\n\n async.until = function (test, iterator, callback) {\n if (!test()) {\n iterator(function (err) {\n if (err) {\n return callback(err);\n }\n async.until(test, iterator, callback);\n });\n }\n else {\n callback();\n }\n };\n\n async.doUntil = function (iterator, test, callback) {\n iterator(function (err) {\n if (err) {\n return callback(err);\n }\n var args = Array.prototype.slice.call(arguments, 1);\n if (!test.apply(null, args)) {\n async.doUntil(iterator, test, callback);\n }\n else {\n callback();\n }\n });\n };\n\n async.queue = function (worker, concurrency) {\n if (concurrency === undefined) {\n concurrency = 1;\n }\n function _insert(q, data, pos, callback) {\n if (!q.started){\n q.started = true;\n }\n if (!_isArray(data)) {\n data = [data];\n }\n if(data.length == 0) {\n // call drain immediately if there are no tasks\n return async.setImmediate(function() {\n if (q.drain) {\n q.drain();\n }\n });\n }\n _each(data, function(task) {\n var item = {\n data: task,\n callback: typeof callback === 'function' ? callback : null\n };\n\n if (pos) {\n q.tasks.unshift(item);\n } else {\n q.tasks.push(item);\n }\n\n if (q.saturated && q.tasks.length === q.concurrency) {\n q.saturated();\n }\n async.setImmediate(q.process);\n });\n }\n\n var workers = 0;\n var q = {\n tasks: [],\n concurrency: concurrency,\n saturated: null,\n empty: null,\n drain: null,\n started: false,\n paused: false,\n push: function (data, callback) {\n _insert(q, data, false, callback);\n },\n kill: function () {\n q.drain = null;\n q.tasks = [];\n },\n unshift: function (data, callback) {\n _insert(q, data, true, callback);\n },\n process: function () {\n if (!q.paused && workers < q.concurrency && q.tasks.length) {\n var task = q.tasks.shift();\n if (q.empty && q.tasks.length === 0) {\n q.empty();\n }\n workers += 1;\n var next = function () {\n workers -= 1;\n if (task.callback) {\n task.callback.apply(task, arguments);\n }\n if (q.drain && q.tasks.length + workers === 0) {\n q.drain();\n }\n q.process();\n };\n var cb = only_once(next);\n worker(task.data, cb);\n }\n },\n length: function () {\n return q.tasks.length;\n },\n running: function () {\n return workers;\n },\n idle: function() {\n return q.tasks.length + workers === 0;\n },\n pause: function () {\n if (q.paused === true) { return; }\n q.paused = true;\n q.process();\n },\n resume: function () {\n if (q.paused === false) { return; }\n q.paused = false;\n q.process();\n }\n };\n return q;\n };\n \n async.priorityQueue = function (worker, concurrency) {\n \n function _compareTasks(a, b){\n return a.priority - b.priority;\n };\n \n function _binarySearch(sequence, item, compare) {\n var beg = -1,\n end = sequence.length - 1;\n while (beg < end) {\n var mid = beg + ((end - beg + 1) >>> 1);\n if (compare(item, sequence[mid]) >= 0) {\n beg = mid;\n } else {\n end = mid - 1;\n }\n }\n return beg;\n }\n \n function _insert(q, data, priority, callback) {\n if (!q.started){\n q.started = true;\n }\n if (!_isArray(data)) {\n data = [data];\n }\n if(data.length == 0) {\n // call drain immediately if there are no tasks\n return async.setImmediate(function() {\n if (q.drain) {\n q.drain();\n }\n });\n }\n _each(data, function(task) {\n var item = {\n data: task,\n priority: priority,\n callback: typeof callback === 'function' ? callback : null\n };\n \n q.tasks.splice(_binarySearch(q.tasks, item, _compareTasks) + 1, 0, item);\n\n if (q.saturated && q.tasks.length === q.concurrency) {\n q.saturated();\n }\n async.setImmediate(q.process);\n });\n }\n \n // Start with a normal queue\n var q = async.queue(worker, concurrency);\n \n // Override push to accept second parameter representing priority\n q.push = function (data, priority, callback) {\n _insert(q, data, priority, callback);\n };\n \n // Remove unshift function\n delete q.unshift;\n\n return q;\n };\n\n async.cargo = function (worker, payload) {\n var working = false,\n tasks = [];\n\n var cargo = {\n tasks: tasks,\n payload: payload,\n saturated: null,\n empty: null,\n drain: null,\n drained: true,\n push: function (data, callback) {\n if (!_isArray(data)) {\n data = [data];\n }\n _each(data, function(task) {\n tasks.push({\n data: task,\n callback: typeof callback === 'function' ? callback : null\n });\n cargo.drained = false;\n if (cargo.saturated && tasks.length === payload) {\n cargo.saturated();\n }\n });\n async.setImmediate(cargo.process);\n },\n process: function process() {\n if (working) return;\n if (tasks.length === 0) {\n if(cargo.drain && !cargo.drained) cargo.drain();\n cargo.drained = true;\n return;\n }\n\n var ts = typeof payload === 'number'\n ? tasks.splice(0, payload)\n : tasks.splice(0, tasks.length);\n\n var ds = _map(ts, function (task) {\n return task.data;\n });\n\n if(cargo.empty) cargo.empty();\n working = true;\n worker(ds, function () {\n working = false;\n\n var args = arguments;\n _each(ts, function (data) {\n if (data.callback) {\n data.callback.apply(null, args);\n }\n });\n\n process();\n });\n },\n length: function () {\n return tasks.length;\n },\n running: function () {\n return working;\n }\n };\n return cargo;\n };\n\n var _console_fn = function (name) {\n return function (fn) {\n var args = Array.prototype.slice.call(arguments, 1);\n fn.apply(null, args.concat([function (err) {\n var args = Array.prototype.slice.call(arguments, 1);\n if (typeof console !== 'undefined') {\n if (err) {\n if (console.error) {\n console.error(err);\n }\n }\n else if (console[name]) {\n _each(args, function (x) {\n console[name](x);\n });\n }\n }\n }]));\n };\n };\n async.log = _console_fn('log');\n async.dir = _console_fn('dir');\n /*async.info = _console_fn('info');\n async.warn = _console_fn('warn');\n async.error = _console_fn('error');*/\n\n async.memoize = function (fn, hasher) {\n var memo = {};\n var queues = {};\n hasher = hasher || function (x) {\n return x;\n };\n var memoized = function () {\n var args = Array.prototype.slice.call(arguments);\n var callback = args.pop();\n var key = hasher.apply(null, args);\n if (key in memo) {\n async.nextTick(function () {\n callback.apply(null, memo[key]);\n });\n }\n else if (key in queues) {\n queues[key].push(callback);\n }\n else {\n queues[key] = [callback];\n fn.apply(null, args.concat([function () {\n memo[key] = arguments;\n var q = queues[key];\n delete queues[key];\n for (var i = 0, l = q.length; i < l; i++) {\n q[i].apply(null, arguments);\n }\n }]));\n }\n };\n memoized.memo = memo;\n memoized.unmemoized = fn;\n return memoized;\n };\n\n async.unmemoize = function (fn) {\n return function () {\n return (fn.unmemoized || fn).apply(null, arguments);\n };\n };\n\n async.times = function (count, iterator, callback) {\n var counter = [];\n for (var i = 0; i < count; i++) {\n counter.push(i);\n }\n return async.map(counter, iterator, callback);\n };\n\n async.timesSeries = function (count, iterator, callback) {\n var counter = [];\n for (var i = 0; i < count; i++) {\n counter.push(i);\n }\n return async.mapSeries(counter, iterator, callback);\n };\n\n async.seq = function (/* functions... */) {\n var fns = arguments;\n return function () {\n var that = this;\n var args = Array.prototype.slice.call(arguments);\n var callback = args.pop();\n async.reduce(fns, args, function (newargs, fn, cb) {\n fn.apply(that, newargs.concat([function () {\n var err = arguments[0];\n var nextargs = Array.prototype.slice.call(arguments, 1);\n cb(err, nextargs);\n }]))\n },\n function (err, results) {\n callback.apply(that, [err].concat(results));\n });\n };\n };\n\n async.compose = function (/* functions... */) {\n return async.seq.apply(null, Array.prototype.reverse.call(arguments));\n };\n\n var _applyEach = function (eachfn, fns /*args...*/) {\n var go = function () {\n var that = this;\n var args = Array.prototype.slice.call(arguments);\n var callback = args.pop();\n return eachfn(fns, function (fn, cb) {\n fn.apply(that, args.concat([cb]));\n },\n callback);\n };\n if (arguments.length > 2) {\n var args = Array.prototype.slice.call(arguments, 2);\n return go.apply(this, args);\n }\n else {\n return go;\n }\n };\n async.applyEach = doParallel(_applyEach);\n async.applyEachSeries = doSeries(_applyEach);\n\n async.forever = function (fn, callback) {\n function next(err) {\n if (err) {\n if (callback) {\n return callback(err);\n }\n throw err;\n }\n fn(next);\n }\n next();\n };\n\n // Node.js\n if (typeof module !== 'undefined' && module.exports) {\n module.exports = async;\n }\n // AMD / RequireJS\n else if (typeof define !== 'undefined' && define.amd) {\n define([], function () {\n return async;\n });\n }\n // included directly via \n // \n if (hasNativeCurrentScriptAccessor) {\n script = document.currentScript;\n }\n }\n\n if (!script) {\n // Inaccuracies:\n // - If script currently being synchronously evaluated by the parser is the\n // originator of this call stack but NOT the source script of the caller/invocation\n // e.g.\n // ```html\n // \n // \n if (isNotOpera && supportsScriptReadyState) {\n for (i = eligibleScripts.length; i--; ) {\n if (eligibleScripts[i].readyState === \"interactive\") {\n script = eligibleScripts[i];\n break;\n }\n }\n }\n }\n\n if (!script) {\n // Inaccuracies:\n // - If a script is created dynamically and appended to some position\n // other than the very end of the document.\n // - If multiple scripts are created dynamically and all appended to the\n // same position within the document (and do not have their `async` attributes\n // set to `false`, at least in browsers that support async script evaluation.\n // other than the very end of the document.\n // - If any scripts are added with the `async` attribute set to `true` in a browser\n // that supports it.\n // - May get confused by `script` elements within `svg` elements\n // - If script currently being synchronously evaluated by the parser is the\n // originator of this call stack but NOT the source script of the caller/invocation\n // e.g.\n // ```html\n // \n // \n // ```\n script = eligibleScripts[eligibleScripts.length - 1] || null;\n }\n\n return script;\n}\n\n// Default stack depth to skip over when analyzing call stack frames\n_nearestExecutingScript.skipStackDepth = 1;\n\n\n\n //\n // Export the API\n //\n var currentExecutingScript = _nearestExecutingScript; // default\n currentExecutingScript.near = _nearestExecutingScript;\n currentExecutingScript.far = _farthestExecutingScript;\n currentExecutingScript.origin = _originatingExecutingScript;\n\n\n // Just return a value to define the module export.\n // This example returns an object, but the module\n // can return a function as the exported value.\n return currentExecutingScript;\n })\n);\n","// Copyright Joyent, Inc. and other Node contributors.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a\n// copy of this software and associated documentation files (the\n// \"Software\"), to deal in the Software without restriction, including\n// without limitation the rights to use, copy, modify, merge, publish,\n// distribute, sublicense, and/or sell copies of the Software, and to permit\n// persons to whom the Software is furnished to do so, subject to the\n// following conditions:\n//\n// The above copyright notice and this permission notice shall be included\n// in all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS\n// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN\n// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,\n// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR\n// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE\n// USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n'use strict';\n\nvar R = typeof Reflect === 'object' ? Reflect : null\nvar ReflectApply = R && typeof R.apply === 'function'\n ? R.apply\n : function ReflectApply(target, receiver, args) {\n return Function.prototype.apply.call(target, receiver, args);\n }\n\nvar ReflectOwnKeys\nif (R && typeof R.ownKeys === 'function') {\n ReflectOwnKeys = R.ownKeys\n} else if (Object.getOwnPropertySymbols) {\n ReflectOwnKeys = function ReflectOwnKeys(target) {\n return Object.getOwnPropertyNames(target)\n .concat(Object.getOwnPropertySymbols(target));\n };\n} else {\n ReflectOwnKeys = function ReflectOwnKeys(target) {\n return Object.getOwnPropertyNames(target);\n };\n}\n\nfunction ProcessEmitWarning(warning) {\n if (console && console.warn) console.warn(warning);\n}\n\nvar NumberIsNaN = Number.isNaN || function NumberIsNaN(value) {\n return value !== value;\n}\n\nfunction EventEmitter() {\n EventEmitter.init.call(this);\n}\nmodule.exports = EventEmitter;\nmodule.exports.once = once;\n\n// Backwards-compat with node 0.10.x\nEventEmitter.EventEmitter = EventEmitter;\n\nEventEmitter.prototype._events = undefined;\nEventEmitter.prototype._eventsCount = 0;\nEventEmitter.prototype._maxListeners = undefined;\n\n// By default EventEmitters will print a warning if more than 10 listeners are\n// added to it. This is a useful default which helps finding memory leaks.\nvar defaultMaxListeners = 10;\n\nfunction checkListener(listener) {\n if (typeof listener !== 'function') {\n throw new TypeError('The \"listener\" argument must be of type Function. Received type ' + typeof listener);\n }\n}\n\nObject.defineProperty(EventEmitter, 'defaultMaxListeners', {\n enumerable: true,\n get: function() {\n return defaultMaxListeners;\n },\n set: function(arg) {\n if (typeof arg !== 'number' || arg < 0 || NumberIsNaN(arg)) {\n throw new RangeError('The value of \"defaultMaxListeners\" is out of range. It must be a non-negative number. Received ' + arg + '.');\n }\n defaultMaxListeners = arg;\n }\n});\n\nEventEmitter.init = function() {\n\n if (this._events === undefined ||\n this._events === Object.getPrototypeOf(this)._events) {\n this._events = Object.create(null);\n this._eventsCount = 0;\n }\n\n this._maxListeners = this._maxListeners || undefined;\n};\n\n// Obviously not all Emitters should be limited to 10. This function allows\n// that to be increased. Set to zero for unlimited.\nEventEmitter.prototype.setMaxListeners = function setMaxListeners(n) {\n if (typeof n !== 'number' || n < 0 || NumberIsNaN(n)) {\n throw new RangeError('The value of \"n\" is out of range. It must be a non-negative number. Received ' + n + '.');\n }\n this._maxListeners = n;\n return this;\n};\n\nfunction _getMaxListeners(that) {\n if (that._maxListeners === undefined)\n return EventEmitter.defaultMaxListeners;\n return that._maxListeners;\n}\n\nEventEmitter.prototype.getMaxListeners = function getMaxListeners() {\n return _getMaxListeners(this);\n};\n\nEventEmitter.prototype.emit = function emit(type) {\n var args = [];\n for (var i = 1; i < arguments.length; i++) args.push(arguments[i]);\n var doError = (type === 'error');\n\n var events = this._events;\n if (events !== undefined)\n doError = (doError && events.error === undefined);\n else if (!doError)\n return false;\n\n // If there is no 'error' event listener then throw.\n if (doError) {\n var er;\n if (args.length > 0)\n er = args[0];\n if (er instanceof Error) {\n // Note: The comments on the `throw` lines are intentional, they show\n // up in Node's output if this results in an unhandled exception.\n throw er; // Unhandled 'error' event\n }\n // At least give some kind of context to the user\n var err = new Error('Unhandled error.' + (er ? ' (' + er.message + ')' : ''));\n err.context = er;\n throw err; // Unhandled 'error' event\n }\n\n var handler = events[type];\n\n if (handler === undefined)\n return false;\n\n if (typeof handler === 'function') {\n ReflectApply(handler, this, args);\n } else {\n var len = handler.length;\n var listeners = arrayClone(handler, len);\n for (var i = 0; i < len; ++i)\n ReflectApply(listeners[i], this, args);\n }\n\n return true;\n};\n\nfunction _addListener(target, type, listener, prepend) {\n var m;\n var events;\n var existing;\n\n checkListener(listener);\n\n events = target._events;\n if (events === undefined) {\n events = target._events = Object.create(null);\n target._eventsCount = 0;\n } else {\n // To avoid recursion in the case that type === \"newListener\"! Before\n // adding it to the listeners, first emit \"newListener\".\n if (events.newListener !== undefined) {\n target.emit('newListener', type,\n listener.listener ? listener.listener : listener);\n\n // Re-assign `events` because a newListener handler could have caused the\n // this._events to be assigned to a new object\n events = target._events;\n }\n existing = events[type];\n }\n\n if (existing === undefined) {\n // Optimize the case of one listener. Don't need the extra array object.\n existing = events[type] = listener;\n ++target._eventsCount;\n } else {\n if (typeof existing === 'function') {\n // Adding the second element, need to change to array.\n existing = events[type] =\n prepend ? [listener, existing] : [existing, listener];\n // If we've already got an array, just append.\n } else if (prepend) {\n existing.unshift(listener);\n } else {\n existing.push(listener);\n }\n\n // Check for listener leak\n m = _getMaxListeners(target);\n if (m > 0 && existing.length > m && !existing.warned) {\n existing.warned = true;\n // No error code for this since it is a Warning\n // eslint-disable-next-line no-restricted-syntax\n var w = new Error('Possible EventEmitter memory leak detected. ' +\n existing.length + ' ' + String(type) + ' listeners ' +\n 'added. Use emitter.setMaxListeners() to ' +\n 'increase limit');\n w.name = 'MaxListenersExceededWarning';\n w.emitter = target;\n w.type = type;\n w.count = existing.length;\n ProcessEmitWarning(w);\n }\n }\n\n return target;\n}\n\nEventEmitter.prototype.addListener = function addListener(type, listener) {\n return _addListener(this, type, listener, false);\n};\n\nEventEmitter.prototype.on = EventEmitter.prototype.addListener;\n\nEventEmitter.prototype.prependListener =\n function prependListener(type, listener) {\n return _addListener(this, type, listener, true);\n };\n\nfunction onceWrapper() {\n if (!this.fired) {\n this.target.removeListener(this.type, this.wrapFn);\n this.fired = true;\n if (arguments.length === 0)\n return this.listener.call(this.target);\n return this.listener.apply(this.target, arguments);\n }\n}\n\nfunction _onceWrap(target, type, listener) {\n var state = { fired: false, wrapFn: undefined, target: target, type: type, listener: listener };\n var wrapped = onceWrapper.bind(state);\n wrapped.listener = listener;\n state.wrapFn = wrapped;\n return wrapped;\n}\n\nEventEmitter.prototype.once = function once(type, listener) {\n checkListener(listener);\n this.on(type, _onceWrap(this, type, listener));\n return this;\n};\n\nEventEmitter.prototype.prependOnceListener =\n function prependOnceListener(type, listener) {\n checkListener(listener);\n this.prependListener(type, _onceWrap(this, type, listener));\n return this;\n };\n\n// Emits a 'removeListener' event if and only if the listener was removed.\nEventEmitter.prototype.removeListener =\n function removeListener(type, listener) {\n var list, events, position, i, originalListener;\n\n checkListener(listener);\n\n events = this._events;\n if (events === undefined)\n return this;\n\n list = events[type];\n if (list === undefined)\n return this;\n\n if (list === listener || list.listener === listener) {\n if (--this._eventsCount === 0)\n this._events = Object.create(null);\n else {\n delete events[type];\n if (events.removeListener)\n this.emit('removeListener', type, list.listener || listener);\n }\n } else if (typeof list !== 'function') {\n position = -1;\n\n for (i = list.length - 1; i >= 0; i--) {\n if (list[i] === listener || list[i].listener === listener) {\n originalListener = list[i].listener;\n position = i;\n break;\n }\n }\n\n if (position < 0)\n return this;\n\n if (position === 0)\n list.shift();\n else {\n spliceOne(list, position);\n }\n\n if (list.length === 1)\n events[type] = list[0];\n\n if (events.removeListener !== undefined)\n this.emit('removeListener', type, originalListener || listener);\n }\n\n return this;\n };\n\nEventEmitter.prototype.off = EventEmitter.prototype.removeListener;\n\nEventEmitter.prototype.removeAllListeners =\n function removeAllListeners(type) {\n var listeners, events, i;\n\n events = this._events;\n if (events === undefined)\n return this;\n\n // not listening for removeListener, no need to emit\n if (events.removeListener === undefined) {\n if (arguments.length === 0) {\n this._events = Object.create(null);\n this._eventsCount = 0;\n } else if (events[type] !== undefined) {\n if (--this._eventsCount === 0)\n this._events = Object.create(null);\n else\n delete events[type];\n }\n return this;\n }\n\n // emit removeListener for all listeners on all events\n if (arguments.length === 0) {\n var keys = Object.keys(events);\n var key;\n for (i = 0; i < keys.length; ++i) {\n key = keys[i];\n if (key === 'removeListener') continue;\n this.removeAllListeners(key);\n }\n this.removeAllListeners('removeListener');\n this._events = Object.create(null);\n this._eventsCount = 0;\n return this;\n }\n\n listeners = events[type];\n\n if (typeof listeners === 'function') {\n this.removeListener(type, listeners);\n } else if (listeners !== undefined) {\n // LIFO order\n for (i = listeners.length - 1; i >= 0; i--) {\n this.removeListener(type, listeners[i]);\n }\n }\n\n return this;\n };\n\nfunction _listeners(target, type, unwrap) {\n var events = target._events;\n\n if (events === undefined)\n return [];\n\n var evlistener = events[type];\n if (evlistener === undefined)\n return [];\n\n if (typeof evlistener === 'function')\n return unwrap ? [evlistener.listener || evlistener] : [evlistener];\n\n return unwrap ?\n unwrapListeners(evlistener) : arrayClone(evlistener, evlistener.length);\n}\n\nEventEmitter.prototype.listeners = function listeners(type) {\n return _listeners(this, type, true);\n};\n\nEventEmitter.prototype.rawListeners = function rawListeners(type) {\n return _listeners(this, type, false);\n};\n\nEventEmitter.listenerCount = function(emitter, type) {\n if (typeof emitter.listenerCount === 'function') {\n return emitter.listenerCount(type);\n } else {\n return listenerCount.call(emitter, type);\n }\n};\n\nEventEmitter.prototype.listenerCount = listenerCount;\nfunction listenerCount(type) {\n var events = this._events;\n\n if (events !== undefined) {\n var evlistener = events[type];\n\n if (typeof evlistener === 'function') {\n return 1;\n } else if (evlistener !== undefined) {\n return evlistener.length;\n }\n }\n\n return 0;\n}\n\nEventEmitter.prototype.eventNames = function eventNames() {\n return this._eventsCount > 0 ? ReflectOwnKeys(this._events) : [];\n};\n\nfunction arrayClone(arr, n) {\n var copy = new Array(n);\n for (var i = 0; i < n; ++i)\n copy[i] = arr[i];\n return copy;\n}\n\nfunction spliceOne(list, index) {\n for (; index + 1 < list.length; index++)\n list[index] = list[index + 1];\n list.pop();\n}\n\nfunction unwrapListeners(arr) {\n var ret = new Array(arr.length);\n for (var i = 0; i < ret.length; ++i) {\n ret[i] = arr[i].listener || arr[i];\n }\n return ret;\n}\n\nfunction once(emitter, name) {\n return new Promise(function (resolve, reject) {\n function errorListener(err) {\n emitter.removeListener(name, resolver);\n reject(err);\n }\n\n function resolver() {\n if (typeof emitter.removeListener === 'function') {\n emitter.removeListener('error', errorListener);\n }\n resolve([].slice.call(arguments));\n };\n\n eventTargetAgnosticAddListener(emitter, name, resolver, { once: true });\n if (name !== 'error') {\n addErrorHandlerIfEventEmitter(emitter, errorListener, { once: true });\n }\n });\n}\n\nfunction addErrorHandlerIfEventEmitter(emitter, handler, flags) {\n if (typeof emitter.on === 'function') {\n eventTargetAgnosticAddListener(emitter, 'error', handler, flags);\n }\n}\n\nfunction eventTargetAgnosticAddListener(emitter, name, listener, flags) {\n if (typeof emitter.on === 'function') {\n if (flags.once) {\n emitter.once(name, listener);\n } else {\n emitter.on(name, listener);\n }\n } else if (typeof emitter.addEventListener === 'function') {\n // EventTarget does not have `error` event semantics like Node\n // EventEmitters, we do not listen for `error` events here.\n emitter.addEventListener(name, function wrapListener(arg) {\n // IE does not have builtin `{ once: true }` support so we\n // have to do it manually.\n if (flags.once) {\n emitter.removeEventListener(name, wrapListener);\n }\n listener(arg);\n });\n } else {\n throw new TypeError('The \"emitter\" argument must be of type EventEmitter. Received type ' + typeof emitter);\n }\n}\n","/**\n * [js-md5]{@link https://github.com/emn178/js-md5}\n *\n * @namespace md5\n * @version 0.7.3\n * @author Chen, Yi-Cyuan [emn178@gmail.com]\n * @copyright Chen, Yi-Cyuan 2014-2017\n * @license MIT\n */\n(function () {\n 'use strict';\n\n var ERROR = 'input is invalid type';\n var WINDOW = typeof window === 'object';\n var root = WINDOW ? window : {};\n if (root.JS_MD5_NO_WINDOW) {\n WINDOW = false;\n }\n var WEB_WORKER = !WINDOW && typeof self === 'object';\n var NODE_JS = !root.JS_MD5_NO_NODE_JS && typeof process === 'object' && process.versions && process.versions.node;\n if (NODE_JS) {\n root = global;\n } else if (WEB_WORKER) {\n root = self;\n }\n var COMMON_JS = !root.JS_MD5_NO_COMMON_JS && typeof module === 'object' && module.exports;\n var AMD = typeof define === 'function' && define.amd;\n var ARRAY_BUFFER = !root.JS_MD5_NO_ARRAY_BUFFER && typeof ArrayBuffer !== 'undefined';\n var HEX_CHARS = '0123456789abcdef'.split('');\n var EXTRA = [128, 32768, 8388608, -2147483648];\n var SHIFT = [0, 8, 16, 24];\n var OUTPUT_TYPES = ['hex', 'array', 'digest', 'buffer', 'arrayBuffer', 'base64'];\n var BASE64_ENCODE_CHAR = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'.split('');\n\n var blocks = [], buffer8;\n if (ARRAY_BUFFER) {\n var buffer = new ArrayBuffer(68);\n buffer8 = new Uint8Array(buffer);\n blocks = new Uint32Array(buffer);\n }\n\n if (root.JS_MD5_NO_NODE_JS || !Array.isArray) {\n Array.isArray = function (obj) {\n return Object.prototype.toString.call(obj) === '[object Array]';\n };\n }\n\n if (ARRAY_BUFFER && (root.JS_MD5_NO_ARRAY_BUFFER_IS_VIEW || !ArrayBuffer.isView)) {\n ArrayBuffer.isView = function (obj) {\n return typeof obj === 'object' && obj.buffer && obj.buffer.constructor === ArrayBuffer;\n };\n }\n\n /**\n * @method hex\n * @memberof md5\n * @description Output hash as hex string\n * @param {String|Array|Uint8Array|ArrayBuffer} message message to hash\n * @returns {String} Hex string\n * @example\n * md5.hex('The quick brown fox jumps over the lazy dog');\n * // equal to\n * md5('The quick brown fox jumps over the lazy dog');\n */\n /**\n * @method digest\n * @memberof md5\n * @description Output hash as bytes array\n * @param {String|Array|Uint8Array|ArrayBuffer} message message to hash\n * @returns {Array} Bytes array\n * @example\n * md5.digest('The quick brown fox jumps over the lazy dog');\n */\n /**\n * @method array\n * @memberof md5\n * @description Output hash as bytes array\n * @param {String|Array|Uint8Array|ArrayBuffer} message message to hash\n * @returns {Array} Bytes array\n * @example\n * md5.array('The quick brown fox jumps over the lazy dog');\n */\n /**\n * @method arrayBuffer\n * @memberof md5\n * @description Output hash as ArrayBuffer\n * @param {String|Array|Uint8Array|ArrayBuffer} message message to hash\n * @returns {ArrayBuffer} ArrayBuffer\n * @example\n * md5.arrayBuffer('The quick brown fox jumps over the lazy dog');\n */\n /**\n * @method buffer\n * @deprecated This maybe confuse with Buffer in node.js. Please use arrayBuffer instead.\n * @memberof md5\n * @description Output hash as ArrayBuffer\n * @param {String|Array|Uint8Array|ArrayBuffer} message message to hash\n * @returns {ArrayBuffer} ArrayBuffer\n * @example\n * md5.buffer('The quick brown fox jumps over the lazy dog');\n */\n /**\n * @method base64\n * @memberof md5\n * @description Output hash as base64 string\n * @param {String|Array|Uint8Array|ArrayBuffer} message message to hash\n * @returns {String} base64 string\n * @example\n * md5.base64('The quick brown fox jumps over the lazy dog');\n */\n var createOutputMethod = function (outputType) {\n return function (message) {\n return new Md5(true).update(message)[outputType]();\n };\n };\n\n /**\n * @method create\n * @memberof md5\n * @description Create Md5 object\n * @returns {Md5} Md5 object.\n * @example\n * var hash = md5.create();\n */\n /**\n * @method update\n * @memberof md5\n * @description Create and update Md5 object\n * @param {String|Array|Uint8Array|ArrayBuffer} message message to hash\n * @returns {Md5} Md5 object.\n * @example\n * var hash = md5.update('The quick brown fox jumps over the lazy dog');\n * // equal to\n * var hash = md5.create();\n * hash.update('The quick brown fox jumps over the lazy dog');\n */\n var createMethod = function () {\n var method = createOutputMethod('hex');\n if (NODE_JS) {\n method = nodeWrap(method);\n }\n method.create = function () {\n return new Md5();\n };\n method.update = function (message) {\n return method.create().update(message);\n };\n for (var i = 0; i < OUTPUT_TYPES.length; ++i) {\n var type = OUTPUT_TYPES[i];\n method[type] = createOutputMethod(type);\n }\n return method;\n };\n\n var nodeWrap = function (method) {\n var crypto = eval(\"require('crypto')\");\n var Buffer = eval(\"require('buffer').Buffer\");\n var nodeMethod = function (message) {\n if (typeof message === 'string') {\n return crypto.createHash('md5').update(message, 'utf8').digest('hex');\n } else {\n if (message === null || message === undefined) {\n throw ERROR;\n } else if (message.constructor === ArrayBuffer) {\n message = new Uint8Array(message);\n }\n }\n if (Array.isArray(message) || ArrayBuffer.isView(message) ||\n message.constructor === Buffer) {\n return crypto.createHash('md5').update(new Buffer(message)).digest('hex');\n } else {\n return method(message);\n }\n };\n return nodeMethod;\n };\n\n /**\n * Md5 class\n * @class Md5\n * @description This is internal class.\n * @see {@link md5.create}\n */\n function Md5(sharedMemory) {\n if (sharedMemory) {\n blocks[0] = blocks[16] = blocks[1] = blocks[2] = blocks[3] =\n blocks[4] = blocks[5] = blocks[6] = blocks[7] =\n blocks[8] = blocks[9] = blocks[10] = blocks[11] =\n blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0;\n this.blocks = blocks;\n this.buffer8 = buffer8;\n } else {\n if (ARRAY_BUFFER) {\n var buffer = new ArrayBuffer(68);\n this.buffer8 = new Uint8Array(buffer);\n this.blocks = new Uint32Array(buffer);\n } else {\n this.blocks = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];\n }\n }\n this.h0 = this.h1 = this.h2 = this.h3 = this.start = this.bytes = this.hBytes = 0;\n this.finalized = this.hashed = false;\n this.first = true;\n }\n\n /**\n * @method update\n * @memberof Md5\n * @instance\n * @description Update hash\n * @param {String|Array|Uint8Array|ArrayBuffer} message message to hash\n * @returns {Md5} Md5 object.\n * @see {@link md5.update}\n */\n Md5.prototype.update = function (message) {\n if (this.finalized) {\n return;\n }\n\n var notString, type = typeof message;\n if (type !== 'string') {\n if (type === 'object') {\n if (message === null) {\n throw ERROR;\n } else if (ARRAY_BUFFER && message.constructor === ArrayBuffer) {\n message = new Uint8Array(message);\n } else if (!Array.isArray(message)) {\n if (!ARRAY_BUFFER || !ArrayBuffer.isView(message)) {\n throw ERROR;\n }\n }\n } else {\n throw ERROR;\n }\n notString = true;\n }\n var code, index = 0, i, length = message.length, blocks = this.blocks;\n var buffer8 = this.buffer8;\n\n while (index < length) {\n if (this.hashed) {\n this.hashed = false;\n blocks[0] = blocks[16];\n blocks[16] = blocks[1] = blocks[2] = blocks[3] =\n blocks[4] = blocks[5] = blocks[6] = blocks[7] =\n blocks[8] = blocks[9] = blocks[10] = blocks[11] =\n blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0;\n }\n\n if (notString) {\n if (ARRAY_BUFFER) {\n for (i = this.start; index < length && i < 64; ++index) {\n buffer8[i++] = message[index];\n }\n } else {\n for (i = this.start; index < length && i < 64; ++index) {\n blocks[i >> 2] |= message[index] << SHIFT[i++ & 3];\n }\n }\n } else {\n if (ARRAY_BUFFER) {\n for (i = this.start; index < length && i < 64; ++index) {\n code = message.charCodeAt(index);\n if (code < 0x80) {\n buffer8[i++] = code;\n } else if (code < 0x800) {\n buffer8[i++] = 0xc0 | (code >> 6);\n buffer8[i++] = 0x80 | (code & 0x3f);\n } else if (code < 0xd800 || code >= 0xe000) {\n buffer8[i++] = 0xe0 | (code >> 12);\n buffer8[i++] = 0x80 | ((code >> 6) & 0x3f);\n buffer8[i++] = 0x80 | (code & 0x3f);\n } else {\n code = 0x10000 + (((code & 0x3ff) << 10) | (message.charCodeAt(++index) & 0x3ff));\n buffer8[i++] = 0xf0 | (code >> 18);\n buffer8[i++] = 0x80 | ((code >> 12) & 0x3f);\n buffer8[i++] = 0x80 | ((code >> 6) & 0x3f);\n buffer8[i++] = 0x80 | (code & 0x3f);\n }\n }\n } else {\n for (i = this.start; index < length && i < 64; ++index) {\n code = message.charCodeAt(index);\n if (code < 0x80) {\n blocks[i >> 2] |= code << SHIFT[i++ & 3];\n } else if (code < 0x800) {\n blocks[i >> 2] |= (0xc0 | (code >> 6)) << SHIFT[i++ & 3];\n blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3];\n } else if (code < 0xd800 || code >= 0xe000) {\n blocks[i >> 2] |= (0xe0 | (code >> 12)) << SHIFT[i++ & 3];\n blocks[i >> 2] |= (0x80 | ((code >> 6) & 0x3f)) << SHIFT[i++ & 3];\n blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3];\n } else {\n code = 0x10000 + (((code & 0x3ff) << 10) | (message.charCodeAt(++index) & 0x3ff));\n blocks[i >> 2] |= (0xf0 | (code >> 18)) << SHIFT[i++ & 3];\n blocks[i >> 2] |= (0x80 | ((code >> 12) & 0x3f)) << SHIFT[i++ & 3];\n blocks[i >> 2] |= (0x80 | ((code >> 6) & 0x3f)) << SHIFT[i++ & 3];\n blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3];\n }\n }\n }\n }\n this.lastByteIndex = i;\n this.bytes += i - this.start;\n if (i >= 64) {\n this.start = i - 64;\n this.hash();\n this.hashed = true;\n } else {\n this.start = i;\n }\n }\n if (this.bytes > 4294967295) {\n this.hBytes += this.bytes / 4294967296 << 0;\n this.bytes = this.bytes % 4294967296;\n }\n return this;\n };\n\n Md5.prototype.finalize = function () {\n if (this.finalized) {\n return;\n }\n this.finalized = true;\n var blocks = this.blocks, i = this.lastByteIndex;\n blocks[i >> 2] |= EXTRA[i & 3];\n if (i >= 56) {\n if (!this.hashed) {\n this.hash();\n }\n blocks[0] = blocks[16];\n blocks[16] = blocks[1] = blocks[2] = blocks[3] =\n blocks[4] = blocks[5] = blocks[6] = blocks[7] =\n blocks[8] = blocks[9] = blocks[10] = blocks[11] =\n blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0;\n }\n blocks[14] = this.bytes << 3;\n blocks[15] = this.hBytes << 3 | this.bytes >>> 29;\n this.hash();\n };\n\n Md5.prototype.hash = function () {\n var a, b, c, d, bc, da, blocks = this.blocks;\n\n if (this.first) {\n a = blocks[0] - 680876937;\n a = (a << 7 | a >>> 25) - 271733879 << 0;\n d = (-1732584194 ^ a & 2004318071) + blocks[1] - 117830708;\n d = (d << 12 | d >>> 20) + a << 0;\n c = (-271733879 ^ (d & (a ^ -271733879))) + blocks[2] - 1126478375;\n c = (c << 17 | c >>> 15) + d << 0;\n b = (a ^ (c & (d ^ a))) + blocks[3] - 1316259209;\n b = (b << 22 | b >>> 10) + c << 0;\n } else {\n a = this.h0;\n b = this.h1;\n c = this.h2;\n d = this.h3;\n a += (d ^ (b & (c ^ d))) + blocks[0] - 680876936;\n a = (a << 7 | a >>> 25) + b << 0;\n d += (c ^ (a & (b ^ c))) + blocks[1] - 389564586;\n d = (d << 12 | d >>> 20) + a << 0;\n c += (b ^ (d & (a ^ b))) + blocks[2] + 606105819;\n c = (c << 17 | c >>> 15) + d << 0;\n b += (a ^ (c & (d ^ a))) + blocks[3] - 1044525330;\n b = (b << 22 | b >>> 10) + c << 0;\n }\n\n a += (d ^ (b & (c ^ d))) + blocks[4] - 176418897;\n a = (a << 7 | a >>> 25) + b << 0;\n d += (c ^ (a & (b ^ c))) + blocks[5] + 1200080426;\n d = (d << 12 | d >>> 20) + a << 0;\n c += (b ^ (d & (a ^ b))) + blocks[6] - 1473231341;\n c = (c << 17 | c >>> 15) + d << 0;\n b += (a ^ (c & (d ^ a))) + blocks[7] - 45705983;\n b = (b << 22 | b >>> 10) + c << 0;\n a += (d ^ (b & (c ^ d))) + blocks[8] + 1770035416;\n a = (a << 7 | a >>> 25) + b << 0;\n d += (c ^ (a & (b ^ c))) + blocks[9] - 1958414417;\n d = (d << 12 | d >>> 20) + a << 0;\n c += (b ^ (d & (a ^ b))) + blocks[10] - 42063;\n c = (c << 17 | c >>> 15) + d << 0;\n b += (a ^ (c & (d ^ a))) + blocks[11] - 1990404162;\n b = (b << 22 | b >>> 10) + c << 0;\n a += (d ^ (b & (c ^ d))) + blocks[12] + 1804603682;\n a = (a << 7 | a >>> 25) + b << 0;\n d += (c ^ (a & (b ^ c))) + blocks[13] - 40341101;\n d = (d << 12 | d >>> 20) + a << 0;\n c += (b ^ (d & (a ^ b))) + blocks[14] - 1502002290;\n c = (c << 17 | c >>> 15) + d << 0;\n b += (a ^ (c & (d ^ a))) + blocks[15] + 1236535329;\n b = (b << 22 | b >>> 10) + c << 0;\n a += (c ^ (d & (b ^ c))) + blocks[1] - 165796510;\n a = (a << 5 | a >>> 27) + b << 0;\n d += (b ^ (c & (a ^ b))) + blocks[6] - 1069501632;\n d = (d << 9 | d >>> 23) + a << 0;\n c += (a ^ (b & (d ^ a))) + blocks[11] + 643717713;\n c = (c << 14 | c >>> 18) + d << 0;\n b += (d ^ (a & (c ^ d))) + blocks[0] - 373897302;\n b = (b << 20 | b >>> 12) + c << 0;\n a += (c ^ (d & (b ^ c))) + blocks[5] - 701558691;\n a = (a << 5 | a >>> 27) + b << 0;\n d += (b ^ (c & (a ^ b))) + blocks[10] + 38016083;\n d = (d << 9 | d >>> 23) + a << 0;\n c += (a ^ (b & (d ^ a))) + blocks[15] - 660478335;\n c = (c << 14 | c >>> 18) + d << 0;\n b += (d ^ (a & (c ^ d))) + blocks[4] - 405537848;\n b = (b << 20 | b >>> 12) + c << 0;\n a += (c ^ (d & (b ^ c))) + blocks[9] + 568446438;\n a = (a << 5 | a >>> 27) + b << 0;\n d += (b ^ (c & (a ^ b))) + blocks[14] - 1019803690;\n d = (d << 9 | d >>> 23) + a << 0;\n c += (a ^ (b & (d ^ a))) + blocks[3] - 187363961;\n c = (c << 14 | c >>> 18) + d << 0;\n b += (d ^ (a & (c ^ d))) + blocks[8] + 1163531501;\n b = (b << 20 | b >>> 12) + c << 0;\n a += (c ^ (d & (b ^ c))) + blocks[13] - 1444681467;\n a = (a << 5 | a >>> 27) + b << 0;\n d += (b ^ (c & (a ^ b))) + blocks[2] - 51403784;\n d = (d << 9 | d >>> 23) + a << 0;\n c += (a ^ (b & (d ^ a))) + blocks[7] + 1735328473;\n c = (c << 14 | c >>> 18) + d << 0;\n b += (d ^ (a & (c ^ d))) + blocks[12] - 1926607734;\n b = (b << 20 | b >>> 12) + c << 0;\n bc = b ^ c;\n a += (bc ^ d) + blocks[5] - 378558;\n a = (a << 4 | a >>> 28) + b << 0;\n d += (bc ^ a) + blocks[8] - 2022574463;\n d = (d << 11 | d >>> 21) + a << 0;\n da = d ^ a;\n c += (da ^ b) + blocks[11] + 1839030562;\n c = (c << 16 | c >>> 16) + d << 0;\n b += (da ^ c) + blocks[14] - 35309556;\n b = (b << 23 | b >>> 9) + c << 0;\n bc = b ^ c;\n a += (bc ^ d) + blocks[1] - 1530992060;\n a = (a << 4 | a >>> 28) + b << 0;\n d += (bc ^ a) + blocks[4] + 1272893353;\n d = (d << 11 | d >>> 21) + a << 0;\n da = d ^ a;\n c += (da ^ b) + blocks[7] - 155497632;\n c = (c << 16 | c >>> 16) + d << 0;\n b += (da ^ c) + blocks[10] - 1094730640;\n b = (b << 23 | b >>> 9) + c << 0;\n bc = b ^ c;\n a += (bc ^ d) + blocks[13] + 681279174;\n a = (a << 4 | a >>> 28) + b << 0;\n d += (bc ^ a) + blocks[0] - 358537222;\n d = (d << 11 | d >>> 21) + a << 0;\n da = d ^ a;\n c += (da ^ b) + blocks[3] - 722521979;\n c = (c << 16 | c >>> 16) + d << 0;\n b += (da ^ c) + blocks[6] + 76029189;\n b = (b << 23 | b >>> 9) + c << 0;\n bc = b ^ c;\n a += (bc ^ d) + blocks[9] - 640364487;\n a = (a << 4 | a >>> 28) + b << 0;\n d += (bc ^ a) + blocks[12] - 421815835;\n d = (d << 11 | d >>> 21) + a << 0;\n da = d ^ a;\n c += (da ^ b) + blocks[15] + 530742520;\n c = (c << 16 | c >>> 16) + d << 0;\n b += (da ^ c) + blocks[2] - 995338651;\n b = (b << 23 | b >>> 9) + c << 0;\n a += (c ^ (b | ~d)) + blocks[0] - 198630844;\n a = (a << 6 | a >>> 26) + b << 0;\n d += (b ^ (a | ~c)) + blocks[7] + 1126891415;\n d = (d << 10 | d >>> 22) + a << 0;\n c += (a ^ (d | ~b)) + blocks[14] - 1416354905;\n c = (c << 15 | c >>> 17) + d << 0;\n b += (d ^ (c | ~a)) + blocks[5] - 57434055;\n b = (b << 21 | b >>> 11) + c << 0;\n a += (c ^ (b | ~d)) + blocks[12] + 1700485571;\n a = (a << 6 | a >>> 26) + b << 0;\n d += (b ^ (a | ~c)) + blocks[3] - 1894986606;\n d = (d << 10 | d >>> 22) + a << 0;\n c += (a ^ (d | ~b)) + blocks[10] - 1051523;\n c = (c << 15 | c >>> 17) + d << 0;\n b += (d ^ (c | ~a)) + blocks[1] - 2054922799;\n b = (b << 21 | b >>> 11) + c << 0;\n a += (c ^ (b | ~d)) + blocks[8] + 1873313359;\n a = (a << 6 | a >>> 26) + b << 0;\n d += (b ^ (a | ~c)) + blocks[15] - 30611744;\n d = (d << 10 | d >>> 22) + a << 0;\n c += (a ^ (d | ~b)) + blocks[6] - 1560198380;\n c = (c << 15 | c >>> 17) + d << 0;\n b += (d ^ (c | ~a)) + blocks[13] + 1309151649;\n b = (b << 21 | b >>> 11) + c << 0;\n a += (c ^ (b | ~d)) + blocks[4] - 145523070;\n a = (a << 6 | a >>> 26) + b << 0;\n d += (b ^ (a | ~c)) + blocks[11] - 1120210379;\n d = (d << 10 | d >>> 22) + a << 0;\n c += (a ^ (d | ~b)) + blocks[2] + 718787259;\n c = (c << 15 | c >>> 17) + d << 0;\n b += (d ^ (c | ~a)) + blocks[9] - 343485551;\n b = (b << 21 | b >>> 11) + c << 0;\n\n if (this.first) {\n this.h0 = a + 1732584193 << 0;\n this.h1 = b - 271733879 << 0;\n this.h2 = c - 1732584194 << 0;\n this.h3 = d + 271733878 << 0;\n this.first = false;\n } else {\n this.h0 = this.h0 + a << 0;\n this.h1 = this.h1 + b << 0;\n this.h2 = this.h2 + c << 0;\n this.h3 = this.h3 + d << 0;\n }\n };\n\n /**\n * @method hex\n * @memberof Md5\n * @instance\n * @description Output hash as hex string\n * @returns {String} Hex string\n * @see {@link md5.hex}\n * @example\n * hash.hex();\n */\n Md5.prototype.hex = function () {\n this.finalize();\n\n var h0 = this.h0, h1 = this.h1, h2 = this.h2, h3 = this.h3;\n\n return HEX_CHARS[(h0 >> 4) & 0x0F] + HEX_CHARS[h0 & 0x0F] +\n HEX_CHARS[(h0 >> 12) & 0x0F] + HEX_CHARS[(h0 >> 8) & 0x0F] +\n HEX_CHARS[(h0 >> 20) & 0x0F] + HEX_CHARS[(h0 >> 16) & 0x0F] +\n HEX_CHARS[(h0 >> 28) & 0x0F] + HEX_CHARS[(h0 >> 24) & 0x0F] +\n HEX_CHARS[(h1 >> 4) & 0x0F] + HEX_CHARS[h1 & 0x0F] +\n HEX_CHARS[(h1 >> 12) & 0x0F] + HEX_CHARS[(h1 >> 8) & 0x0F] +\n HEX_CHARS[(h1 >> 20) & 0x0F] + HEX_CHARS[(h1 >> 16) & 0x0F] +\n HEX_CHARS[(h1 >> 28) & 0x0F] + HEX_CHARS[(h1 >> 24) & 0x0F] +\n HEX_CHARS[(h2 >> 4) & 0x0F] + HEX_CHARS[h2 & 0x0F] +\n HEX_CHARS[(h2 >> 12) & 0x0F] + HEX_CHARS[(h2 >> 8) & 0x0F] +\n HEX_CHARS[(h2 >> 20) & 0x0F] + HEX_CHARS[(h2 >> 16) & 0x0F] +\n HEX_CHARS[(h2 >> 28) & 0x0F] + HEX_CHARS[(h2 >> 24) & 0x0F] +\n HEX_CHARS[(h3 >> 4) & 0x0F] + HEX_CHARS[h3 & 0x0F] +\n HEX_CHARS[(h3 >> 12) & 0x0F] + HEX_CHARS[(h3 >> 8) & 0x0F] +\n HEX_CHARS[(h3 >> 20) & 0x0F] + HEX_CHARS[(h3 >> 16) & 0x0F] +\n HEX_CHARS[(h3 >> 28) & 0x0F] + HEX_CHARS[(h3 >> 24) & 0x0F];\n };\n\n /**\n * @method toString\n * @memberof Md5\n * @instance\n * @description Output hash as hex string\n * @returns {String} Hex string\n * @see {@link md5.hex}\n * @example\n * hash.toString();\n */\n Md5.prototype.toString = Md5.prototype.hex;\n\n /**\n * @method digest\n * @memberof Md5\n * @instance\n * @description Output hash as bytes array\n * @returns {Array} Bytes array\n * @see {@link md5.digest}\n * @example\n * hash.digest();\n */\n Md5.prototype.digest = function () {\n this.finalize();\n\n var h0 = this.h0, h1 = this.h1, h2 = this.h2, h3 = this.h3;\n return [\n h0 & 0xFF, (h0 >> 8) & 0xFF, (h0 >> 16) & 0xFF, (h0 >> 24) & 0xFF,\n h1 & 0xFF, (h1 >> 8) & 0xFF, (h1 >> 16) & 0xFF, (h1 >> 24) & 0xFF,\n h2 & 0xFF, (h2 >> 8) & 0xFF, (h2 >> 16) & 0xFF, (h2 >> 24) & 0xFF,\n h3 & 0xFF, (h3 >> 8) & 0xFF, (h3 >> 16) & 0xFF, (h3 >> 24) & 0xFF\n ];\n };\n\n /**\n * @method array\n * @memberof Md5\n * @instance\n * @description Output hash as bytes array\n * @returns {Array} Bytes array\n * @see {@link md5.array}\n * @example\n * hash.array();\n */\n Md5.prototype.array = Md5.prototype.digest;\n\n /**\n * @method arrayBuffer\n * @memberof Md5\n * @instance\n * @description Output hash as ArrayBuffer\n * @returns {ArrayBuffer} ArrayBuffer\n * @see {@link md5.arrayBuffer}\n * @example\n * hash.arrayBuffer();\n */\n Md5.prototype.arrayBuffer = function () {\n this.finalize();\n\n var buffer = new ArrayBuffer(16);\n var blocks = new Uint32Array(buffer);\n blocks[0] = this.h0;\n blocks[1] = this.h1;\n blocks[2] = this.h2;\n blocks[3] = this.h3;\n return buffer;\n };\n\n /**\n * @method buffer\n * @deprecated This maybe confuse with Buffer in node.js. Please use arrayBuffer instead.\n * @memberof Md5\n * @instance\n * @description Output hash as ArrayBuffer\n * @returns {ArrayBuffer} ArrayBuffer\n * @see {@link md5.buffer}\n * @example\n * hash.buffer();\n */\n Md5.prototype.buffer = Md5.prototype.arrayBuffer;\n\n /**\n * @method base64\n * @memberof Md5\n * @instance\n * @description Output hash as base64 string\n * @returns {String} base64 string\n * @see {@link md5.base64}\n * @example\n * hash.base64();\n */\n Md5.prototype.base64 = function () {\n var v1, v2, v3, base64Str = '', bytes = this.array();\n for (var i = 0; i < 15;) {\n v1 = bytes[i++];\n v2 = bytes[i++];\n v3 = bytes[i++];\n base64Str += BASE64_ENCODE_CHAR[v1 >>> 2] +\n BASE64_ENCODE_CHAR[(v1 << 4 | v2 >>> 4) & 63] +\n BASE64_ENCODE_CHAR[(v2 << 2 | v3 >>> 6) & 63] +\n BASE64_ENCODE_CHAR[v3 & 63];\n }\n v1 = bytes[i];\n base64Str += BASE64_ENCODE_CHAR[v1 >>> 2] +\n BASE64_ENCODE_CHAR[(v1 << 4) & 63] +\n '==';\n return base64Str;\n };\n\n var exports = createMethod();\n\n if (COMMON_JS) {\n module.exports = exports;\n } else {\n /**\n * @method md5\b\n * @description Md5 hash function, export to global in browsers.\n * @param {String|Array|Uint8Array|ArrayBuffer} message message to hash\n * @returns {String} md5 hashes\n * @example\n * md5(''); // d41d8cd98f00b204e9800998ecf8427e\n * md5('The quick brown fox jumps over the lazy dog'); // 9e107d9d372bb6826bd81d3542a419d6\n * md5('The quick brown fox jumps over the lazy dog.'); // e4d909c290d0fb1ca068ffaddf22cbd0\n *\n * // It also supports UTF-8 encoding\n * md5('中文'); // a7bac2239fcdcb3a067903d8077c4a07\n *\n * // It also supports byte `Array`, `Uint8Array`, `ArrayBuffer`\n * md5([]); // d41d8cd98f00b204e9800998ecf8427e\n * md5(new Uint8Array([])); // d41d8cd98f00b204e9800998ecf8427e\n */\n root.md5 = exports;\n if (AMD) {\n define(function () {\n return exports;\n });\n }\n }\n})();\n","/**\n * lodash (Custom Build) \n * Build: `lodash modularize exports=\"npm\" -o ./`\n * Copyright jQuery Foundation and other contributors \n * Released under MIT license \n * Based on Underscore.js 1.8.3 \n * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors\n */\n\n/** Used as the size to enable large array optimizations. */\nvar LARGE_ARRAY_SIZE = 200;\n\n/** Used to stand-in for `undefined` hash values. */\nvar HASH_UNDEFINED = '__lodash_hash_undefined__';\n\n/** Used as references for various `Number` constants. */\nvar MAX_SAFE_INTEGER = 9007199254740991;\n\n/** `Object#toString` result references. */\nvar argsTag = '[object Arguments]',\n arrayTag = '[object Array]',\n boolTag = '[object Boolean]',\n dateTag = '[object Date]',\n errorTag = '[object Error]',\n funcTag = '[object Function]',\n genTag = '[object GeneratorFunction]',\n mapTag = '[object Map]',\n numberTag = '[object Number]',\n objectTag = '[object Object]',\n promiseTag = '[object Promise]',\n regexpTag = '[object RegExp]',\n setTag = '[object Set]',\n stringTag = '[object String]',\n symbolTag = '[object Symbol]',\n weakMapTag = '[object WeakMap]';\n\nvar arrayBufferTag = '[object ArrayBuffer]',\n dataViewTag = '[object DataView]',\n float32Tag = '[object Float32Array]',\n float64Tag = '[object Float64Array]',\n int8Tag = '[object Int8Array]',\n int16Tag = '[object Int16Array]',\n int32Tag = '[object Int32Array]',\n uint8Tag = '[object Uint8Array]',\n uint8ClampedTag = '[object Uint8ClampedArray]',\n uint16Tag = '[object Uint16Array]',\n uint32Tag = '[object Uint32Array]';\n\n/**\n * Used to match `RegExp`\n * [syntax characters](http://ecma-international.org/ecma-262/7.0/#sec-patterns).\n */\nvar reRegExpChar = /[\\\\^$.*+?()[\\]{}|]/g;\n\n/** Used to match `RegExp` flags from their coerced string values. */\nvar reFlags = /\\w*$/;\n\n/** Used to detect host constructors (Safari). */\nvar reIsHostCtor = /^\\[object .+?Constructor\\]$/;\n\n/** Used to detect unsigned integer values. */\nvar reIsUint = /^(?:0|[1-9]\\d*)$/;\n\n/** Used to identify `toStringTag` values supported by `_.clone`. */\nvar cloneableTags = {};\ncloneableTags[argsTag] = cloneableTags[arrayTag] =\ncloneableTags[arrayBufferTag] = cloneableTags[dataViewTag] =\ncloneableTags[boolTag] = cloneableTags[dateTag] =\ncloneableTags[float32Tag] = cloneableTags[float64Tag] =\ncloneableTags[int8Tag] = cloneableTags[int16Tag] =\ncloneableTags[int32Tag] = cloneableTags[mapTag] =\ncloneableTags[numberTag] = cloneableTags[objectTag] =\ncloneableTags[regexpTag] = cloneableTags[setTag] =\ncloneableTags[stringTag] = cloneableTags[symbolTag] =\ncloneableTags[uint8Tag] = cloneableTags[uint8ClampedTag] =\ncloneableTags[uint16Tag] = cloneableTags[uint32Tag] = true;\ncloneableTags[errorTag] = cloneableTags[funcTag] =\ncloneableTags[weakMapTag] = false;\n\n/** Detect free variable `global` from Node.js. */\nvar freeGlobal = typeof global == 'object' && global && global.Object === Object && global;\n\n/** Detect free variable `self`. */\nvar freeSelf = typeof self == 'object' && self && self.Object === Object && self;\n\n/** Used as a reference to the global object. */\nvar root = freeGlobal || freeSelf || Function('return this')();\n\n/** Detect free variable `exports`. */\nvar freeExports = typeof exports == 'object' && exports && !exports.nodeType && exports;\n\n/** Detect free variable `module`. */\nvar freeModule = freeExports && typeof module == 'object' && module && !module.nodeType && module;\n\n/** Detect the popular CommonJS extension `module.exports`. */\nvar moduleExports = freeModule && freeModule.exports === freeExports;\n\n/**\n * Adds the key-value `pair` to `map`.\n *\n * @private\n * @param {Object} map The map to modify.\n * @param {Array} pair The key-value pair to add.\n * @returns {Object} Returns `map`.\n */\nfunction addMapEntry(map, pair) {\n // Don't return `map.set` because it's not chainable in IE 11.\n map.set(pair[0], pair[1]);\n return map;\n}\n\n/**\n * Adds `value` to `set`.\n *\n * @private\n * @param {Object} set The set to modify.\n * @param {*} value The value to add.\n * @returns {Object} Returns `set`.\n */\nfunction addSetEntry(set, value) {\n // Don't return `set.add` because it's not chainable in IE 11.\n set.add(value);\n return set;\n}\n\n/**\n * A specialized version of `_.forEach` for arrays without support for\n * iteratee shorthands.\n *\n * @private\n * @param {Array} [array] The array to iterate over.\n * @param {Function} iteratee The function invoked per iteration.\n * @returns {Array} Returns `array`.\n */\nfunction arrayEach(array, iteratee) {\n var index = -1,\n length = array ? array.length : 0;\n\n while (++index < length) {\n if (iteratee(array[index], index, array) === false) {\n break;\n }\n }\n return array;\n}\n\n/**\n * Appends the elements of `values` to `array`.\n *\n * @private\n * @param {Array} array The array to modify.\n * @param {Array} values The values to append.\n * @returns {Array} Returns `array`.\n */\nfunction arrayPush(array, values) {\n var index = -1,\n length = values.length,\n offset = array.length;\n\n while (++index < length) {\n array[offset + index] = values[index];\n }\n return array;\n}\n\n/**\n * A specialized version of `_.reduce` for arrays without support for\n * iteratee shorthands.\n *\n * @private\n * @param {Array} [array] The array to iterate over.\n * @param {Function} iteratee The function invoked per iteration.\n * @param {*} [accumulator] The initial value.\n * @param {boolean} [initAccum] Specify using the first element of `array` as\n * the initial value.\n * @returns {*} Returns the accumulated value.\n */\nfunction arrayReduce(array, iteratee, accumulator, initAccum) {\n var index = -1,\n length = array ? array.length : 0;\n\n if (initAccum && length) {\n accumulator = array[++index];\n }\n while (++index < length) {\n accumulator = iteratee(accumulator, array[index], index, array);\n }\n return accumulator;\n}\n\n/**\n * The base implementation of `_.times` without support for iteratee shorthands\n * or max array length checks.\n *\n * @private\n * @param {number} n The number of times to invoke `iteratee`.\n * @param {Function} iteratee The function invoked per iteration.\n * @returns {Array} Returns the array of results.\n */\nfunction baseTimes(n, iteratee) {\n var index = -1,\n result = Array(n);\n\n while (++index < n) {\n result[index] = iteratee(index);\n }\n return result;\n}\n\n/**\n * Gets the value at `key` of `object`.\n *\n * @private\n * @param {Object} [object] The object to query.\n * @param {string} key The key of the property to get.\n * @returns {*} Returns the property value.\n */\nfunction getValue(object, key) {\n return object == null ? undefined : object[key];\n}\n\n/**\n * Checks if `value` is a host object in IE < 9.\n *\n * @private\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a host object, else `false`.\n */\nfunction isHostObject(value) {\n // Many host objects are `Object` objects that can coerce to strings\n // despite having improperly defined `toString` methods.\n var result = false;\n if (value != null && typeof value.toString != 'function') {\n try {\n result = !!(value + '');\n } catch (e) {}\n }\n return result;\n}\n\n/**\n * Converts `map` to its key-value pairs.\n *\n * @private\n * @param {Object} map The map to convert.\n * @returns {Array} Returns the key-value pairs.\n */\nfunction mapToArray(map) {\n var index = -1,\n result = Array(map.size);\n\n map.forEach(function(value, key) {\n result[++index] = [key, value];\n });\n return result;\n}\n\n/**\n * Creates a unary function that invokes `func` with its argument transformed.\n *\n * @private\n * @param {Function} func The function to wrap.\n * @param {Function} transform The argument transform.\n * @returns {Function} Returns the new function.\n */\nfunction overArg(func, transform) {\n return function(arg) {\n return func(transform(arg));\n };\n}\n\n/**\n * Converts `set` to an array of its values.\n *\n * @private\n * @param {Object} set The set to convert.\n * @returns {Array} Returns the values.\n */\nfunction setToArray(set) {\n var index = -1,\n result = Array(set.size);\n\n set.forEach(function(value) {\n result[++index] = value;\n });\n return result;\n}\n\n/** Used for built-in method references. */\nvar arrayProto = Array.prototype,\n funcProto = Function.prototype,\n objectProto = Object.prototype;\n\n/** Used to detect overreaching core-js shims. */\nvar coreJsData = root['__core-js_shared__'];\n\n/** Used to detect methods masquerading as native. */\nvar maskSrcKey = (function() {\n var uid = /[^.]+$/.exec(coreJsData && coreJsData.keys && coreJsData.keys.IE_PROTO || '');\n return uid ? ('Symbol(src)_1.' + uid) : '';\n}());\n\n/** Used to resolve the decompiled source of functions. */\nvar funcToString = funcProto.toString;\n\n/** Used to check objects for own properties. */\nvar hasOwnProperty = objectProto.hasOwnProperty;\n\n/**\n * Used to resolve the\n * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)\n * of values.\n */\nvar objectToString = objectProto.toString;\n\n/** Used to detect if a method is native. */\nvar reIsNative = RegExp('^' +\n funcToString.call(hasOwnProperty).replace(reRegExpChar, '\\\\$&')\n .replace(/hasOwnProperty|(function).*?(?=\\\\\\()| for .+?(?=\\\\\\])/g, '$1.*?') + '$'\n);\n\n/** Built-in value references. */\nvar Buffer = moduleExports ? root.Buffer : undefined,\n Symbol = root.Symbol,\n Uint8Array = root.Uint8Array,\n getPrototype = overArg(Object.getPrototypeOf, Object),\n objectCreate = Object.create,\n propertyIsEnumerable = objectProto.propertyIsEnumerable,\n splice = arrayProto.splice;\n\n/* Built-in method references for those with the same name as other `lodash` methods. */\nvar nativeGetSymbols = Object.getOwnPropertySymbols,\n nativeIsBuffer = Buffer ? Buffer.isBuffer : undefined,\n nativeKeys = overArg(Object.keys, Object);\n\n/* Built-in method references that are verified to be native. */\nvar DataView = getNative(root, 'DataView'),\n Map = getNative(root, 'Map'),\n Promise = getNative(root, 'Promise'),\n Set = getNative(root, 'Set'),\n WeakMap = getNative(root, 'WeakMap'),\n nativeCreate = getNative(Object, 'create');\n\n/** Used to detect maps, sets, and weakmaps. */\nvar dataViewCtorString = toSource(DataView),\n mapCtorString = toSource(Map),\n promiseCtorString = toSource(Promise),\n setCtorString = toSource(Set),\n weakMapCtorString = toSource(WeakMap);\n\n/** Used to convert symbols to primitives and strings. */\nvar symbolProto = Symbol ? Symbol.prototype : undefined,\n symbolValueOf = symbolProto ? symbolProto.valueOf : undefined;\n\n/**\n * Creates a hash object.\n *\n * @private\n * @constructor\n * @param {Array} [entries] The key-value pairs to cache.\n */\nfunction Hash(entries) {\n var index = -1,\n length = entries ? entries.length : 0;\n\n this.clear();\n while (++index < length) {\n var entry = entries[index];\n this.set(entry[0], entry[1]);\n }\n}\n\n/**\n * Removes all key-value entries from the hash.\n *\n * @private\n * @name clear\n * @memberOf Hash\n */\nfunction hashClear() {\n this.__data__ = nativeCreate ? nativeCreate(null) : {};\n}\n\n/**\n * Removes `key` and its value from the hash.\n *\n * @private\n * @name delete\n * @memberOf Hash\n * @param {Object} hash The hash to modify.\n * @param {string} key The key of the value to remove.\n * @returns {boolean} Returns `true` if the entry was removed, else `false`.\n */\nfunction hashDelete(key) {\n return this.has(key) && delete this.__data__[key];\n}\n\n/**\n * Gets the hash value for `key`.\n *\n * @private\n * @name get\n * @memberOf Hash\n * @param {string} key The key of the value to get.\n * @returns {*} Returns the entry value.\n */\nfunction hashGet(key) {\n var data = this.__data__;\n if (nativeCreate) {\n var result = data[key];\n return result === HASH_UNDEFINED ? undefined : result;\n }\n return hasOwnProperty.call(data, key) ? data[key] : undefined;\n}\n\n/**\n * Checks if a hash value for `key` exists.\n *\n * @private\n * @name has\n * @memberOf Hash\n * @param {string} key The key of the entry to check.\n * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.\n */\nfunction hashHas(key) {\n var data = this.__data__;\n return nativeCreate ? data[key] !== undefined : hasOwnProperty.call(data, key);\n}\n\n/**\n * Sets the hash `key` to `value`.\n *\n * @private\n * @name set\n * @memberOf Hash\n * @param {string} key The key of the value to set.\n * @param {*} value The value to set.\n * @returns {Object} Returns the hash instance.\n */\nfunction hashSet(key, value) {\n var data = this.__data__;\n data[key] = (nativeCreate && value === undefined) ? HASH_UNDEFINED : value;\n return this;\n}\n\n// Add methods to `Hash`.\nHash.prototype.clear = hashClear;\nHash.prototype['delete'] = hashDelete;\nHash.prototype.get = hashGet;\nHash.prototype.has = hashHas;\nHash.prototype.set = hashSet;\n\n/**\n * Creates an list cache object.\n *\n * @private\n * @constructor\n * @param {Array} [entries] The key-value pairs to cache.\n */\nfunction ListCache(entries) {\n var index = -1,\n length = entries ? entries.length : 0;\n\n this.clear();\n while (++index < length) {\n var entry = entries[index];\n this.set(entry[0], entry[1]);\n }\n}\n\n/**\n * Removes all key-value entries from the list cache.\n *\n * @private\n * @name clear\n * @memberOf ListCache\n */\nfunction listCacheClear() {\n this.__data__ = [];\n}\n\n/**\n * Removes `key` and its value from the list cache.\n *\n * @private\n * @name delete\n * @memberOf ListCache\n * @param {string} key The key of the value to remove.\n * @returns {boolean} Returns `true` if the entry was removed, else `false`.\n */\nfunction listCacheDelete(key) {\n var data = this.__data__,\n index = assocIndexOf(data, key);\n\n if (index < 0) {\n return false;\n }\n var lastIndex = data.length - 1;\n if (index == lastIndex) {\n data.pop();\n } else {\n splice.call(data, index, 1);\n }\n return true;\n}\n\n/**\n * Gets the list cache value for `key`.\n *\n * @private\n * @name get\n * @memberOf ListCache\n * @param {string} key The key of the value to get.\n * @returns {*} Returns the entry value.\n */\nfunction listCacheGet(key) {\n var data = this.__data__,\n index = assocIndexOf(data, key);\n\n return index < 0 ? undefined : data[index][1];\n}\n\n/**\n * Checks if a list cache value for `key` exists.\n *\n * @private\n * @name has\n * @memberOf ListCache\n * @param {string} key The key of the entry to check.\n * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.\n */\nfunction listCacheHas(key) {\n return assocIndexOf(this.__data__, key) > -1;\n}\n\n/**\n * Sets the list cache `key` to `value`.\n *\n * @private\n * @name set\n * @memberOf ListCache\n * @param {string} key The key of the value to set.\n * @param {*} value The value to set.\n * @returns {Object} Returns the list cache instance.\n */\nfunction listCacheSet(key, value) {\n var data = this.__data__,\n index = assocIndexOf(data, key);\n\n if (index < 0) {\n data.push([key, value]);\n } else {\n data[index][1] = value;\n }\n return this;\n}\n\n// Add methods to `ListCache`.\nListCache.prototype.clear = listCacheClear;\nListCache.prototype['delete'] = listCacheDelete;\nListCache.prototype.get = listCacheGet;\nListCache.prototype.has = listCacheHas;\nListCache.prototype.set = listCacheSet;\n\n/**\n * Creates a map cache object to store key-value pairs.\n *\n * @private\n * @constructor\n * @param {Array} [entries] The key-value pairs to cache.\n */\nfunction MapCache(entries) {\n var index = -1,\n length = entries ? entries.length : 0;\n\n this.clear();\n while (++index < length) {\n var entry = entries[index];\n this.set(entry[0], entry[1]);\n }\n}\n\n/**\n * Removes all key-value entries from the map.\n *\n * @private\n * @name clear\n * @memberOf MapCache\n */\nfunction mapCacheClear() {\n this.__data__ = {\n 'hash': new Hash,\n 'map': new (Map || ListCache),\n 'string': new Hash\n };\n}\n\n/**\n * Removes `key` and its value from the map.\n *\n * @private\n * @name delete\n * @memberOf MapCache\n * @param {string} key The key of the value to remove.\n * @returns {boolean} Returns `true` if the entry was removed, else `false`.\n */\nfunction mapCacheDelete(key) {\n return getMapData(this, key)['delete'](key);\n}\n\n/**\n * Gets the map value for `key`.\n *\n * @private\n * @name get\n * @memberOf MapCache\n * @param {string} key The key of the value to get.\n * @returns {*} Returns the entry value.\n */\nfunction mapCacheGet(key) {\n return getMapData(this, key).get(key);\n}\n\n/**\n * Checks if a map value for `key` exists.\n *\n * @private\n * @name has\n * @memberOf MapCache\n * @param {string} key The key of the entry to check.\n * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.\n */\nfunction mapCacheHas(key) {\n return getMapData(this, key).has(key);\n}\n\n/**\n * Sets the map `key` to `value`.\n *\n * @private\n * @name set\n * @memberOf MapCache\n * @param {string} key The key of the value to set.\n * @param {*} value The value to set.\n * @returns {Object} Returns the map cache instance.\n */\nfunction mapCacheSet(key, value) {\n getMapData(this, key).set(key, value);\n return this;\n}\n\n// Add methods to `MapCache`.\nMapCache.prototype.clear = mapCacheClear;\nMapCache.prototype['delete'] = mapCacheDelete;\nMapCache.prototype.get = mapCacheGet;\nMapCache.prototype.has = mapCacheHas;\nMapCache.prototype.set = mapCacheSet;\n\n/**\n * Creates a stack cache object to store key-value pairs.\n *\n * @private\n * @constructor\n * @param {Array} [entries] The key-value pairs to cache.\n */\nfunction Stack(entries) {\n this.__data__ = new ListCache(entries);\n}\n\n/**\n * Removes all key-value entries from the stack.\n *\n * @private\n * @name clear\n * @memberOf Stack\n */\nfunction stackClear() {\n this.__data__ = new ListCache;\n}\n\n/**\n * Removes `key` and its value from the stack.\n *\n * @private\n * @name delete\n * @memberOf Stack\n * @param {string} key The key of the value to remove.\n * @returns {boolean} Returns `true` if the entry was removed, else `false`.\n */\nfunction stackDelete(key) {\n return this.__data__['delete'](key);\n}\n\n/**\n * Gets the stack value for `key`.\n *\n * @private\n * @name get\n * @memberOf Stack\n * @param {string} key The key of the value to get.\n * @returns {*} Returns the entry value.\n */\nfunction stackGet(key) {\n return this.__data__.get(key);\n}\n\n/**\n * Checks if a stack value for `key` exists.\n *\n * @private\n * @name has\n * @memberOf Stack\n * @param {string} key The key of the entry to check.\n * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.\n */\nfunction stackHas(key) {\n return this.__data__.has(key);\n}\n\n/**\n * Sets the stack `key` to `value`.\n *\n * @private\n * @name set\n * @memberOf Stack\n * @param {string} key The key of the value to set.\n * @param {*} value The value to set.\n * @returns {Object} Returns the stack cache instance.\n */\nfunction stackSet(key, value) {\n var cache = this.__data__;\n if (cache instanceof ListCache) {\n var pairs = cache.__data__;\n if (!Map || (pairs.length < LARGE_ARRAY_SIZE - 1)) {\n pairs.push([key, value]);\n return this;\n }\n cache = this.__data__ = new MapCache(pairs);\n }\n cache.set(key, value);\n return this;\n}\n\n// Add methods to `Stack`.\nStack.prototype.clear = stackClear;\nStack.prototype['delete'] = stackDelete;\nStack.prototype.get = stackGet;\nStack.prototype.has = stackHas;\nStack.prototype.set = stackSet;\n\n/**\n * Creates an array of the enumerable property names of the array-like `value`.\n *\n * @private\n * @param {*} value The value to query.\n * @param {boolean} inherited Specify returning inherited property names.\n * @returns {Array} Returns the array of property names.\n */\nfunction arrayLikeKeys(value, inherited) {\n // Safari 8.1 makes `arguments.callee` enumerable in strict mode.\n // Safari 9 makes `arguments.length` enumerable in strict mode.\n var result = (isArray(value) || isArguments(value))\n ? baseTimes(value.length, String)\n : [];\n\n var length = result.length,\n skipIndexes = !!length;\n\n for (var key in value) {\n if ((inherited || hasOwnProperty.call(value, key)) &&\n !(skipIndexes && (key == 'length' || isIndex(key, length)))) {\n result.push(key);\n }\n }\n return result;\n}\n\n/**\n * Assigns `value` to `key` of `object` if the existing value is not equivalent\n * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)\n * for equality comparisons.\n *\n * @private\n * @param {Object} object The object to modify.\n * @param {string} key The key of the property to assign.\n * @param {*} value The value to assign.\n */\nfunction assignValue(object, key, value) {\n var objValue = object[key];\n if (!(hasOwnProperty.call(object, key) && eq(objValue, value)) ||\n (value === undefined && !(key in object))) {\n object[key] = value;\n }\n}\n\n/**\n * Gets the index at which the `key` is found in `array` of key-value pairs.\n *\n * @private\n * @param {Array} array The array to inspect.\n * @param {*} key The key to search for.\n * @returns {number} Returns the index of the matched value, else `-1`.\n */\nfunction assocIndexOf(array, key) {\n var length = array.length;\n while (length--) {\n if (eq(array[length][0], key)) {\n return length;\n }\n }\n return -1;\n}\n\n/**\n * The base implementation of `_.assign` without support for multiple sources\n * or `customizer` functions.\n *\n * @private\n * @param {Object} object The destination object.\n * @param {Object} source The source object.\n * @returns {Object} Returns `object`.\n */\nfunction baseAssign(object, source) {\n return object && copyObject(source, keys(source), object);\n}\n\n/**\n * The base implementation of `_.clone` and `_.cloneDeep` which tracks\n * traversed objects.\n *\n * @private\n * @param {*} value The value to clone.\n * @param {boolean} [isDeep] Specify a deep clone.\n * @param {boolean} [isFull] Specify a clone including symbols.\n * @param {Function} [customizer] The function to customize cloning.\n * @param {string} [key] The key of `value`.\n * @param {Object} [object] The parent object of `value`.\n * @param {Object} [stack] Tracks traversed objects and their clone counterparts.\n * @returns {*} Returns the cloned value.\n */\nfunction baseClone(value, isDeep, isFull, customizer, key, object, stack) {\n var result;\n if (customizer) {\n result = object ? customizer(value, key, object, stack) : customizer(value);\n }\n if (result !== undefined) {\n return result;\n }\n if (!isObject(value)) {\n return value;\n }\n var isArr = isArray(value);\n if (isArr) {\n result = initCloneArray(value);\n if (!isDeep) {\n return copyArray(value, result);\n }\n } else {\n var tag = getTag(value),\n isFunc = tag == funcTag || tag == genTag;\n\n if (isBuffer(value)) {\n return cloneBuffer(value, isDeep);\n }\n if (tag == objectTag || tag == argsTag || (isFunc && !object)) {\n if (isHostObject(value)) {\n return object ? value : {};\n }\n result = initCloneObject(isFunc ? {} : value);\n if (!isDeep) {\n return copySymbols(value, baseAssign(result, value));\n }\n } else {\n if (!cloneableTags[tag]) {\n return object ? value : {};\n }\n result = initCloneByTag(value, tag, baseClone, isDeep);\n }\n }\n // Check for circular references and return its corresponding clone.\n stack || (stack = new Stack);\n var stacked = stack.get(value);\n if (stacked) {\n return stacked;\n }\n stack.set(value, result);\n\n if (!isArr) {\n var props = isFull ? getAllKeys(value) : keys(value);\n }\n arrayEach(props || value, function(subValue, key) {\n if (props) {\n key = subValue;\n subValue = value[key];\n }\n // Recursively populate clone (susceptible to call stack limits).\n assignValue(result, key, baseClone(subValue, isDeep, isFull, customizer, key, value, stack));\n });\n return result;\n}\n\n/**\n * The base implementation of `_.create` without support for assigning\n * properties to the created object.\n *\n * @private\n * @param {Object} prototype The object to inherit from.\n * @returns {Object} Returns the new object.\n */\nfunction baseCreate(proto) {\n return isObject(proto) ? objectCreate(proto) : {};\n}\n\n/**\n * The base implementation of `getAllKeys` and `getAllKeysIn` which uses\n * `keysFunc` and `symbolsFunc` to get the enumerable property names and\n * symbols of `object`.\n *\n * @private\n * @param {Object} object The object to query.\n * @param {Function} keysFunc The function to get the keys of `object`.\n * @param {Function} symbolsFunc The function to get the symbols of `object`.\n * @returns {Array} Returns the array of property names and symbols.\n */\nfunction baseGetAllKeys(object, keysFunc, symbolsFunc) {\n var result = keysFunc(object);\n return isArray(object) ? result : arrayPush(result, symbolsFunc(object));\n}\n\n/**\n * The base implementation of `getTag`.\n *\n * @private\n * @param {*} value The value to query.\n * @returns {string} Returns the `toStringTag`.\n */\nfunction baseGetTag(value) {\n return objectToString.call(value);\n}\n\n/**\n * The base implementation of `_.isNative` without bad shim checks.\n *\n * @private\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a native function,\n * else `false`.\n */\nfunction baseIsNative(value) {\n if (!isObject(value) || isMasked(value)) {\n return false;\n }\n var pattern = (isFunction(value) || isHostObject(value)) ? reIsNative : reIsHostCtor;\n return pattern.test(toSource(value));\n}\n\n/**\n * The base implementation of `_.keys` which doesn't treat sparse arrays as dense.\n *\n * @private\n * @param {Object} object The object to query.\n * @returns {Array} Returns the array of property names.\n */\nfunction baseKeys(object) {\n if (!isPrototype(object)) {\n return nativeKeys(object);\n }\n var result = [];\n for (var key in Object(object)) {\n if (hasOwnProperty.call(object, key) && key != 'constructor') {\n result.push(key);\n }\n }\n return result;\n}\n\n/**\n * Creates a clone of `buffer`.\n *\n * @private\n * @param {Buffer} buffer The buffer to clone.\n * @param {boolean} [isDeep] Specify a deep clone.\n * @returns {Buffer} Returns the cloned buffer.\n */\nfunction cloneBuffer(buffer, isDeep) {\n if (isDeep) {\n return buffer.slice();\n }\n var result = new buffer.constructor(buffer.length);\n buffer.copy(result);\n return result;\n}\n\n/**\n * Creates a clone of `arrayBuffer`.\n *\n * @private\n * @param {ArrayBuffer} arrayBuffer The array buffer to clone.\n * @returns {ArrayBuffer} Returns the cloned array buffer.\n */\nfunction cloneArrayBuffer(arrayBuffer) {\n var result = new arrayBuffer.constructor(arrayBuffer.byteLength);\n new Uint8Array(result).set(new Uint8Array(arrayBuffer));\n return result;\n}\n\n/**\n * Creates a clone of `dataView`.\n *\n * @private\n * @param {Object} dataView The data view to clone.\n * @param {boolean} [isDeep] Specify a deep clone.\n * @returns {Object} Returns the cloned data view.\n */\nfunction cloneDataView(dataView, isDeep) {\n var buffer = isDeep ? cloneArrayBuffer(dataView.buffer) : dataView.buffer;\n return new dataView.constructor(buffer, dataView.byteOffset, dataView.byteLength);\n}\n\n/**\n * Creates a clone of `map`.\n *\n * @private\n * @param {Object} map The map to clone.\n * @param {Function} cloneFunc The function to clone values.\n * @param {boolean} [isDeep] Specify a deep clone.\n * @returns {Object} Returns the cloned map.\n */\nfunction cloneMap(map, isDeep, cloneFunc) {\n var array = isDeep ? cloneFunc(mapToArray(map), true) : mapToArray(map);\n return arrayReduce(array, addMapEntry, new map.constructor);\n}\n\n/**\n * Creates a clone of `regexp`.\n *\n * @private\n * @param {Object} regexp The regexp to clone.\n * @returns {Object} Returns the cloned regexp.\n */\nfunction cloneRegExp(regexp) {\n var result = new regexp.constructor(regexp.source, reFlags.exec(regexp));\n result.lastIndex = regexp.lastIndex;\n return result;\n}\n\n/**\n * Creates a clone of `set`.\n *\n * @private\n * @param {Object} set The set to clone.\n * @param {Function} cloneFunc The function to clone values.\n * @param {boolean} [isDeep] Specify a deep clone.\n * @returns {Object} Returns the cloned set.\n */\nfunction cloneSet(set, isDeep, cloneFunc) {\n var array = isDeep ? cloneFunc(setToArray(set), true) : setToArray(set);\n return arrayReduce(array, addSetEntry, new set.constructor);\n}\n\n/**\n * Creates a clone of the `symbol` object.\n *\n * @private\n * @param {Object} symbol The symbol object to clone.\n * @returns {Object} Returns the cloned symbol object.\n */\nfunction cloneSymbol(symbol) {\n return symbolValueOf ? Object(symbolValueOf.call(symbol)) : {};\n}\n\n/**\n * Creates a clone of `typedArray`.\n *\n * @private\n * @param {Object} typedArray The typed array to clone.\n * @param {boolean} [isDeep] Specify a deep clone.\n * @returns {Object} Returns the cloned typed array.\n */\nfunction cloneTypedArray(typedArray, isDeep) {\n var buffer = isDeep ? cloneArrayBuffer(typedArray.buffer) : typedArray.buffer;\n return new typedArray.constructor(buffer, typedArray.byteOffset, typedArray.length);\n}\n\n/**\n * Copies the values of `source` to `array`.\n *\n * @private\n * @param {Array} source The array to copy values from.\n * @param {Array} [array=[]] The array to copy values to.\n * @returns {Array} Returns `array`.\n */\nfunction copyArray(source, array) {\n var index = -1,\n length = source.length;\n\n array || (array = Array(length));\n while (++index < length) {\n array[index] = source[index];\n }\n return array;\n}\n\n/**\n * Copies properties of `source` to `object`.\n *\n * @private\n * @param {Object} source The object to copy properties from.\n * @param {Array} props The property identifiers to copy.\n * @param {Object} [object={}] The object to copy properties to.\n * @param {Function} [customizer] The function to customize copied values.\n * @returns {Object} Returns `object`.\n */\nfunction copyObject(source, props, object, customizer) {\n object || (object = {});\n\n var index = -1,\n length = props.length;\n\n while (++index < length) {\n var key = props[index];\n\n var newValue = customizer\n ? customizer(object[key], source[key], key, object, source)\n : undefined;\n\n assignValue(object, key, newValue === undefined ? source[key] : newValue);\n }\n return object;\n}\n\n/**\n * Copies own symbol properties of `source` to `object`.\n *\n * @private\n * @param {Object} source The object to copy symbols from.\n * @param {Object} [object={}] The object to copy symbols to.\n * @returns {Object} Returns `object`.\n */\nfunction copySymbols(source, object) {\n return copyObject(source, getSymbols(source), object);\n}\n\n/**\n * Creates an array of own enumerable property names and symbols of `object`.\n *\n * @private\n * @param {Object} object The object to query.\n * @returns {Array} Returns the array of property names and symbols.\n */\nfunction getAllKeys(object) {\n return baseGetAllKeys(object, keys, getSymbols);\n}\n\n/**\n * Gets the data for `map`.\n *\n * @private\n * @param {Object} map The map to query.\n * @param {string} key The reference key.\n * @returns {*} Returns the map data.\n */\nfunction getMapData(map, key) {\n var data = map.__data__;\n return isKeyable(key)\n ? data[typeof key == 'string' ? 'string' : 'hash']\n : data.map;\n}\n\n/**\n * Gets the native function at `key` of `object`.\n *\n * @private\n * @param {Object} object The object to query.\n * @param {string} key The key of the method to get.\n * @returns {*} Returns the function if it's native, else `undefined`.\n */\nfunction getNative(object, key) {\n var value = getValue(object, key);\n return baseIsNative(value) ? value : undefined;\n}\n\n/**\n * Creates an array of the own enumerable symbol properties of `object`.\n *\n * @private\n * @param {Object} object The object to query.\n * @returns {Array} Returns the array of symbols.\n */\nvar getSymbols = nativeGetSymbols ? overArg(nativeGetSymbols, Object) : stubArray;\n\n/**\n * Gets the `toStringTag` of `value`.\n *\n * @private\n * @param {*} value The value to query.\n * @returns {string} Returns the `toStringTag`.\n */\nvar getTag = baseGetTag;\n\n// Fallback for data views, maps, sets, and weak maps in IE 11,\n// for data views in Edge < 14, and promises in Node.js.\nif ((DataView && getTag(new DataView(new ArrayBuffer(1))) != dataViewTag) ||\n (Map && getTag(new Map) != mapTag) ||\n (Promise && getTag(Promise.resolve()) != promiseTag) ||\n (Set && getTag(new Set) != setTag) ||\n (WeakMap && getTag(new WeakMap) != weakMapTag)) {\n getTag = function(value) {\n var result = objectToString.call(value),\n Ctor = result == objectTag ? value.constructor : undefined,\n ctorString = Ctor ? toSource(Ctor) : undefined;\n\n if (ctorString) {\n switch (ctorString) {\n case dataViewCtorString: return dataViewTag;\n case mapCtorString: return mapTag;\n case promiseCtorString: return promiseTag;\n case setCtorString: return setTag;\n case weakMapCtorString: return weakMapTag;\n }\n }\n return result;\n };\n}\n\n/**\n * Initializes an array clone.\n *\n * @private\n * @param {Array} array The array to clone.\n * @returns {Array} Returns the initialized clone.\n */\nfunction initCloneArray(array) {\n var length = array.length,\n result = array.constructor(length);\n\n // Add properties assigned by `RegExp#exec`.\n if (length && typeof array[0] == 'string' && hasOwnProperty.call(array, 'index')) {\n result.index = array.index;\n result.input = array.input;\n }\n return result;\n}\n\n/**\n * Initializes an object clone.\n *\n * @private\n * @param {Object} object The object to clone.\n * @returns {Object} Returns the initialized clone.\n */\nfunction initCloneObject(object) {\n return (typeof object.constructor == 'function' && !isPrototype(object))\n ? baseCreate(getPrototype(object))\n : {};\n}\n\n/**\n * Initializes an object clone based on its `toStringTag`.\n *\n * **Note:** This function only supports cloning values with tags of\n * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`.\n *\n * @private\n * @param {Object} object The object to clone.\n * @param {string} tag The `toStringTag` of the object to clone.\n * @param {Function} cloneFunc The function to clone values.\n * @param {boolean} [isDeep] Specify a deep clone.\n * @returns {Object} Returns the initialized clone.\n */\nfunction initCloneByTag(object, tag, cloneFunc, isDeep) {\n var Ctor = object.constructor;\n switch (tag) {\n case arrayBufferTag:\n return cloneArrayBuffer(object);\n\n case boolTag:\n case dateTag:\n return new Ctor(+object);\n\n case dataViewTag:\n return cloneDataView(object, isDeep);\n\n case float32Tag: case float64Tag:\n case int8Tag: case int16Tag: case int32Tag:\n case uint8Tag: case uint8ClampedTag: case uint16Tag: case uint32Tag:\n return cloneTypedArray(object, isDeep);\n\n case mapTag:\n return cloneMap(object, isDeep, cloneFunc);\n\n case numberTag:\n case stringTag:\n return new Ctor(object);\n\n case regexpTag:\n return cloneRegExp(object);\n\n case setTag:\n return cloneSet(object, isDeep, cloneFunc);\n\n case symbolTag:\n return cloneSymbol(object);\n }\n}\n\n/**\n * Checks if `value` is a valid array-like index.\n *\n * @private\n * @param {*} value The value to check.\n * @param {number} [length=MAX_SAFE_INTEGER] The upper bounds of a valid index.\n * @returns {boolean} Returns `true` if `value` is a valid index, else `false`.\n */\nfunction isIndex(value, length) {\n length = length == null ? MAX_SAFE_INTEGER : length;\n return !!length &&\n (typeof value == 'number' || reIsUint.test(value)) &&\n (value > -1 && value % 1 == 0 && value < length);\n}\n\n/**\n * Checks if `value` is suitable for use as unique object key.\n *\n * @private\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is suitable, else `false`.\n */\nfunction isKeyable(value) {\n var type = typeof value;\n return (type == 'string' || type == 'number' || type == 'symbol' || type == 'boolean')\n ? (value !== '__proto__')\n : (value === null);\n}\n\n/**\n * Checks if `func` has its source masked.\n *\n * @private\n * @param {Function} func The function to check.\n * @returns {boolean} Returns `true` if `func` is masked, else `false`.\n */\nfunction isMasked(func) {\n return !!maskSrcKey && (maskSrcKey in func);\n}\n\n/**\n * Checks if `value` is likely a prototype object.\n *\n * @private\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a prototype, else `false`.\n */\nfunction isPrototype(value) {\n var Ctor = value && value.constructor,\n proto = (typeof Ctor == 'function' && Ctor.prototype) || objectProto;\n\n return value === proto;\n}\n\n/**\n * Converts `func` to its source code.\n *\n * @private\n * @param {Function} func The function to process.\n * @returns {string} Returns the source code.\n */\nfunction toSource(func) {\n if (func != null) {\n try {\n return funcToString.call(func);\n } catch (e) {}\n try {\n return (func + '');\n } catch (e) {}\n }\n return '';\n}\n\n/**\n * This method is like `_.clone` except that it recursively clones `value`.\n *\n * @static\n * @memberOf _\n * @since 1.0.0\n * @category Lang\n * @param {*} value The value to recursively clone.\n * @returns {*} Returns the deep cloned value.\n * @see _.clone\n * @example\n *\n * var objects = [{ 'a': 1 }, { 'b': 2 }];\n *\n * var deep = _.cloneDeep(objects);\n * console.log(deep[0] === objects[0]);\n * // => false\n */\nfunction cloneDeep(value) {\n return baseClone(value, true, true);\n}\n\n/**\n * Performs a\n * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)\n * comparison between two values to determine if they are equivalent.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to compare.\n * @param {*} other The other value to compare.\n * @returns {boolean} Returns `true` if the values are equivalent, else `false`.\n * @example\n *\n * var object = { 'a': 1 };\n * var other = { 'a': 1 };\n *\n * _.eq(object, object);\n * // => true\n *\n * _.eq(object, other);\n * // => false\n *\n * _.eq('a', 'a');\n * // => true\n *\n * _.eq('a', Object('a'));\n * // => false\n *\n * _.eq(NaN, NaN);\n * // => true\n */\nfunction eq(value, other) {\n return value === other || (value !== value && other !== other);\n}\n\n/**\n * Checks if `value` is likely an `arguments` object.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is an `arguments` object,\n * else `false`.\n * @example\n *\n * _.isArguments(function() { return arguments; }());\n * // => true\n *\n * _.isArguments([1, 2, 3]);\n * // => false\n */\nfunction isArguments(value) {\n // Safari 8.1 makes `arguments.callee` enumerable in strict mode.\n return isArrayLikeObject(value) && hasOwnProperty.call(value, 'callee') &&\n (!propertyIsEnumerable.call(value, 'callee') || objectToString.call(value) == argsTag);\n}\n\n/**\n * Checks if `value` is classified as an `Array` object.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is an array, else `false`.\n * @example\n *\n * _.isArray([1, 2, 3]);\n * // => true\n *\n * _.isArray(document.body.children);\n * // => false\n *\n * _.isArray('abc');\n * // => false\n *\n * _.isArray(_.noop);\n * // => false\n */\nvar isArray = Array.isArray;\n\n/**\n * Checks if `value` is array-like. A value is considered array-like if it's\n * not a function and has a `value.length` that's an integer greater than or\n * equal to `0` and less than or equal to `Number.MAX_SAFE_INTEGER`.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is array-like, else `false`.\n * @example\n *\n * _.isArrayLike([1, 2, 3]);\n * // => true\n *\n * _.isArrayLike(document.body.children);\n * // => true\n *\n * _.isArrayLike('abc');\n * // => true\n *\n * _.isArrayLike(_.noop);\n * // => false\n */\nfunction isArrayLike(value) {\n return value != null && isLength(value.length) && !isFunction(value);\n}\n\n/**\n * This method is like `_.isArrayLike` except that it also checks if `value`\n * is an object.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is an array-like object,\n * else `false`.\n * @example\n *\n * _.isArrayLikeObject([1, 2, 3]);\n * // => true\n *\n * _.isArrayLikeObject(document.body.children);\n * // => true\n *\n * _.isArrayLikeObject('abc');\n * // => false\n *\n * _.isArrayLikeObject(_.noop);\n * // => false\n */\nfunction isArrayLikeObject(value) {\n return isObjectLike(value) && isArrayLike(value);\n}\n\n/**\n * Checks if `value` is a buffer.\n *\n * @static\n * @memberOf _\n * @since 4.3.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a buffer, else `false`.\n * @example\n *\n * _.isBuffer(new Buffer(2));\n * // => true\n *\n * _.isBuffer(new Uint8Array(2));\n * // => false\n */\nvar isBuffer = nativeIsBuffer || stubFalse;\n\n/**\n * Checks if `value` is classified as a `Function` object.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a function, else `false`.\n * @example\n *\n * _.isFunction(_);\n * // => true\n *\n * _.isFunction(/abc/);\n * // => false\n */\nfunction isFunction(value) {\n // The use of `Object#toString` avoids issues with the `typeof` operator\n // in Safari 8-9 which returns 'object' for typed array and other constructors.\n var tag = isObject(value) ? objectToString.call(value) : '';\n return tag == funcTag || tag == genTag;\n}\n\n/**\n * Checks if `value` is a valid array-like length.\n *\n * **Note:** This method is loosely based on\n * [`ToLength`](http://ecma-international.org/ecma-262/7.0/#sec-tolength).\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a valid length, else `false`.\n * @example\n *\n * _.isLength(3);\n * // => true\n *\n * _.isLength(Number.MIN_VALUE);\n * // => false\n *\n * _.isLength(Infinity);\n * // => false\n *\n * _.isLength('3');\n * // => false\n */\nfunction isLength(value) {\n return typeof value == 'number' &&\n value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER;\n}\n\n/**\n * Checks if `value` is the\n * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types)\n * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is an object, else `false`.\n * @example\n *\n * _.isObject({});\n * // => true\n *\n * _.isObject([1, 2, 3]);\n * // => true\n *\n * _.isObject(_.noop);\n * // => true\n *\n * _.isObject(null);\n * // => false\n */\nfunction isObject(value) {\n var type = typeof value;\n return !!value && (type == 'object' || type == 'function');\n}\n\n/**\n * Checks if `value` is object-like. A value is object-like if it's not `null`\n * and has a `typeof` result of \"object\".\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is object-like, else `false`.\n * @example\n *\n * _.isObjectLike({});\n * // => true\n *\n * _.isObjectLike([1, 2, 3]);\n * // => true\n *\n * _.isObjectLike(_.noop);\n * // => false\n *\n * _.isObjectLike(null);\n * // => false\n */\nfunction isObjectLike(value) {\n return !!value && typeof value == 'object';\n}\n\n/**\n * Creates an array of the own enumerable property names of `object`.\n *\n * **Note:** Non-object values are coerced to objects. See the\n * [ES spec](http://ecma-international.org/ecma-262/7.0/#sec-object.keys)\n * for more details.\n *\n * @static\n * @since 0.1.0\n * @memberOf _\n * @category Object\n * @param {Object} object The object to query.\n * @returns {Array} Returns the array of property names.\n * @example\n *\n * function Foo() {\n * this.a = 1;\n * this.b = 2;\n * }\n *\n * Foo.prototype.c = 3;\n *\n * _.keys(new Foo);\n * // => ['a', 'b'] (iteration order is not guaranteed)\n *\n * _.keys('hi');\n * // => ['0', '1']\n */\nfunction keys(object) {\n return isArrayLike(object) ? arrayLikeKeys(object) : baseKeys(object);\n}\n\n/**\n * This method returns a new empty array.\n *\n * @static\n * @memberOf _\n * @since 4.13.0\n * @category Util\n * @returns {Array} Returns the new empty array.\n * @example\n *\n * var arrays = _.times(2, _.stubArray);\n *\n * console.log(arrays);\n * // => [[], []]\n *\n * console.log(arrays[0] === arrays[1]);\n * // => false\n */\nfunction stubArray() {\n return [];\n}\n\n/**\n * This method returns `false`.\n *\n * @static\n * @memberOf _\n * @since 4.13.0\n * @category Util\n * @returns {boolean} Returns `false`.\n * @example\n *\n * _.times(2, _.stubFalse);\n * // => [false, false]\n */\nfunction stubFalse() {\n return false;\n}\n\nmodule.exports = cloneDeep;\n","/**\n * lodash (Custom Build) \n * Build: `lodash modularize exports=\"npm\" -o ./`\n * Copyright jQuery Foundation and other contributors \n * Released under MIT license \n * Based on Underscore.js 1.8.3 \n * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors\n */\n\n/** Used as the `TypeError` message for \"Functions\" methods. */\nvar FUNC_ERROR_TEXT = 'Expected a function';\n\n/** Used as references for various `Number` constants. */\nvar NAN = 0 / 0;\n\n/** `Object#toString` result references. */\nvar symbolTag = '[object Symbol]';\n\n/** Used to match leading and trailing whitespace. */\nvar reTrim = /^\\s+|\\s+$/g;\n\n/** Used to detect bad signed hexadecimal string values. */\nvar reIsBadHex = /^[-+]0x[0-9a-f]+$/i;\n\n/** Used to detect binary string values. */\nvar reIsBinary = /^0b[01]+$/i;\n\n/** Used to detect octal string values. */\nvar reIsOctal = /^0o[0-7]+$/i;\n\n/** Built-in method references without a dependency on `root`. */\nvar freeParseInt = parseInt;\n\n/** Detect free variable `global` from Node.js. */\nvar freeGlobal = typeof global == 'object' && global && global.Object === Object && global;\n\n/** Detect free variable `self`. */\nvar freeSelf = typeof self == 'object' && self && self.Object === Object && self;\n\n/** Used as a reference to the global object. */\nvar root = freeGlobal || freeSelf || Function('return this')();\n\n/** Used for built-in method references. */\nvar objectProto = Object.prototype;\n\n/**\n * Used to resolve the\n * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)\n * of values.\n */\nvar objectToString = objectProto.toString;\n\n/* Built-in method references for those with the same name as other `lodash` methods. */\nvar nativeMax = Math.max,\n nativeMin = Math.min;\n\n/**\n * Gets the timestamp of the number of milliseconds that have elapsed since\n * the Unix epoch (1 January 1970 00:00:00 UTC).\n *\n * @static\n * @memberOf _\n * @since 2.4.0\n * @category Date\n * @returns {number} Returns the timestamp.\n * @example\n *\n * _.defer(function(stamp) {\n * console.log(_.now() - stamp);\n * }, _.now());\n * // => Logs the number of milliseconds it took for the deferred invocation.\n */\nvar now = function() {\n return root.Date.now();\n};\n\n/**\n * Creates a debounced function that delays invoking `func` until after `wait`\n * milliseconds have elapsed since the last time the debounced function was\n * invoked. The debounced function comes with a `cancel` method to cancel\n * delayed `func` invocations and a `flush` method to immediately invoke them.\n * Provide `options` to indicate whether `func` should be invoked on the\n * leading and/or trailing edge of the `wait` timeout. The `func` is invoked\n * with the last arguments provided to the debounced function. Subsequent\n * calls to the debounced function return the result of the last `func`\n * invocation.\n *\n * **Note:** If `leading` and `trailing` options are `true`, `func` is\n * invoked on the trailing edge of the timeout only if the debounced function\n * is invoked more than once during the `wait` timeout.\n *\n * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred\n * until to the next tick, similar to `setTimeout` with a timeout of `0`.\n *\n * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)\n * for details over the differences between `_.debounce` and `_.throttle`.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Function\n * @param {Function} func The function to debounce.\n * @param {number} [wait=0] The number of milliseconds to delay.\n * @param {Object} [options={}] The options object.\n * @param {boolean} [options.leading=false]\n * Specify invoking on the leading edge of the timeout.\n * @param {number} [options.maxWait]\n * The maximum time `func` is allowed to be delayed before it's invoked.\n * @param {boolean} [options.trailing=true]\n * Specify invoking on the trailing edge of the timeout.\n * @returns {Function} Returns the new debounced function.\n * @example\n *\n * // Avoid costly calculations while the window size is in flux.\n * jQuery(window).on('resize', _.debounce(calculateLayout, 150));\n *\n * // Invoke `sendMail` when clicked, debouncing subsequent calls.\n * jQuery(element).on('click', _.debounce(sendMail, 300, {\n * 'leading': true,\n * 'trailing': false\n * }));\n *\n * // Ensure `batchLog` is invoked once after 1 second of debounced calls.\n * var debounced = _.debounce(batchLog, 250, { 'maxWait': 1000 });\n * var source = new EventSource('/stream');\n * jQuery(source).on('message', debounced);\n *\n * // Cancel the trailing debounced invocation.\n * jQuery(window).on('popstate', debounced.cancel);\n */\nfunction debounce(func, wait, options) {\n var lastArgs,\n lastThis,\n maxWait,\n result,\n timerId,\n lastCallTime,\n lastInvokeTime = 0,\n leading = false,\n maxing = false,\n trailing = true;\n\n if (typeof func != 'function') {\n throw new TypeError(FUNC_ERROR_TEXT);\n }\n wait = toNumber(wait) || 0;\n if (isObject(options)) {\n leading = !!options.leading;\n maxing = 'maxWait' in options;\n maxWait = maxing ? nativeMax(toNumber(options.maxWait) || 0, wait) : maxWait;\n trailing = 'trailing' in options ? !!options.trailing : trailing;\n }\n\n function invokeFunc(time) {\n var args = lastArgs,\n thisArg = lastThis;\n\n lastArgs = lastThis = undefined;\n lastInvokeTime = time;\n result = func.apply(thisArg, args);\n return result;\n }\n\n function leadingEdge(time) {\n // Reset any `maxWait` timer.\n lastInvokeTime = time;\n // Start the timer for the trailing edge.\n timerId = setTimeout(timerExpired, wait);\n // Invoke the leading edge.\n return leading ? invokeFunc(time) : result;\n }\n\n function remainingWait(time) {\n var timeSinceLastCall = time - lastCallTime,\n timeSinceLastInvoke = time - lastInvokeTime,\n result = wait - timeSinceLastCall;\n\n return maxing ? nativeMin(result, maxWait - timeSinceLastInvoke) : result;\n }\n\n function shouldInvoke(time) {\n var timeSinceLastCall = time - lastCallTime,\n timeSinceLastInvoke = time - lastInvokeTime;\n\n // Either this is the first call, activity has stopped and we're at the\n // trailing edge, the system time has gone backwards and we're treating\n // it as the trailing edge, or we've hit the `maxWait` limit.\n return (lastCallTime === undefined || (timeSinceLastCall >= wait) ||\n (timeSinceLastCall < 0) || (maxing && timeSinceLastInvoke >= maxWait));\n }\n\n function timerExpired() {\n var time = now();\n if (shouldInvoke(time)) {\n return trailingEdge(time);\n }\n // Restart the timer.\n timerId = setTimeout(timerExpired, remainingWait(time));\n }\n\n function trailingEdge(time) {\n timerId = undefined;\n\n // Only invoke if we have `lastArgs` which means `func` has been\n // debounced at least once.\n if (trailing && lastArgs) {\n return invokeFunc(time);\n }\n lastArgs = lastThis = undefined;\n return result;\n }\n\n function cancel() {\n if (timerId !== undefined) {\n clearTimeout(timerId);\n }\n lastInvokeTime = 0;\n lastArgs = lastCallTime = lastThis = timerId = undefined;\n }\n\n function flush() {\n return timerId === undefined ? result : trailingEdge(now());\n }\n\n function debounced() {\n var time = now(),\n isInvoking = shouldInvoke(time);\n\n lastArgs = arguments;\n lastThis = this;\n lastCallTime = time;\n\n if (isInvoking) {\n if (timerId === undefined) {\n return leadingEdge(lastCallTime);\n }\n if (maxing) {\n // Handle invocations in a tight loop.\n timerId = setTimeout(timerExpired, wait);\n return invokeFunc(lastCallTime);\n }\n }\n if (timerId === undefined) {\n timerId = setTimeout(timerExpired, wait);\n }\n return result;\n }\n debounced.cancel = cancel;\n debounced.flush = flush;\n return debounced;\n}\n\n/**\n * Checks if `value` is the\n * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types)\n * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is an object, else `false`.\n * @example\n *\n * _.isObject({});\n * // => true\n *\n * _.isObject([1, 2, 3]);\n * // => true\n *\n * _.isObject(_.noop);\n * // => true\n *\n * _.isObject(null);\n * // => false\n */\nfunction isObject(value) {\n var type = typeof value;\n return !!value && (type == 'object' || type == 'function');\n}\n\n/**\n * Checks if `value` is object-like. A value is object-like if it's not `null`\n * and has a `typeof` result of \"object\".\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is object-like, else `false`.\n * @example\n *\n * _.isObjectLike({});\n * // => true\n *\n * _.isObjectLike([1, 2, 3]);\n * // => true\n *\n * _.isObjectLike(_.noop);\n * // => false\n *\n * _.isObjectLike(null);\n * // => false\n */\nfunction isObjectLike(value) {\n return !!value && typeof value == 'object';\n}\n\n/**\n * Checks if `value` is classified as a `Symbol` primitive or object.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a symbol, else `false`.\n * @example\n *\n * _.isSymbol(Symbol.iterator);\n * // => true\n *\n * _.isSymbol('abc');\n * // => false\n */\nfunction isSymbol(value) {\n return typeof value == 'symbol' ||\n (isObjectLike(value) && objectToString.call(value) == symbolTag);\n}\n\n/**\n * Converts `value` to a number.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to process.\n * @returns {number} Returns the number.\n * @example\n *\n * _.toNumber(3.2);\n * // => 3.2\n *\n * _.toNumber(Number.MIN_VALUE);\n * // => 5e-324\n *\n * _.toNumber(Infinity);\n * // => Infinity\n *\n * _.toNumber('3.2');\n * // => 3.2\n */\nfunction toNumber(value) {\n if (typeof value == 'number') {\n return value;\n }\n if (isSymbol(value)) {\n return NAN;\n }\n if (isObject(value)) {\n var other = typeof value.valueOf == 'function' ? value.valueOf() : value;\n value = isObject(other) ? (other + '') : other;\n }\n if (typeof value != 'string') {\n return value === 0 ? value : +value;\n }\n value = value.replace(reTrim, '');\n var isBinary = reIsBinary.test(value);\n return (isBinary || reIsOctal.test(value))\n ? freeParseInt(value.slice(2), isBinary ? 2 : 8)\n : (reIsBadHex.test(value) ? NAN : +value);\n}\n\nmodule.exports = debounce;\n","/**\n * Lodash (Custom Build) \n * Build: `lodash modularize exports=\"npm\" -o ./`\n * Copyright JS Foundation and other contributors \n * Released under MIT license \n * Based on Underscore.js 1.8.3 \n * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors\n */\n\n/** Used as the size to enable large array optimizations. */\nvar LARGE_ARRAY_SIZE = 200;\n\n/** Used to stand-in for `undefined` hash values. */\nvar HASH_UNDEFINED = '__lodash_hash_undefined__';\n\n/** Used to compose bitmasks for value comparisons. */\nvar COMPARE_PARTIAL_FLAG = 1,\n COMPARE_UNORDERED_FLAG = 2;\n\n/** Used as references for various `Number` constants. */\nvar MAX_SAFE_INTEGER = 9007199254740991;\n\n/** `Object#toString` result references. */\nvar argsTag = '[object Arguments]',\n arrayTag = '[object Array]',\n asyncTag = '[object AsyncFunction]',\n boolTag = '[object Boolean]',\n dateTag = '[object Date]',\n errorTag = '[object Error]',\n funcTag = '[object Function]',\n genTag = '[object GeneratorFunction]',\n mapTag = '[object Map]',\n numberTag = '[object Number]',\n nullTag = '[object Null]',\n objectTag = '[object Object]',\n promiseTag = '[object Promise]',\n proxyTag = '[object Proxy]',\n regexpTag = '[object RegExp]',\n setTag = '[object Set]',\n stringTag = '[object String]',\n symbolTag = '[object Symbol]',\n undefinedTag = '[object Undefined]',\n weakMapTag = '[object WeakMap]';\n\nvar arrayBufferTag = '[object ArrayBuffer]',\n dataViewTag = '[object DataView]',\n float32Tag = '[object Float32Array]',\n float64Tag = '[object Float64Array]',\n int8Tag = '[object Int8Array]',\n int16Tag = '[object Int16Array]',\n int32Tag = '[object Int32Array]',\n uint8Tag = '[object Uint8Array]',\n uint8ClampedTag = '[object Uint8ClampedArray]',\n uint16Tag = '[object Uint16Array]',\n uint32Tag = '[object Uint32Array]';\n\n/**\n * Used to match `RegExp`\n * [syntax characters](http://ecma-international.org/ecma-262/7.0/#sec-patterns).\n */\nvar reRegExpChar = /[\\\\^$.*+?()[\\]{}|]/g;\n\n/** Used to detect host constructors (Safari). */\nvar reIsHostCtor = /^\\[object .+?Constructor\\]$/;\n\n/** Used to detect unsigned integer values. */\nvar reIsUint = /^(?:0|[1-9]\\d*)$/;\n\n/** Used to identify `toStringTag` values of typed arrays. */\nvar typedArrayTags = {};\ntypedArrayTags[float32Tag] = typedArrayTags[float64Tag] =\ntypedArrayTags[int8Tag] = typedArrayTags[int16Tag] =\ntypedArrayTags[int32Tag] = typedArrayTags[uint8Tag] =\ntypedArrayTags[uint8ClampedTag] = typedArrayTags[uint16Tag] =\ntypedArrayTags[uint32Tag] = true;\ntypedArrayTags[argsTag] = typedArrayTags[arrayTag] =\ntypedArrayTags[arrayBufferTag] = typedArrayTags[boolTag] =\ntypedArrayTags[dataViewTag] = typedArrayTags[dateTag] =\ntypedArrayTags[errorTag] = typedArrayTags[funcTag] =\ntypedArrayTags[mapTag] = typedArrayTags[numberTag] =\ntypedArrayTags[objectTag] = typedArrayTags[regexpTag] =\ntypedArrayTags[setTag] = typedArrayTags[stringTag] =\ntypedArrayTags[weakMapTag] = false;\n\n/** Detect free variable `global` from Node.js. */\nvar freeGlobal = typeof global == 'object' && global && global.Object === Object && global;\n\n/** Detect free variable `self`. */\nvar freeSelf = typeof self == 'object' && self && self.Object === Object && self;\n\n/** Used as a reference to the global object. */\nvar root = freeGlobal || freeSelf || Function('return this')();\n\n/** Detect free variable `exports`. */\nvar freeExports = typeof exports == 'object' && exports && !exports.nodeType && exports;\n\n/** Detect free variable `module`. */\nvar freeModule = freeExports && typeof module == 'object' && module && !module.nodeType && module;\n\n/** Detect the popular CommonJS extension `module.exports`. */\nvar moduleExports = freeModule && freeModule.exports === freeExports;\n\n/** Detect free variable `process` from Node.js. */\nvar freeProcess = moduleExports && freeGlobal.process;\n\n/** Used to access faster Node.js helpers. */\nvar nodeUtil = (function() {\n try {\n return freeProcess && freeProcess.binding && freeProcess.binding('util');\n } catch (e) {}\n}());\n\n/* Node.js helper references. */\nvar nodeIsTypedArray = nodeUtil && nodeUtil.isTypedArray;\n\n/**\n * A specialized version of `_.filter` for arrays without support for\n * iteratee shorthands.\n *\n * @private\n * @param {Array} [array] The array to iterate over.\n * @param {Function} predicate The function invoked per iteration.\n * @returns {Array} Returns the new filtered array.\n */\nfunction arrayFilter(array, predicate) {\n var index = -1,\n length = array == null ? 0 : array.length,\n resIndex = 0,\n result = [];\n\n while (++index < length) {\n var value = array[index];\n if (predicate(value, index, array)) {\n result[resIndex++] = value;\n }\n }\n return result;\n}\n\n/**\n * Appends the elements of `values` to `array`.\n *\n * @private\n * @param {Array} array The array to modify.\n * @param {Array} values The values to append.\n * @returns {Array} Returns `array`.\n */\nfunction arrayPush(array, values) {\n var index = -1,\n length = values.length,\n offset = array.length;\n\n while (++index < length) {\n array[offset + index] = values[index];\n }\n return array;\n}\n\n/**\n * A specialized version of `_.some` for arrays without support for iteratee\n * shorthands.\n *\n * @private\n * @param {Array} [array] The array to iterate over.\n * @param {Function} predicate The function invoked per iteration.\n * @returns {boolean} Returns `true` if any element passes the predicate check,\n * else `false`.\n */\nfunction arraySome(array, predicate) {\n var index = -1,\n length = array == null ? 0 : array.length;\n\n while (++index < length) {\n if (predicate(array[index], index, array)) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * The base implementation of `_.times` without support for iteratee shorthands\n * or max array length checks.\n *\n * @private\n * @param {number} n The number of times to invoke `iteratee`.\n * @param {Function} iteratee The function invoked per iteration.\n * @returns {Array} Returns the array of results.\n */\nfunction baseTimes(n, iteratee) {\n var index = -1,\n result = Array(n);\n\n while (++index < n) {\n result[index] = iteratee(index);\n }\n return result;\n}\n\n/**\n * The base implementation of `_.unary` without support for storing metadata.\n *\n * @private\n * @param {Function} func The function to cap arguments for.\n * @returns {Function} Returns the new capped function.\n */\nfunction baseUnary(func) {\n return function(value) {\n return func(value);\n };\n}\n\n/**\n * Checks if a `cache` value for `key` exists.\n *\n * @private\n * @param {Object} cache The cache to query.\n * @param {string} key The key of the entry to check.\n * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.\n */\nfunction cacheHas(cache, key) {\n return cache.has(key);\n}\n\n/**\n * Gets the value at `key` of `object`.\n *\n * @private\n * @param {Object} [object] The object to query.\n * @param {string} key The key of the property to get.\n * @returns {*} Returns the property value.\n */\nfunction getValue(object, key) {\n return object == null ? undefined : object[key];\n}\n\n/**\n * Converts `map` to its key-value pairs.\n *\n * @private\n * @param {Object} map The map to convert.\n * @returns {Array} Returns the key-value pairs.\n */\nfunction mapToArray(map) {\n var index = -1,\n result = Array(map.size);\n\n map.forEach(function(value, key) {\n result[++index] = [key, value];\n });\n return result;\n}\n\n/**\n * Creates a unary function that invokes `func` with its argument transformed.\n *\n * @private\n * @param {Function} func The function to wrap.\n * @param {Function} transform The argument transform.\n * @returns {Function} Returns the new function.\n */\nfunction overArg(func, transform) {\n return function(arg) {\n return func(transform(arg));\n };\n}\n\n/**\n * Converts `set` to an array of its values.\n *\n * @private\n * @param {Object} set The set to convert.\n * @returns {Array} Returns the values.\n */\nfunction setToArray(set) {\n var index = -1,\n result = Array(set.size);\n\n set.forEach(function(value) {\n result[++index] = value;\n });\n return result;\n}\n\n/** Used for built-in method references. */\nvar arrayProto = Array.prototype,\n funcProto = Function.prototype,\n objectProto = Object.prototype;\n\n/** Used to detect overreaching core-js shims. */\nvar coreJsData = root['__core-js_shared__'];\n\n/** Used to resolve the decompiled source of functions. */\nvar funcToString = funcProto.toString;\n\n/** Used to check objects for own properties. */\nvar hasOwnProperty = objectProto.hasOwnProperty;\n\n/** Used to detect methods masquerading as native. */\nvar maskSrcKey = (function() {\n var uid = /[^.]+$/.exec(coreJsData && coreJsData.keys && coreJsData.keys.IE_PROTO || '');\n return uid ? ('Symbol(src)_1.' + uid) : '';\n}());\n\n/**\n * Used to resolve the\n * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)\n * of values.\n */\nvar nativeObjectToString = objectProto.toString;\n\n/** Used to detect if a method is native. */\nvar reIsNative = RegExp('^' +\n funcToString.call(hasOwnProperty).replace(reRegExpChar, '\\\\$&')\n .replace(/hasOwnProperty|(function).*?(?=\\\\\\()| for .+?(?=\\\\\\])/g, '$1.*?') + '$'\n);\n\n/** Built-in value references. */\nvar Buffer = moduleExports ? root.Buffer : undefined,\n Symbol = root.Symbol,\n Uint8Array = root.Uint8Array,\n propertyIsEnumerable = objectProto.propertyIsEnumerable,\n splice = arrayProto.splice,\n symToStringTag = Symbol ? Symbol.toStringTag : undefined;\n\n/* Built-in method references for those with the same name as other `lodash` methods. */\nvar nativeGetSymbols = Object.getOwnPropertySymbols,\n nativeIsBuffer = Buffer ? Buffer.isBuffer : undefined,\n nativeKeys = overArg(Object.keys, Object);\n\n/* Built-in method references that are verified to be native. */\nvar DataView = getNative(root, 'DataView'),\n Map = getNative(root, 'Map'),\n Promise = getNative(root, 'Promise'),\n Set = getNative(root, 'Set'),\n WeakMap = getNative(root, 'WeakMap'),\n nativeCreate = getNative(Object, 'create');\n\n/** Used to detect maps, sets, and weakmaps. */\nvar dataViewCtorString = toSource(DataView),\n mapCtorString = toSource(Map),\n promiseCtorString = toSource(Promise),\n setCtorString = toSource(Set),\n weakMapCtorString = toSource(WeakMap);\n\n/** Used to convert symbols to primitives and strings. */\nvar symbolProto = Symbol ? Symbol.prototype : undefined,\n symbolValueOf = symbolProto ? symbolProto.valueOf : undefined;\n\n/**\n * Creates a hash object.\n *\n * @private\n * @constructor\n * @param {Array} [entries] The key-value pairs to cache.\n */\nfunction Hash(entries) {\n var index = -1,\n length = entries == null ? 0 : entries.length;\n\n this.clear();\n while (++index < length) {\n var entry = entries[index];\n this.set(entry[0], entry[1]);\n }\n}\n\n/**\n * Removes all key-value entries from the hash.\n *\n * @private\n * @name clear\n * @memberOf Hash\n */\nfunction hashClear() {\n this.__data__ = nativeCreate ? nativeCreate(null) : {};\n this.size = 0;\n}\n\n/**\n * Removes `key` and its value from the hash.\n *\n * @private\n * @name delete\n * @memberOf Hash\n * @param {Object} hash The hash to modify.\n * @param {string} key The key of the value to remove.\n * @returns {boolean} Returns `true` if the entry was removed, else `false`.\n */\nfunction hashDelete(key) {\n var result = this.has(key) && delete this.__data__[key];\n this.size -= result ? 1 : 0;\n return result;\n}\n\n/**\n * Gets the hash value for `key`.\n *\n * @private\n * @name get\n * @memberOf Hash\n * @param {string} key The key of the value to get.\n * @returns {*} Returns the entry value.\n */\nfunction hashGet(key) {\n var data = this.__data__;\n if (nativeCreate) {\n var result = data[key];\n return result === HASH_UNDEFINED ? undefined : result;\n }\n return hasOwnProperty.call(data, key) ? data[key] : undefined;\n}\n\n/**\n * Checks if a hash value for `key` exists.\n *\n * @private\n * @name has\n * @memberOf Hash\n * @param {string} key The key of the entry to check.\n * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.\n */\nfunction hashHas(key) {\n var data = this.__data__;\n return nativeCreate ? (data[key] !== undefined) : hasOwnProperty.call(data, key);\n}\n\n/**\n * Sets the hash `key` to `value`.\n *\n * @private\n * @name set\n * @memberOf Hash\n * @param {string} key The key of the value to set.\n * @param {*} value The value to set.\n * @returns {Object} Returns the hash instance.\n */\nfunction hashSet(key, value) {\n var data = this.__data__;\n this.size += this.has(key) ? 0 : 1;\n data[key] = (nativeCreate && value === undefined) ? HASH_UNDEFINED : value;\n return this;\n}\n\n// Add methods to `Hash`.\nHash.prototype.clear = hashClear;\nHash.prototype['delete'] = hashDelete;\nHash.prototype.get = hashGet;\nHash.prototype.has = hashHas;\nHash.prototype.set = hashSet;\n\n/**\n * Creates an list cache object.\n *\n * @private\n * @constructor\n * @param {Array} [entries] The key-value pairs to cache.\n */\nfunction ListCache(entries) {\n var index = -1,\n length = entries == null ? 0 : entries.length;\n\n this.clear();\n while (++index < length) {\n var entry = entries[index];\n this.set(entry[0], entry[1]);\n }\n}\n\n/**\n * Removes all key-value entries from the list cache.\n *\n * @private\n * @name clear\n * @memberOf ListCache\n */\nfunction listCacheClear() {\n this.__data__ = [];\n this.size = 0;\n}\n\n/**\n * Removes `key` and its value from the list cache.\n *\n * @private\n * @name delete\n * @memberOf ListCache\n * @param {string} key The key of the value to remove.\n * @returns {boolean} Returns `true` if the entry was removed, else `false`.\n */\nfunction listCacheDelete(key) {\n var data = this.__data__,\n index = assocIndexOf(data, key);\n\n if (index < 0) {\n return false;\n }\n var lastIndex = data.length - 1;\n if (index == lastIndex) {\n data.pop();\n } else {\n splice.call(data, index, 1);\n }\n --this.size;\n return true;\n}\n\n/**\n * Gets the list cache value for `key`.\n *\n * @private\n * @name get\n * @memberOf ListCache\n * @param {string} key The key of the value to get.\n * @returns {*} Returns the entry value.\n */\nfunction listCacheGet(key) {\n var data = this.__data__,\n index = assocIndexOf(data, key);\n\n return index < 0 ? undefined : data[index][1];\n}\n\n/**\n * Checks if a list cache value for `key` exists.\n *\n * @private\n * @name has\n * @memberOf ListCache\n * @param {string} key The key of the entry to check.\n * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.\n */\nfunction listCacheHas(key) {\n return assocIndexOf(this.__data__, key) > -1;\n}\n\n/**\n * Sets the list cache `key` to `value`.\n *\n * @private\n * @name set\n * @memberOf ListCache\n * @param {string} key The key of the value to set.\n * @param {*} value The value to set.\n * @returns {Object} Returns the list cache instance.\n */\nfunction listCacheSet(key, value) {\n var data = this.__data__,\n index = assocIndexOf(data, key);\n\n if (index < 0) {\n ++this.size;\n data.push([key, value]);\n } else {\n data[index][1] = value;\n }\n return this;\n}\n\n// Add methods to `ListCache`.\nListCache.prototype.clear = listCacheClear;\nListCache.prototype['delete'] = listCacheDelete;\nListCache.prototype.get = listCacheGet;\nListCache.prototype.has = listCacheHas;\nListCache.prototype.set = listCacheSet;\n\n/**\n * Creates a map cache object to store key-value pairs.\n *\n * @private\n * @constructor\n * @param {Array} [entries] The key-value pairs to cache.\n */\nfunction MapCache(entries) {\n var index = -1,\n length = entries == null ? 0 : entries.length;\n\n this.clear();\n while (++index < length) {\n var entry = entries[index];\n this.set(entry[0], entry[1]);\n }\n}\n\n/**\n * Removes all key-value entries from the map.\n *\n * @private\n * @name clear\n * @memberOf MapCache\n */\nfunction mapCacheClear() {\n this.size = 0;\n this.__data__ = {\n 'hash': new Hash,\n 'map': new (Map || ListCache),\n 'string': new Hash\n };\n}\n\n/**\n * Removes `key` and its value from the map.\n *\n * @private\n * @name delete\n * @memberOf MapCache\n * @param {string} key The key of the value to remove.\n * @returns {boolean} Returns `true` if the entry was removed, else `false`.\n */\nfunction mapCacheDelete(key) {\n var result = getMapData(this, key)['delete'](key);\n this.size -= result ? 1 : 0;\n return result;\n}\n\n/**\n * Gets the map value for `key`.\n *\n * @private\n * @name get\n * @memberOf MapCache\n * @param {string} key The key of the value to get.\n * @returns {*} Returns the entry value.\n */\nfunction mapCacheGet(key) {\n return getMapData(this, key).get(key);\n}\n\n/**\n * Checks if a map value for `key` exists.\n *\n * @private\n * @name has\n * @memberOf MapCache\n * @param {string} key The key of the entry to check.\n * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.\n */\nfunction mapCacheHas(key) {\n return getMapData(this, key).has(key);\n}\n\n/**\n * Sets the map `key` to `value`.\n *\n * @private\n * @name set\n * @memberOf MapCache\n * @param {string} key The key of the value to set.\n * @param {*} value The value to set.\n * @returns {Object} Returns the map cache instance.\n */\nfunction mapCacheSet(key, value) {\n var data = getMapData(this, key),\n size = data.size;\n\n data.set(key, value);\n this.size += data.size == size ? 0 : 1;\n return this;\n}\n\n// Add methods to `MapCache`.\nMapCache.prototype.clear = mapCacheClear;\nMapCache.prototype['delete'] = mapCacheDelete;\nMapCache.prototype.get = mapCacheGet;\nMapCache.prototype.has = mapCacheHas;\nMapCache.prototype.set = mapCacheSet;\n\n/**\n *\n * Creates an array cache object to store unique values.\n *\n * @private\n * @constructor\n * @param {Array} [values] The values to cache.\n */\nfunction SetCache(values) {\n var index = -1,\n length = values == null ? 0 : values.length;\n\n this.__data__ = new MapCache;\n while (++index < length) {\n this.add(values[index]);\n }\n}\n\n/**\n * Adds `value` to the array cache.\n *\n * @private\n * @name add\n * @memberOf SetCache\n * @alias push\n * @param {*} value The value to cache.\n * @returns {Object} Returns the cache instance.\n */\nfunction setCacheAdd(value) {\n this.__data__.set(value, HASH_UNDEFINED);\n return this;\n}\n\n/**\n * Checks if `value` is in the array cache.\n *\n * @private\n * @name has\n * @memberOf SetCache\n * @param {*} value The value to search for.\n * @returns {number} Returns `true` if `value` is found, else `false`.\n */\nfunction setCacheHas(value) {\n return this.__data__.has(value);\n}\n\n// Add methods to `SetCache`.\nSetCache.prototype.add = SetCache.prototype.push = setCacheAdd;\nSetCache.prototype.has = setCacheHas;\n\n/**\n * Creates a stack cache object to store key-value pairs.\n *\n * @private\n * @constructor\n * @param {Array} [entries] The key-value pairs to cache.\n */\nfunction Stack(entries) {\n var data = this.__data__ = new ListCache(entries);\n this.size = data.size;\n}\n\n/**\n * Removes all key-value entries from the stack.\n *\n * @private\n * @name clear\n * @memberOf Stack\n */\nfunction stackClear() {\n this.__data__ = new ListCache;\n this.size = 0;\n}\n\n/**\n * Removes `key` and its value from the stack.\n *\n * @private\n * @name delete\n * @memberOf Stack\n * @param {string} key The key of the value to remove.\n * @returns {boolean} Returns `true` if the entry was removed, else `false`.\n */\nfunction stackDelete(key) {\n var data = this.__data__,\n result = data['delete'](key);\n\n this.size = data.size;\n return result;\n}\n\n/**\n * Gets the stack value for `key`.\n *\n * @private\n * @name get\n * @memberOf Stack\n * @param {string} key The key of the value to get.\n * @returns {*} Returns the entry value.\n */\nfunction stackGet(key) {\n return this.__data__.get(key);\n}\n\n/**\n * Checks if a stack value for `key` exists.\n *\n * @private\n * @name has\n * @memberOf Stack\n * @param {string} key The key of the entry to check.\n * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.\n */\nfunction stackHas(key) {\n return this.__data__.has(key);\n}\n\n/**\n * Sets the stack `key` to `value`.\n *\n * @private\n * @name set\n * @memberOf Stack\n * @param {string} key The key of the value to set.\n * @param {*} value The value to set.\n * @returns {Object} Returns the stack cache instance.\n */\nfunction stackSet(key, value) {\n var data = this.__data__;\n if (data instanceof ListCache) {\n var pairs = data.__data__;\n if (!Map || (pairs.length < LARGE_ARRAY_SIZE - 1)) {\n pairs.push([key, value]);\n this.size = ++data.size;\n return this;\n }\n data = this.__data__ = new MapCache(pairs);\n }\n data.set(key, value);\n this.size = data.size;\n return this;\n}\n\n// Add methods to `Stack`.\nStack.prototype.clear = stackClear;\nStack.prototype['delete'] = stackDelete;\nStack.prototype.get = stackGet;\nStack.prototype.has = stackHas;\nStack.prototype.set = stackSet;\n\n/**\n * Creates an array of the enumerable property names of the array-like `value`.\n *\n * @private\n * @param {*} value The value to query.\n * @param {boolean} inherited Specify returning inherited property names.\n * @returns {Array} Returns the array of property names.\n */\nfunction arrayLikeKeys(value, inherited) {\n var isArr = isArray(value),\n isArg = !isArr && isArguments(value),\n isBuff = !isArr && !isArg && isBuffer(value),\n isType = !isArr && !isArg && !isBuff && isTypedArray(value),\n skipIndexes = isArr || isArg || isBuff || isType,\n result = skipIndexes ? baseTimes(value.length, String) : [],\n length = result.length;\n\n for (var key in value) {\n if ((inherited || hasOwnProperty.call(value, key)) &&\n !(skipIndexes && (\n // Safari 9 has enumerable `arguments.length` in strict mode.\n key == 'length' ||\n // Node.js 0.10 has enumerable non-index properties on buffers.\n (isBuff && (key == 'offset' || key == 'parent')) ||\n // PhantomJS 2 has enumerable non-index properties on typed arrays.\n (isType && (key == 'buffer' || key == 'byteLength' || key == 'byteOffset')) ||\n // Skip index properties.\n isIndex(key, length)\n ))) {\n result.push(key);\n }\n }\n return result;\n}\n\n/**\n * Gets the index at which the `key` is found in `array` of key-value pairs.\n *\n * @private\n * @param {Array} array The array to inspect.\n * @param {*} key The key to search for.\n * @returns {number} Returns the index of the matched value, else `-1`.\n */\nfunction assocIndexOf(array, key) {\n var length = array.length;\n while (length--) {\n if (eq(array[length][0], key)) {\n return length;\n }\n }\n return -1;\n}\n\n/**\n * The base implementation of `getAllKeys` and `getAllKeysIn` which uses\n * `keysFunc` and `symbolsFunc` to get the enumerable property names and\n * symbols of `object`.\n *\n * @private\n * @param {Object} object The object to query.\n * @param {Function} keysFunc The function to get the keys of `object`.\n * @param {Function} symbolsFunc The function to get the symbols of `object`.\n * @returns {Array} Returns the array of property names and symbols.\n */\nfunction baseGetAllKeys(object, keysFunc, symbolsFunc) {\n var result = keysFunc(object);\n return isArray(object) ? result : arrayPush(result, symbolsFunc(object));\n}\n\n/**\n * The base implementation of `getTag` without fallbacks for buggy environments.\n *\n * @private\n * @param {*} value The value to query.\n * @returns {string} Returns the `toStringTag`.\n */\nfunction baseGetTag(value) {\n if (value == null) {\n return value === undefined ? undefinedTag : nullTag;\n }\n return (symToStringTag && symToStringTag in Object(value))\n ? getRawTag(value)\n : objectToString(value);\n}\n\n/**\n * The base implementation of `_.isArguments`.\n *\n * @private\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is an `arguments` object,\n */\nfunction baseIsArguments(value) {\n return isObjectLike(value) && baseGetTag(value) == argsTag;\n}\n\n/**\n * The base implementation of `_.isEqual` which supports partial comparisons\n * and tracks traversed objects.\n *\n * @private\n * @param {*} value The value to compare.\n * @param {*} other The other value to compare.\n * @param {boolean} bitmask The bitmask flags.\n * 1 - Unordered comparison\n * 2 - Partial comparison\n * @param {Function} [customizer] The function to customize comparisons.\n * @param {Object} [stack] Tracks traversed `value` and `other` objects.\n * @returns {boolean} Returns `true` if the values are equivalent, else `false`.\n */\nfunction baseIsEqual(value, other, bitmask, customizer, stack) {\n if (value === other) {\n return true;\n }\n if (value == null || other == null || (!isObjectLike(value) && !isObjectLike(other))) {\n return value !== value && other !== other;\n }\n return baseIsEqualDeep(value, other, bitmask, customizer, baseIsEqual, stack);\n}\n\n/**\n * A specialized version of `baseIsEqual` for arrays and objects which performs\n * deep comparisons and tracks traversed objects enabling objects with circular\n * references to be compared.\n *\n * @private\n * @param {Object} object The object to compare.\n * @param {Object} other The other object to compare.\n * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details.\n * @param {Function} customizer The function to customize comparisons.\n * @param {Function} equalFunc The function to determine equivalents of values.\n * @param {Object} [stack] Tracks traversed `object` and `other` objects.\n * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.\n */\nfunction baseIsEqualDeep(object, other, bitmask, customizer, equalFunc, stack) {\n var objIsArr = isArray(object),\n othIsArr = isArray(other),\n objTag = objIsArr ? arrayTag : getTag(object),\n othTag = othIsArr ? arrayTag : getTag(other);\n\n objTag = objTag == argsTag ? objectTag : objTag;\n othTag = othTag == argsTag ? objectTag : othTag;\n\n var objIsObj = objTag == objectTag,\n othIsObj = othTag == objectTag,\n isSameTag = objTag == othTag;\n\n if (isSameTag && isBuffer(object)) {\n if (!isBuffer(other)) {\n return false;\n }\n objIsArr = true;\n objIsObj = false;\n }\n if (isSameTag && !objIsObj) {\n stack || (stack = new Stack);\n return (objIsArr || isTypedArray(object))\n ? equalArrays(object, other, bitmask, customizer, equalFunc, stack)\n : equalByTag(object, other, objTag, bitmask, customizer, equalFunc, stack);\n }\n if (!(bitmask & COMPARE_PARTIAL_FLAG)) {\n var objIsWrapped = objIsObj && hasOwnProperty.call(object, '__wrapped__'),\n othIsWrapped = othIsObj && hasOwnProperty.call(other, '__wrapped__');\n\n if (objIsWrapped || othIsWrapped) {\n var objUnwrapped = objIsWrapped ? object.value() : object,\n othUnwrapped = othIsWrapped ? other.value() : other;\n\n stack || (stack = new Stack);\n return equalFunc(objUnwrapped, othUnwrapped, bitmask, customizer, stack);\n }\n }\n if (!isSameTag) {\n return false;\n }\n stack || (stack = new Stack);\n return equalObjects(object, other, bitmask, customizer, equalFunc, stack);\n}\n\n/**\n * The base implementation of `_.isNative` without bad shim checks.\n *\n * @private\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a native function,\n * else `false`.\n */\nfunction baseIsNative(value) {\n if (!isObject(value) || isMasked(value)) {\n return false;\n }\n var pattern = isFunction(value) ? reIsNative : reIsHostCtor;\n return pattern.test(toSource(value));\n}\n\n/**\n * The base implementation of `_.isTypedArray` without Node.js optimizations.\n *\n * @private\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a typed array, else `false`.\n */\nfunction baseIsTypedArray(value) {\n return isObjectLike(value) &&\n isLength(value.length) && !!typedArrayTags[baseGetTag(value)];\n}\n\n/**\n * The base implementation of `_.keys` which doesn't treat sparse arrays as dense.\n *\n * @private\n * @param {Object} object The object to query.\n * @returns {Array} Returns the array of property names.\n */\nfunction baseKeys(object) {\n if (!isPrototype(object)) {\n return nativeKeys(object);\n }\n var result = [];\n for (var key in Object(object)) {\n if (hasOwnProperty.call(object, key) && key != 'constructor') {\n result.push(key);\n }\n }\n return result;\n}\n\n/**\n * A specialized version of `baseIsEqualDeep` for arrays with support for\n * partial deep comparisons.\n *\n * @private\n * @param {Array} array The array to compare.\n * @param {Array} other The other array to compare.\n * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details.\n * @param {Function} customizer The function to customize comparisons.\n * @param {Function} equalFunc The function to determine equivalents of values.\n * @param {Object} stack Tracks traversed `array` and `other` objects.\n * @returns {boolean} Returns `true` if the arrays are equivalent, else `false`.\n */\nfunction equalArrays(array, other, bitmask, customizer, equalFunc, stack) {\n var isPartial = bitmask & COMPARE_PARTIAL_FLAG,\n arrLength = array.length,\n othLength = other.length;\n\n if (arrLength != othLength && !(isPartial && othLength > arrLength)) {\n return false;\n }\n // Assume cyclic values are equal.\n var stacked = stack.get(array);\n if (stacked && stack.get(other)) {\n return stacked == other;\n }\n var index = -1,\n result = true,\n seen = (bitmask & COMPARE_UNORDERED_FLAG) ? new SetCache : undefined;\n\n stack.set(array, other);\n stack.set(other, array);\n\n // Ignore non-index properties.\n while (++index < arrLength) {\n var arrValue = array[index],\n othValue = other[index];\n\n if (customizer) {\n var compared = isPartial\n ? customizer(othValue, arrValue, index, other, array, stack)\n : customizer(arrValue, othValue, index, array, other, stack);\n }\n if (compared !== undefined) {\n if (compared) {\n continue;\n }\n result = false;\n break;\n }\n // Recursively compare arrays (susceptible to call stack limits).\n if (seen) {\n if (!arraySome(other, function(othValue, othIndex) {\n if (!cacheHas(seen, othIndex) &&\n (arrValue === othValue || equalFunc(arrValue, othValue, bitmask, customizer, stack))) {\n return seen.push(othIndex);\n }\n })) {\n result = false;\n break;\n }\n } else if (!(\n arrValue === othValue ||\n equalFunc(arrValue, othValue, bitmask, customizer, stack)\n )) {\n result = false;\n break;\n }\n }\n stack['delete'](array);\n stack['delete'](other);\n return result;\n}\n\n/**\n * A specialized version of `baseIsEqualDeep` for comparing objects of\n * the same `toStringTag`.\n *\n * **Note:** This function only supports comparing values with tags of\n * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`.\n *\n * @private\n * @param {Object} object The object to compare.\n * @param {Object} other The other object to compare.\n * @param {string} tag The `toStringTag` of the objects to compare.\n * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details.\n * @param {Function} customizer The function to customize comparisons.\n * @param {Function} equalFunc The function to determine equivalents of values.\n * @param {Object} stack Tracks traversed `object` and `other` objects.\n * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.\n */\nfunction equalByTag(object, other, tag, bitmask, customizer, equalFunc, stack) {\n switch (tag) {\n case dataViewTag:\n if ((object.byteLength != other.byteLength) ||\n (object.byteOffset != other.byteOffset)) {\n return false;\n }\n object = object.buffer;\n other = other.buffer;\n\n case arrayBufferTag:\n if ((object.byteLength != other.byteLength) ||\n !equalFunc(new Uint8Array(object), new Uint8Array(other))) {\n return false;\n }\n return true;\n\n case boolTag:\n case dateTag:\n case numberTag:\n // Coerce booleans to `1` or `0` and dates to milliseconds.\n // Invalid dates are coerced to `NaN`.\n return eq(+object, +other);\n\n case errorTag:\n return object.name == other.name && object.message == other.message;\n\n case regexpTag:\n case stringTag:\n // Coerce regexes to strings and treat strings, primitives and objects,\n // as equal. See http://www.ecma-international.org/ecma-262/7.0/#sec-regexp.prototype.tostring\n // for more details.\n return object == (other + '');\n\n case mapTag:\n var convert = mapToArray;\n\n case setTag:\n var isPartial = bitmask & COMPARE_PARTIAL_FLAG;\n convert || (convert = setToArray);\n\n if (object.size != other.size && !isPartial) {\n return false;\n }\n // Assume cyclic values are equal.\n var stacked = stack.get(object);\n if (stacked) {\n return stacked == other;\n }\n bitmask |= COMPARE_UNORDERED_FLAG;\n\n // Recursively compare objects (susceptible to call stack limits).\n stack.set(object, other);\n var result = equalArrays(convert(object), convert(other), bitmask, customizer, equalFunc, stack);\n stack['delete'](object);\n return result;\n\n case symbolTag:\n if (symbolValueOf) {\n return symbolValueOf.call(object) == symbolValueOf.call(other);\n }\n }\n return false;\n}\n\n/**\n * A specialized version of `baseIsEqualDeep` for objects with support for\n * partial deep comparisons.\n *\n * @private\n * @param {Object} object The object to compare.\n * @param {Object} other The other object to compare.\n * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details.\n * @param {Function} customizer The function to customize comparisons.\n * @param {Function} equalFunc The function to determine equivalents of values.\n * @param {Object} stack Tracks traversed `object` and `other` objects.\n * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.\n */\nfunction equalObjects(object, other, bitmask, customizer, equalFunc, stack) {\n var isPartial = bitmask & COMPARE_PARTIAL_FLAG,\n objProps = getAllKeys(object),\n objLength = objProps.length,\n othProps = getAllKeys(other),\n othLength = othProps.length;\n\n if (objLength != othLength && !isPartial) {\n return false;\n }\n var index = objLength;\n while (index--) {\n var key = objProps[index];\n if (!(isPartial ? key in other : hasOwnProperty.call(other, key))) {\n return false;\n }\n }\n // Assume cyclic values are equal.\n var stacked = stack.get(object);\n if (stacked && stack.get(other)) {\n return stacked == other;\n }\n var result = true;\n stack.set(object, other);\n stack.set(other, object);\n\n var skipCtor = isPartial;\n while (++index < objLength) {\n key = objProps[index];\n var objValue = object[key],\n othValue = other[key];\n\n if (customizer) {\n var compared = isPartial\n ? customizer(othValue, objValue, key, other, object, stack)\n : customizer(objValue, othValue, key, object, other, stack);\n }\n // Recursively compare objects (susceptible to call stack limits).\n if (!(compared === undefined\n ? (objValue === othValue || equalFunc(objValue, othValue, bitmask, customizer, stack))\n : compared\n )) {\n result = false;\n break;\n }\n skipCtor || (skipCtor = key == 'constructor');\n }\n if (result && !skipCtor) {\n var objCtor = object.constructor,\n othCtor = other.constructor;\n\n // Non `Object` object instances with different constructors are not equal.\n if (objCtor != othCtor &&\n ('constructor' in object && 'constructor' in other) &&\n !(typeof objCtor == 'function' && objCtor instanceof objCtor &&\n typeof othCtor == 'function' && othCtor instanceof othCtor)) {\n result = false;\n }\n }\n stack['delete'](object);\n stack['delete'](other);\n return result;\n}\n\n/**\n * Creates an array of own enumerable property names and symbols of `object`.\n *\n * @private\n * @param {Object} object The object to query.\n * @returns {Array} Returns the array of property names and symbols.\n */\nfunction getAllKeys(object) {\n return baseGetAllKeys(object, keys, getSymbols);\n}\n\n/**\n * Gets the data for `map`.\n *\n * @private\n * @param {Object} map The map to query.\n * @param {string} key The reference key.\n * @returns {*} Returns the map data.\n */\nfunction getMapData(map, key) {\n var data = map.__data__;\n return isKeyable(key)\n ? data[typeof key == 'string' ? 'string' : 'hash']\n : data.map;\n}\n\n/**\n * Gets the native function at `key` of `object`.\n *\n * @private\n * @param {Object} object The object to query.\n * @param {string} key The key of the method to get.\n * @returns {*} Returns the function if it's native, else `undefined`.\n */\nfunction getNative(object, key) {\n var value = getValue(object, key);\n return baseIsNative(value) ? value : undefined;\n}\n\n/**\n * A specialized version of `baseGetTag` which ignores `Symbol.toStringTag` values.\n *\n * @private\n * @param {*} value The value to query.\n * @returns {string} Returns the raw `toStringTag`.\n */\nfunction getRawTag(value) {\n var isOwn = hasOwnProperty.call(value, symToStringTag),\n tag = value[symToStringTag];\n\n try {\n value[symToStringTag] = undefined;\n var unmasked = true;\n } catch (e) {}\n\n var result = nativeObjectToString.call(value);\n if (unmasked) {\n if (isOwn) {\n value[symToStringTag] = tag;\n } else {\n delete value[symToStringTag];\n }\n }\n return result;\n}\n\n/**\n * Creates an array of the own enumerable symbols of `object`.\n *\n * @private\n * @param {Object} object The object to query.\n * @returns {Array} Returns the array of symbols.\n */\nvar getSymbols = !nativeGetSymbols ? stubArray : function(object) {\n if (object == null) {\n return [];\n }\n object = Object(object);\n return arrayFilter(nativeGetSymbols(object), function(symbol) {\n return propertyIsEnumerable.call(object, symbol);\n });\n};\n\n/**\n * Gets the `toStringTag` of `value`.\n *\n * @private\n * @param {*} value The value to query.\n * @returns {string} Returns the `toStringTag`.\n */\nvar getTag = baseGetTag;\n\n// Fallback for data views, maps, sets, and weak maps in IE 11 and promises in Node.js < 6.\nif ((DataView && getTag(new DataView(new ArrayBuffer(1))) != dataViewTag) ||\n (Map && getTag(new Map) != mapTag) ||\n (Promise && getTag(Promise.resolve()) != promiseTag) ||\n (Set && getTag(new Set) != setTag) ||\n (WeakMap && getTag(new WeakMap) != weakMapTag)) {\n getTag = function(value) {\n var result = baseGetTag(value),\n Ctor = result == objectTag ? value.constructor : undefined,\n ctorString = Ctor ? toSource(Ctor) : '';\n\n if (ctorString) {\n switch (ctorString) {\n case dataViewCtorString: return dataViewTag;\n case mapCtorString: return mapTag;\n case promiseCtorString: return promiseTag;\n case setCtorString: return setTag;\n case weakMapCtorString: return weakMapTag;\n }\n }\n return result;\n };\n}\n\n/**\n * Checks if `value` is a valid array-like index.\n *\n * @private\n * @param {*} value The value to check.\n * @param {number} [length=MAX_SAFE_INTEGER] The upper bounds of a valid index.\n * @returns {boolean} Returns `true` if `value` is a valid index, else `false`.\n */\nfunction isIndex(value, length) {\n length = length == null ? MAX_SAFE_INTEGER : length;\n return !!length &&\n (typeof value == 'number' || reIsUint.test(value)) &&\n (value > -1 && value % 1 == 0 && value < length);\n}\n\n/**\n * Checks if `value` is suitable for use as unique object key.\n *\n * @private\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is suitable, else `false`.\n */\nfunction isKeyable(value) {\n var type = typeof value;\n return (type == 'string' || type == 'number' || type == 'symbol' || type == 'boolean')\n ? (value !== '__proto__')\n : (value === null);\n}\n\n/**\n * Checks if `func` has its source masked.\n *\n * @private\n * @param {Function} func The function to check.\n * @returns {boolean} Returns `true` if `func` is masked, else `false`.\n */\nfunction isMasked(func) {\n return !!maskSrcKey && (maskSrcKey in func);\n}\n\n/**\n * Checks if `value` is likely a prototype object.\n *\n * @private\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a prototype, else `false`.\n */\nfunction isPrototype(value) {\n var Ctor = value && value.constructor,\n proto = (typeof Ctor == 'function' && Ctor.prototype) || objectProto;\n\n return value === proto;\n}\n\n/**\n * Converts `value` to a string using `Object.prototype.toString`.\n *\n * @private\n * @param {*} value The value to convert.\n * @returns {string} Returns the converted string.\n */\nfunction objectToString(value) {\n return nativeObjectToString.call(value);\n}\n\n/**\n * Converts `func` to its source code.\n *\n * @private\n * @param {Function} func The function to convert.\n * @returns {string} Returns the source code.\n */\nfunction toSource(func) {\n if (func != null) {\n try {\n return funcToString.call(func);\n } catch (e) {}\n try {\n return (func + '');\n } catch (e) {}\n }\n return '';\n}\n\n/**\n * Performs a\n * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)\n * comparison between two values to determine if they are equivalent.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to compare.\n * @param {*} other The other value to compare.\n * @returns {boolean} Returns `true` if the values are equivalent, else `false`.\n * @example\n *\n * var object = { 'a': 1 };\n * var other = { 'a': 1 };\n *\n * _.eq(object, object);\n * // => true\n *\n * _.eq(object, other);\n * // => false\n *\n * _.eq('a', 'a');\n * // => true\n *\n * _.eq('a', Object('a'));\n * // => false\n *\n * _.eq(NaN, NaN);\n * // => true\n */\nfunction eq(value, other) {\n return value === other || (value !== value && other !== other);\n}\n\n/**\n * Checks if `value` is likely an `arguments` object.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is an `arguments` object,\n * else `false`.\n * @example\n *\n * _.isArguments(function() { return arguments; }());\n * // => true\n *\n * _.isArguments([1, 2, 3]);\n * // => false\n */\nvar isArguments = baseIsArguments(function() { return arguments; }()) ? baseIsArguments : function(value) {\n return isObjectLike(value) && hasOwnProperty.call(value, 'callee') &&\n !propertyIsEnumerable.call(value, 'callee');\n};\n\n/**\n * Checks if `value` is classified as an `Array` object.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is an array, else `false`.\n * @example\n *\n * _.isArray([1, 2, 3]);\n * // => true\n *\n * _.isArray(document.body.children);\n * // => false\n *\n * _.isArray('abc');\n * // => false\n *\n * _.isArray(_.noop);\n * // => false\n */\nvar isArray = Array.isArray;\n\n/**\n * Checks if `value` is array-like. A value is considered array-like if it's\n * not a function and has a `value.length` that's an integer greater than or\n * equal to `0` and less than or equal to `Number.MAX_SAFE_INTEGER`.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is array-like, else `false`.\n * @example\n *\n * _.isArrayLike([1, 2, 3]);\n * // => true\n *\n * _.isArrayLike(document.body.children);\n * // => true\n *\n * _.isArrayLike('abc');\n * // => true\n *\n * _.isArrayLike(_.noop);\n * // => false\n */\nfunction isArrayLike(value) {\n return value != null && isLength(value.length) && !isFunction(value);\n}\n\n/**\n * Checks if `value` is a buffer.\n *\n * @static\n * @memberOf _\n * @since 4.3.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a buffer, else `false`.\n * @example\n *\n * _.isBuffer(new Buffer(2));\n * // => true\n *\n * _.isBuffer(new Uint8Array(2));\n * // => false\n */\nvar isBuffer = nativeIsBuffer || stubFalse;\n\n/**\n * Performs a deep comparison between two values to determine if they are\n * equivalent.\n *\n * **Note:** This method supports comparing arrays, array buffers, booleans,\n * date objects, error objects, maps, numbers, `Object` objects, regexes,\n * sets, strings, symbols, and typed arrays. `Object` objects are compared\n * by their own, not inherited, enumerable properties. Functions and DOM\n * nodes are compared by strict equality, i.e. `===`.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Lang\n * @param {*} value The value to compare.\n * @param {*} other The other value to compare.\n * @returns {boolean} Returns `true` if the values are equivalent, else `false`.\n * @example\n *\n * var object = { 'a': 1 };\n * var other = { 'a': 1 };\n *\n * _.isEqual(object, other);\n * // => true\n *\n * object === other;\n * // => false\n */\nfunction isEqual(value, other) {\n return baseIsEqual(value, other);\n}\n\n/**\n * Checks if `value` is classified as a `Function` object.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a function, else `false`.\n * @example\n *\n * _.isFunction(_);\n * // => true\n *\n * _.isFunction(/abc/);\n * // => false\n */\nfunction isFunction(value) {\n if (!isObject(value)) {\n return false;\n }\n // The use of `Object#toString` avoids issues with the `typeof` operator\n // in Safari 9 which returns 'object' for typed arrays and other constructors.\n var tag = baseGetTag(value);\n return tag == funcTag || tag == genTag || tag == asyncTag || tag == proxyTag;\n}\n\n/**\n * Checks if `value` is a valid array-like length.\n *\n * **Note:** This method is loosely based on\n * [`ToLength`](http://ecma-international.org/ecma-262/7.0/#sec-tolength).\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a valid length, else `false`.\n * @example\n *\n * _.isLength(3);\n * // => true\n *\n * _.isLength(Number.MIN_VALUE);\n * // => false\n *\n * _.isLength(Infinity);\n * // => false\n *\n * _.isLength('3');\n * // => false\n */\nfunction isLength(value) {\n return typeof value == 'number' &&\n value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER;\n}\n\n/**\n * Checks if `value` is the\n * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types)\n * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is an object, else `false`.\n * @example\n *\n * _.isObject({});\n * // => true\n *\n * _.isObject([1, 2, 3]);\n * // => true\n *\n * _.isObject(_.noop);\n * // => true\n *\n * _.isObject(null);\n * // => false\n */\nfunction isObject(value) {\n var type = typeof value;\n return value != null && (type == 'object' || type == 'function');\n}\n\n/**\n * Checks if `value` is object-like. A value is object-like if it's not `null`\n * and has a `typeof` result of \"object\".\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is object-like, else `false`.\n * @example\n *\n * _.isObjectLike({});\n * // => true\n *\n * _.isObjectLike([1, 2, 3]);\n * // => true\n *\n * _.isObjectLike(_.noop);\n * // => false\n *\n * _.isObjectLike(null);\n * // => false\n */\nfunction isObjectLike(value) {\n return value != null && typeof value == 'object';\n}\n\n/**\n * Checks if `value` is classified as a typed array.\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a typed array, else `false`.\n * @example\n *\n * _.isTypedArray(new Uint8Array);\n * // => true\n *\n * _.isTypedArray([]);\n * // => false\n */\nvar isTypedArray = nodeIsTypedArray ? baseUnary(nodeIsTypedArray) : baseIsTypedArray;\n\n/**\n * Creates an array of the own enumerable property names of `object`.\n *\n * **Note:** Non-object values are coerced to objects. See the\n * [ES spec](http://ecma-international.org/ecma-262/7.0/#sec-object.keys)\n * for more details.\n *\n * @static\n * @since 0.1.0\n * @memberOf _\n * @category Object\n * @param {Object} object The object to query.\n * @returns {Array} Returns the array of property names.\n * @example\n *\n * function Foo() {\n * this.a = 1;\n * this.b = 2;\n * }\n *\n * Foo.prototype.c = 3;\n *\n * _.keys(new Foo);\n * // => ['a', 'b'] (iteration order is not guaranteed)\n *\n * _.keys('hi');\n * // => ['0', '1']\n */\nfunction keys(object) {\n return isArrayLike(object) ? arrayLikeKeys(object) : baseKeys(object);\n}\n\n/**\n * This method returns a new empty array.\n *\n * @static\n * @memberOf _\n * @since 4.13.0\n * @category Util\n * @returns {Array} Returns the new empty array.\n * @example\n *\n * var arrays = _.times(2, _.stubArray);\n *\n * console.log(arrays);\n * // => [[], []]\n *\n * console.log(arrays[0] === arrays[1]);\n * // => false\n */\nfunction stubArray() {\n return [];\n}\n\n/**\n * This method returns `false`.\n *\n * @static\n * @memberOf _\n * @since 4.13.0\n * @category Util\n * @returns {boolean} Returns `false`.\n * @example\n *\n * _.times(2, _.stubFalse);\n * // => [false, false]\n */\nfunction stubFalse() {\n return false;\n}\n\nmodule.exports = isEqual;\n","var grammar = module.exports = {\n v: [{\n name: 'version',\n reg: /^(\\d*)$/\n }],\n o: [{ //o=- 20518 0 IN IP4 203.0.113.1\n // NB: sessionId will be a String in most cases because it is huge\n name: 'origin',\n reg: /^(\\S*) (\\d*) (\\d*) (\\S*) IP(\\d) (\\S*)/,\n names: ['username', 'sessionId', 'sessionVersion', 'netType', 'ipVer', 'address'],\n format: '%s %s %d %s IP%d %s'\n }],\n // default parsing of these only (though some of these feel outdated)\n s: [{ name: 'name' }],\n i: [{ name: 'description' }],\n u: [{ name: 'uri' }],\n e: [{ name: 'email' }],\n p: [{ name: 'phone' }],\n z: [{ name: 'timezones' }], // TODO: this one can actually be parsed properly..\n r: [{ name: 'repeats' }], // TODO: this one can also be parsed properly\n //k: [{}], // outdated thing ignored\n t: [{ //t=0 0\n name: 'timing',\n reg: /^(\\d*) (\\d*)/,\n names: ['start', 'stop'],\n format: '%d %d'\n }],\n c: [{ //c=IN IP4 10.47.197.26\n name: 'connection',\n reg: /^IN IP(\\d) (\\S*)/,\n names: ['version', 'ip'],\n format: 'IN IP%d %s'\n }],\n b: [{ //b=AS:4000\n push: 'bandwidth',\n reg: /^(TIAS|AS|CT|RR|RS):(\\d*)/,\n names: ['type', 'limit'],\n format: '%s:%s'\n }],\n m: [{ //m=video 51744 RTP/AVP 126 97 98 34 31\n // NB: special - pushes to session\n // TODO: rtp/fmtp should be filtered by the payloads found here?\n reg: /^(\\w*) (\\d*) ([\\w\\/]*)(?: (.*))?/,\n names: ['type', 'port', 'protocol', 'payloads'],\n format: '%s %d %s %s'\n }],\n a: [\n { //a=rtpmap:110 opus/48000/2\n push: 'rtp',\n reg: /^rtpmap:(\\d*) ([\\w\\-\\.]*)(?:\\s*\\/(\\d*)(?:\\s*\\/(\\S*))?)?/,\n names: ['payload', 'codec', 'rate', 'encoding'],\n format: function (o) {\n return (o.encoding) ?\n 'rtpmap:%d %s/%s/%s':\n o.rate ?\n 'rtpmap:%d %s/%s':\n 'rtpmap:%d %s';\n }\n },\n { //a=fmtp:108 profile-level-id=24;object=23;bitrate=64000\n //a=fmtp:111 minptime=10; useinbandfec=1\n push: 'fmtp',\n reg: /^fmtp:(\\d*) ([\\S| ]*)/,\n names: ['payload', 'config'],\n format: 'fmtp:%d %s'\n },\n { //a=control:streamid=0\n name: 'control',\n reg: /^control:(.*)/,\n format: 'control:%s'\n },\n { //a=rtcp:65179 IN IP4 193.84.77.194\n name: 'rtcp',\n reg: /^rtcp:(\\d*)(?: (\\S*) IP(\\d) (\\S*))?/,\n names: ['port', 'netType', 'ipVer', 'address'],\n format: function (o) {\n return (o.address != null) ?\n 'rtcp:%d %s IP%d %s':\n 'rtcp:%d';\n }\n },\n { //a=rtcp-fb:98 trr-int 100\n push: 'rtcpFbTrrInt',\n reg: /^rtcp-fb:(\\*|\\d*) trr-int (\\d*)/,\n names: ['payload', 'value'],\n format: 'rtcp-fb:%d trr-int %d'\n },\n { //a=rtcp-fb:98 nack rpsi\n push: 'rtcpFb',\n reg: /^rtcp-fb:(\\*|\\d*) ([\\w-_]*)(?: ([\\w-_]*))?/,\n names: ['payload', 'type', 'subtype'],\n format: function (o) {\n return (o.subtype != null) ?\n 'rtcp-fb:%s %s %s':\n 'rtcp-fb:%s %s';\n }\n },\n { //a=extmap:2 urn:ietf:params:rtp-hdrext:toffset\n //a=extmap:1/recvonly URI-gps-string\n push: 'ext',\n reg: /^extmap:(\\d+)(?:\\/(\\w+))? (\\S*)(?: (\\S*))?/,\n names: ['value', 'direction', 'uri', 'config'],\n format: function (o) {\n return 'extmap:%d' + (o.direction ? '/%s' : '%v') + ' %s' + (o.config ? ' %s' : '');\n }\n },\n { //a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:PS1uQCVeeCFCanVmcjkpPywjNWhcYD0mXXtxaVBR|2^20|1:32\n push: 'crypto',\n reg: /^crypto:(\\d*) ([\\w_]*) (\\S*)(?: (\\S*))?/,\n names: ['id', 'suite', 'config', 'sessionConfig'],\n format: function (o) {\n return (o.sessionConfig != null) ?\n 'crypto:%d %s %s %s':\n 'crypto:%d %s %s';\n }\n },\n { //a=setup:actpass\n name: 'setup',\n reg: /^setup:(\\w*)/,\n format: 'setup:%s'\n },\n { //a=mid:1\n name: 'mid',\n reg: /^mid:([^\\s]*)/,\n format: 'mid:%s'\n },\n { //a=msid:0c8b064d-d807-43b4-b434-f92a889d8587 98178685-d409-46e0-8e16-7ef0db0db64a\n name: 'msid',\n reg: /^msid:(.*)/,\n format: 'msid:%s'\n },\n { //a=ptime:20\n name: 'ptime',\n reg: /^ptime:(\\d*)/,\n format: 'ptime:%d'\n },\n { //a=maxptime:60\n name: 'maxptime',\n reg: /^maxptime:(\\d*)/,\n format: 'maxptime:%d'\n },\n { //a=sendrecv\n name: 'direction',\n reg: /^(sendrecv|recvonly|sendonly|inactive)/\n },\n { //a=ice-lite\n name: 'icelite',\n reg: /^(ice-lite)/\n },\n { //a=ice-ufrag:F7gI\n name: 'iceUfrag',\n reg: /^ice-ufrag:(\\S*)/,\n format: 'ice-ufrag:%s'\n },\n { //a=ice-pwd:x9cml/YzichV2+XlhiMu8g\n name: 'icePwd',\n reg: /^ice-pwd:(\\S*)/,\n format: 'ice-pwd:%s'\n },\n { //a=fingerprint:SHA-1 00:11:22:33:44:55:66:77:88:99:AA:BB:CC:DD:EE:FF:00:11:22:33\n name: 'fingerprint',\n reg: /^fingerprint:(\\S*) (\\S*)/,\n names: ['type', 'hash'],\n format: 'fingerprint:%s %s'\n },\n { //a=candidate:0 1 UDP 2113667327 203.0.113.1 54400 typ host\n //a=candidate:1162875081 1 udp 2113937151 192.168.34.75 60017 typ host generation 0 network-id 3 network-cost 10\n //a=candidate:3289912957 2 udp 1845501695 193.84.77.194 60017 typ srflx raddr 192.168.34.75 rport 60017 generation 0 network-id 3 network-cost 10\n //a=candidate:229815620 1 tcp 1518280447 192.168.150.19 60017 typ host tcptype active generation 0 network-id 3 network-cost 10\n //a=candidate:3289912957 2 tcp 1845501695 193.84.77.194 60017 typ srflx raddr 192.168.34.75 rport 60017 tcptype passive generation 0 network-id 3 network-cost 10\n push:'candidates',\n reg: /^candidate:(\\S*) (\\d*) (\\S*) (\\d*) (\\S*) (\\d*) typ (\\S*)(?: raddr (\\S*) rport (\\d*))?(?: tcptype (\\S*))?(?: generation (\\d*))?(?: network-id (\\d*))?(?: network-cost (\\d*))?/,\n names: ['foundation', 'component', 'transport', 'priority', 'ip', 'port', 'type', 'raddr', 'rport', 'tcptype', 'generation', 'network-id', 'network-cost'],\n format: function (o) {\n var str = 'candidate:%s %d %s %d %s %d typ %s';\n\n str += (o.raddr != null) ? ' raddr %s rport %d' : '%v%v';\n\n // NB: candidate has three optional chunks, so %void middles one if it's missing\n str += (o.tcptype != null) ? ' tcptype %s' : '%v';\n\n if (o.generation != null) {\n str += ' generation %d';\n }\n\n str += (o['network-id'] != null) ? ' network-id %d' : '%v';\n str += (o['network-cost'] != null) ? ' network-cost %d' : '%v';\n return str;\n }\n },\n { //a=end-of-candidates (keep after the candidates line for readability)\n name: 'endOfCandidates',\n reg: /^(end-of-candidates)/\n },\n { //a=remote-candidates:1 203.0.113.1 54400 2 203.0.113.1 54401 ...\n name: 'remoteCandidates',\n reg: /^remote-candidates:(.*)/,\n format: 'remote-candidates:%s'\n },\n { //a=ice-options:google-ice\n name: 'iceOptions',\n reg: /^ice-options:(\\S*)/,\n format: 'ice-options:%s'\n },\n { //a=ssrc:2566107569 cname:t9YU8M1UxTF8Y1A1\n push: 'ssrcs',\n reg: /^ssrc:(\\d*) ([\\w_]*)(?::(.*))?/,\n names: ['id', 'attribute', 'value'],\n format: function (o) {\n var str = 'ssrc:%d';\n if (o.attribute != null) {\n str += ' %s';\n if (o.value != null) {\n str += ':%s';\n }\n }\n return str;\n }\n },\n { //a=ssrc-group:FEC 1 2\n //a=ssrc-group:FEC-FR 3004364195 1080772241\n push: 'ssrcGroups',\n // token-char = %x21 / %x23-27 / %x2A-2B / %x2D-2E / %x30-39 / %x41-5A / %x5E-7E\n reg: /^ssrc-group:([\\x21\\x23\\x24\\x25\\x26\\x27\\x2A\\x2B\\x2D\\x2E\\w]*) (.*)/,\n names: ['semantics', 'ssrcs'],\n format: 'ssrc-group:%s %s'\n },\n { //a=msid-semantic: WMS Jvlam5X3SX1OP6pn20zWogvaKJz5Hjf9OnlV\n name: 'msidSemantic',\n reg: /^msid-semantic:\\s?(\\w*) (\\S*)/,\n names: ['semantic', 'token'],\n format: 'msid-semantic: %s %s' // space after ':' is not accidental\n },\n { //a=group:BUNDLE audio video\n push: 'groups',\n reg: /^group:(\\w*) (.*)/,\n names: ['type', 'mids'],\n format: 'group:%s %s'\n },\n { //a=rtcp-mux\n name: 'rtcpMux',\n reg: /^(rtcp-mux)/\n },\n { //a=rtcp-rsize\n name: 'rtcpRsize',\n reg: /^(rtcp-rsize)/\n },\n { //a=sctpmap:5000 webrtc-datachannel 1024\n name: 'sctpmap',\n reg: /^sctpmap:([\\w_\\/]*) (\\S*)(?: (\\S*))?/,\n names: ['sctpmapNumber', 'app', 'maxMessageSize'],\n format: function (o) {\n return (o.maxMessageSize != null) ?\n 'sctpmap:%s %s %s' :\n 'sctpmap:%s %s';\n }\n },\n { //a=x-google-flag:conference\n name: 'xGoogleFlag',\n reg: /^x-google-flag:([^\\s]*)/,\n format: 'x-google-flag:%s'\n },\n { //a=rid:1 send max-width=1280;max-height=720;max-fps=30;depend=0\n push: 'rids',\n reg: /^rid:([\\d\\w]+) (\\w+)(?: ([\\S| ]*))?/,\n names: ['id', 'direction', 'params'],\n format: function (o) {\n return (o.params) ? 'rid:%s %s %s' : 'rid:%s %s';\n }\n },\n { //a=imageattr:97 send [x=800,y=640,sar=1.1,q=0.6] [x=480,y=320] recv [x=330,y=250]\n //a=imageattr:* send [x=800,y=640] recv *\n //a=imageattr:100 recv [x=320,y=240]\n push: 'imageattrs',\n reg: new RegExp(\n //a=imageattr:97\n '^imageattr:(\\\\d+|\\\\*)' +\n //send [x=800,y=640,sar=1.1,q=0.6] [x=480,y=320]\n '[\\\\s\\\\t]+(send|recv)[\\\\s\\\\t]+(\\\\*|\\\\[\\\\S+\\\\](?:[\\\\s\\\\t]+\\\\[\\\\S+\\\\])*)' +\n //recv [x=330,y=250]\n '(?:[\\\\s\\\\t]+(recv|send)[\\\\s\\\\t]+(\\\\*|\\\\[\\\\S+\\\\](?:[\\\\s\\\\t]+\\\\[\\\\S+\\\\])*))?'\n ),\n names: ['pt', 'dir1', 'attrs1', 'dir2', 'attrs2'],\n format: function (o) {\n return 'imageattr:%s %s %s' + (o.dir2 ? ' %s %s' : '');\n }\n },\n { //a=simulcast:send 1,2,3;~4,~5 recv 6;~7,~8\n //a=simulcast:recv 1;4,5 send 6;7\n name: 'simulcast',\n reg: new RegExp(\n //a=simulcast:\n '^simulcast:' +\n //send 1,2,3;~4,~5\n '(send|recv) ([a-zA-Z0-9\\\\-_~;,]+)' +\n //space + recv 6;~7,~8\n '(?:\\\\s?(send|recv) ([a-zA-Z0-9\\\\-_~;,]+))?' +\n //end\n '$'\n ),\n names: ['dir1', 'list1', 'dir2', 'list2'],\n format: function (o) {\n return 'simulcast:%s %s' + (o.dir2 ? ' %s %s' : '');\n }\n },\n { //Old simulcast draft 03 (implemented by Firefox)\n // https://tools.ietf.org/html/draft-ietf-mmusic-sdp-simulcast-03\n //a=simulcast: recv pt=97;98 send pt=97\n //a=simulcast: send rid=5;6;7 paused=6,7\n name: 'simulcast_03',\n reg: /^simulcast:[\\s\\t]+([\\S+\\s\\t]+)$/,\n names: ['value'],\n format: 'simulcast: %s'\n },\n {\n //a=framerate:25\n //a=framerate:29.97\n name: 'framerate',\n reg: /^framerate:(\\d+(?:$|\\.\\d+))/,\n format: 'framerate:%s'\n },\n { // any a= that we don't understand is kepts verbatim on media.invalid\n push: 'invalid',\n names: ['value']\n }\n ]\n};\n\n// set sensible defaults to avoid polluting the grammar with boring details\nObject.keys(grammar).forEach(function (key) {\n var objs = grammar[key];\n objs.forEach(function (obj) {\n if (!obj.reg) {\n obj.reg = /(.*)/;\n }\n if (!obj.format) {\n obj.format = '%s';\n }\n });\n});\n","var parser = require('./parser');\nvar writer = require('./writer');\n\nexports.write = writer;\nexports.parse = parser.parse;\nexports.parseFmtpConfig = parser.parseFmtpConfig;\nexports.parseParams = parser.parseParams;\nexports.parsePayloads = parser.parsePayloads;\nexports.parseRemoteCandidates = parser.parseRemoteCandidates;\nexports.parseImageAttributes = parser.parseImageAttributes;\nexports.parseSimulcastStreamList = parser.parseSimulcastStreamList;\n","var toIntIfInt = function (v) {\n return String(Number(v)) === v ? Number(v) : v;\n};\n\nvar attachProperties = function (match, location, names, rawName) {\n if (rawName && !names) {\n location[rawName] = toIntIfInt(match[1]);\n }\n else {\n for (var i = 0; i < names.length; i += 1) {\n if (match[i+1] != null) {\n location[names[i]] = toIntIfInt(match[i+1]);\n }\n }\n }\n};\n\nvar parseReg = function (obj, location, content) {\n var needsBlank = obj.name && obj.names;\n if (obj.push && !location[obj.push]) {\n location[obj.push] = [];\n }\n else if (needsBlank && !location[obj.name]) {\n location[obj.name] = {};\n }\n var keyLocation = obj.push ?\n {} : // blank object that will be pushed\n needsBlank ? location[obj.name] : location; // otherwise, named location or root\n\n attachProperties(content.match(obj.reg), keyLocation, obj.names, obj.name);\n\n if (obj.push) {\n location[obj.push].push(keyLocation);\n }\n};\n\nvar grammar = require('./grammar');\nvar validLine = RegExp.prototype.test.bind(/^([a-z])=(.*)/);\n\nexports.parse = function (sdp) {\n var session = {}\n , media = []\n , location = session; // points at where properties go under (one of the above)\n\n // parse lines we understand\n sdp.split(/(\\r\\n|\\r|\\n)/).filter(validLine).forEach(function (l) {\n var type = l[0];\n var content = l.slice(2);\n if (type === 'm') {\n media.push({rtp: [], fmtp: []});\n location = media[media.length-1]; // point at latest media line\n }\n\n for (var j = 0; j < (grammar[type] || []).length; j += 1) {\n var obj = grammar[type][j];\n if (obj.reg.test(content)) {\n return parseReg(obj, location, content);\n }\n }\n });\n\n session.media = media; // link it up\n return session;\n};\n\nvar paramReducer = function (acc, expr) {\n var s = expr.split(/=(.+)/, 2);\n if (s.length === 2) {\n acc[s[0]] = toIntIfInt(s[1]);\n }\n return acc;\n};\n\nexports.parseParams = function (str) {\n return str.split(/\\;\\s?/).reduce(paramReducer, {});\n};\n\n// For backward compatibility - alias will be removed in 3.0.0\nexports.parseFmtpConfig = exports.parseParams;\n\nexports.parsePayloads = function (str) {\n return str.split(' ').map(Number);\n};\n\nexports.parseRemoteCandidates = function (str) {\n var candidates = [];\n var parts = str.split(' ').map(toIntIfInt);\n for (var i = 0; i < parts.length; i += 3) {\n candidates.push({\n component: parts[i],\n ip: parts[i + 1],\n port: parts[i + 2]\n });\n }\n return candidates;\n};\n\nexports.parseImageAttributes = function (str) {\n return str.split(' ').map(function (item) {\n return item.substring(1, item.length-1).split(',').reduce(paramReducer, {});\n });\n};\n\nexports.parseSimulcastStreamList = function (str) {\n return str.split(';').map(function (stream) {\n return stream.split(',').map(function (format) {\n var scid, paused = false;\n\n if (format[0] !== '~') {\n scid = toIntIfInt(format);\n } else {\n scid = toIntIfInt(format.substring(1, format.length));\n paused = true;\n }\n\n return {\n scid: scid,\n paused: paused\n };\n });\n });\n};\n","var grammar = require('./grammar');\n\n// customized util.format - discards excess arguments and can void middle ones\nvar formatRegExp = /%[sdv%]/g;\nvar format = function (formatStr) {\n var i = 1;\n var args = arguments;\n var len = args.length;\n return formatStr.replace(formatRegExp, function (x) {\n if (i >= len) {\n return x; // missing argument\n }\n var arg = args[i];\n i += 1;\n switch (x) {\n case '%%':\n return '%';\n case '%s':\n return String(arg);\n case '%d':\n return Number(arg);\n case '%v':\n return '';\n }\n });\n // NB: we discard excess arguments - they are typically undefined from makeLine\n};\n\nvar makeLine = function (type, obj, location) {\n var str = obj.format instanceof Function ?\n (obj.format(obj.push ? location : location[obj.name])) :\n obj.format;\n\n var args = [type + '=' + str];\n if (obj.names) {\n for (var i = 0; i < obj.names.length; i += 1) {\n var n = obj.names[i];\n if (obj.name) {\n args.push(location[obj.name][n]);\n }\n else { // for mLine and push attributes\n args.push(location[obj.names[i]]);\n }\n }\n }\n else {\n args.push(location[obj.name]);\n }\n return format.apply(null, args);\n};\n\n// RFC specified order\n// TODO: extend this with all the rest\nvar defaultOuterOrder = [\n 'v', 'o', 's', 'i',\n 'u', 'e', 'p', 'c',\n 'b', 't', 'r', 'z', 'a'\n];\nvar defaultInnerOrder = ['i', 'c', 'b', 'a'];\n\n\nmodule.exports = function (session, opts) {\n opts = opts || {};\n // ensure certain properties exist\n if (session.version == null) {\n session.version = 0; // 'v=0' must be there (only defined version atm)\n }\n if (session.name == null) {\n session.name = ' '; // 's= ' must be there if no meaningful name set\n }\n session.media.forEach(function (mLine) {\n if (mLine.payloads == null) {\n mLine.payloads = '';\n }\n });\n\n var outerOrder = opts.outerOrder || defaultOuterOrder;\n var innerOrder = opts.innerOrder || defaultInnerOrder;\n var sdp = [];\n\n // loop through outerOrder for matching properties on session\n outerOrder.forEach(function (type) {\n grammar[type].forEach(function (obj) {\n if (obj.name in session && session[obj.name] != null) {\n sdp.push(makeLine(type, obj, session));\n }\n else if (obj.push in session && session[obj.push] != null) {\n session[obj.push].forEach(function (el) {\n sdp.push(makeLine(type, obj, el));\n });\n }\n });\n });\n\n // then for each media line, follow the innerOrder\n session.media.forEach(function (mLine) {\n sdp.push(makeLine('m', grammar.m[0], mLine));\n\n innerOrder.forEach(function (type) {\n grammar[type].forEach(function (obj) {\n if (obj.name in mLine && mLine[obj.name] != null) {\n sdp.push(makeLine(type, obj, mLine));\n }\n else if (obj.push in mLine && mLine[obj.push] != null) {\n mLine[obj.push].forEach(function (el) {\n sdp.push(makeLine(type, obj, el));\n });\n }\n });\n });\n });\n\n return sdp.join('\\r\\n') + '\\r\\n';\n};\n","/* eslint-env node */\n'use strict';\n\n// SDP helpers.\nconst SDPUtils = {};\n\n// Generate an alphanumeric identifier for cname or mids.\n// TODO: use UUIDs instead? https://gist.github.com/jed/982883\nSDPUtils.generateIdentifier = function() {\n return Math.random().toString(36).substr(2, 10);\n};\n\n// The RTCP CNAME used by all peerconnections from the same JS.\nSDPUtils.localCName = SDPUtils.generateIdentifier();\n\n// Splits SDP into lines, dealing with both CRLF and LF.\nSDPUtils.splitLines = function(blob) {\n return blob.trim().split('\\n').map(line => line.trim());\n};\n// Splits SDP into sessionpart and mediasections. Ensures CRLF.\nSDPUtils.splitSections = function(blob) {\n const parts = blob.split('\\nm=');\n return parts.map((part, index) => (index > 0 ?\n 'm=' + part : part).trim() + '\\r\\n');\n};\n\n// Returns the session description.\nSDPUtils.getDescription = function(blob) {\n const sections = SDPUtils.splitSections(blob);\n return sections && sections[0];\n};\n\n// Returns the individual media sections.\nSDPUtils.getMediaSections = function(blob) {\n const sections = SDPUtils.splitSections(blob);\n sections.shift();\n return sections;\n};\n\n// Returns lines that start with a certain prefix.\nSDPUtils.matchPrefix = function(blob, prefix) {\n return SDPUtils.splitLines(blob).filter(line => line.indexOf(prefix) === 0);\n};\n\n// Parses an ICE candidate line. Sample input:\n// candidate:702786350 2 udp 41819902 8.8.8.8 60769 typ relay raddr 8.8.8.8\n// rport 55996\"\n// Input can be prefixed with a=.\nSDPUtils.parseCandidate = function(line) {\n let parts;\n // Parse both variants.\n if (line.indexOf('a=candidate:') === 0) {\n parts = line.substring(12).split(' ');\n } else {\n parts = line.substring(10).split(' ');\n }\n\n const candidate = {\n foundation: parts[0],\n component: {1: 'rtp', 2: 'rtcp'}[parts[1]] || parts[1],\n protocol: parts[2].toLowerCase(),\n priority: parseInt(parts[3], 10),\n ip: parts[4],\n address: parts[4], // address is an alias for ip.\n port: parseInt(parts[5], 10),\n // skip parts[6] == 'typ'\n type: parts[7],\n };\n\n for (let i = 8; i < parts.length; i += 2) {\n switch (parts[i]) {\n case 'raddr':\n candidate.relatedAddress = parts[i + 1];\n break;\n case 'rport':\n candidate.relatedPort = parseInt(parts[i + 1], 10);\n break;\n case 'tcptype':\n candidate.tcpType = parts[i + 1];\n break;\n case 'ufrag':\n candidate.ufrag = parts[i + 1]; // for backward compatibility.\n candidate.usernameFragment = parts[i + 1];\n break;\n default: // extension handling, in particular ufrag. Don't overwrite.\n if (candidate[parts[i]] === undefined) {\n candidate[parts[i]] = parts[i + 1];\n }\n break;\n }\n }\n return candidate;\n};\n\n// Translates a candidate object into SDP candidate attribute.\n// This does not include the a= prefix!\nSDPUtils.writeCandidate = function(candidate) {\n const sdp = [];\n sdp.push(candidate.foundation);\n\n const component = candidate.component;\n if (component === 'rtp') {\n sdp.push(1);\n } else if (component === 'rtcp') {\n sdp.push(2);\n } else {\n sdp.push(component);\n }\n sdp.push(candidate.protocol.toUpperCase());\n sdp.push(candidate.priority);\n sdp.push(candidate.address || candidate.ip);\n sdp.push(candidate.port);\n\n const type = candidate.type;\n sdp.push('typ');\n sdp.push(type);\n if (type !== 'host' && candidate.relatedAddress &&\n candidate.relatedPort) {\n sdp.push('raddr');\n sdp.push(candidate.relatedAddress);\n sdp.push('rport');\n sdp.push(candidate.relatedPort);\n }\n if (candidate.tcpType && candidate.protocol.toLowerCase() === 'tcp') {\n sdp.push('tcptype');\n sdp.push(candidate.tcpType);\n }\n if (candidate.usernameFragment || candidate.ufrag) {\n sdp.push('ufrag');\n sdp.push(candidate.usernameFragment || candidate.ufrag);\n }\n return 'candidate:' + sdp.join(' ');\n};\n\n// Parses an ice-options line, returns an array of option tags.\n// Sample input:\n// a=ice-options:foo bar\nSDPUtils.parseIceOptions = function(line) {\n return line.substr(14).split(' ');\n};\n\n// Parses a rtpmap line, returns RTCRtpCoddecParameters. Sample input:\n// a=rtpmap:111 opus/48000/2\nSDPUtils.parseRtpMap = function(line) {\n let parts = line.substr(9).split(' ');\n const parsed = {\n payloadType: parseInt(parts.shift(), 10), // was: id\n };\n\n parts = parts[0].split('/');\n\n parsed.name = parts[0];\n parsed.clockRate = parseInt(parts[1], 10); // was: clockrate\n parsed.channels = parts.length === 3 ? parseInt(parts[2], 10) : 1;\n // legacy alias, got renamed back to channels in ORTC.\n parsed.numChannels = parsed.channels;\n return parsed;\n};\n\n// Generates a rtpmap line from RTCRtpCodecCapability or\n// RTCRtpCodecParameters.\nSDPUtils.writeRtpMap = function(codec) {\n let pt = codec.payloadType;\n if (codec.preferredPayloadType !== undefined) {\n pt = codec.preferredPayloadType;\n }\n const channels = codec.channels || codec.numChannels || 1;\n return 'a=rtpmap:' + pt + ' ' + codec.name + '/' + codec.clockRate +\n (channels !== 1 ? '/' + channels : '') + '\\r\\n';\n};\n\n// Parses a extmap line (headerextension from RFC 5285). Sample input:\n// a=extmap:2 urn:ietf:params:rtp-hdrext:toffset\n// a=extmap:2/sendonly urn:ietf:params:rtp-hdrext:toffset\nSDPUtils.parseExtmap = function(line) {\n const parts = line.substr(9).split(' ');\n return {\n id: parseInt(parts[0], 10),\n direction: parts[0].indexOf('/') > 0 ? parts[0].split('/')[1] : 'sendrecv',\n uri: parts[1],\n };\n};\n\n// Generates an extmap line from RTCRtpHeaderExtensionParameters or\n// RTCRtpHeaderExtension.\nSDPUtils.writeExtmap = function(headerExtension) {\n return 'a=extmap:' + (headerExtension.id || headerExtension.preferredId) +\n (headerExtension.direction && headerExtension.direction !== 'sendrecv'\n ? '/' + headerExtension.direction\n : '') +\n ' ' + headerExtension.uri + '\\r\\n';\n};\n\n// Parses a fmtp line, returns dictionary. Sample input:\n// a=fmtp:96 vbr=on;cng=on\n// Also deals with vbr=on; cng=on\nSDPUtils.parseFmtp = function(line) {\n const parsed = {};\n let kv;\n const parts = line.substr(line.indexOf(' ') + 1).split(';');\n for (let j = 0; j < parts.length; j++) {\n kv = parts[j].trim().split('=');\n parsed[kv[0].trim()] = kv[1];\n }\n return parsed;\n};\n\n// Generates a fmtp line from RTCRtpCodecCapability or RTCRtpCodecParameters.\nSDPUtils.writeFmtp = function(codec) {\n let line = '';\n let pt = codec.payloadType;\n if (codec.preferredPayloadType !== undefined) {\n pt = codec.preferredPayloadType;\n }\n if (codec.parameters && Object.keys(codec.parameters).length) {\n const params = [];\n Object.keys(codec.parameters).forEach(param => {\n if (codec.parameters[param] !== undefined) {\n params.push(param + '=' + codec.parameters[param]);\n } else {\n params.push(param);\n }\n });\n line += 'a=fmtp:' + pt + ' ' + params.join(';') + '\\r\\n';\n }\n return line;\n};\n\n// Parses a rtcp-fb line, returns RTCPRtcpFeedback object. Sample input:\n// a=rtcp-fb:98 nack rpsi\nSDPUtils.parseRtcpFb = function(line) {\n const parts = line.substr(line.indexOf(' ') + 1).split(' ');\n return {\n type: parts.shift(),\n parameter: parts.join(' '),\n };\n};\n\n// Generate a=rtcp-fb lines from RTCRtpCodecCapability or RTCRtpCodecParameters.\nSDPUtils.writeRtcpFb = function(codec) {\n let lines = '';\n let pt = codec.payloadType;\n if (codec.preferredPayloadType !== undefined) {\n pt = codec.preferredPayloadType;\n }\n if (codec.rtcpFeedback && codec.rtcpFeedback.length) {\n // FIXME: special handling for trr-int?\n codec.rtcpFeedback.forEach(fb => {\n lines += 'a=rtcp-fb:' + pt + ' ' + fb.type +\n (fb.parameter && fb.parameter.length ? ' ' + fb.parameter : '') +\n '\\r\\n';\n });\n }\n return lines;\n};\n\n// Parses a RFC 5576 ssrc media attribute. Sample input:\n// a=ssrc:3735928559 cname:something\nSDPUtils.parseSsrcMedia = function(line) {\n const sp = line.indexOf(' ');\n const parts = {\n ssrc: parseInt(line.substr(7, sp - 7), 10),\n };\n const colon = line.indexOf(':', sp);\n if (colon > -1) {\n parts.attribute = line.substr(sp + 1, colon - sp - 1);\n parts.value = line.substr(colon + 1);\n } else {\n parts.attribute = line.substr(sp + 1);\n }\n return parts;\n};\n\n// Parse a ssrc-group line (see RFC 5576). Sample input:\n// a=ssrc-group:semantics 12 34\nSDPUtils.parseSsrcGroup = function(line) {\n const parts = line.substr(13).split(' ');\n return {\n semantics: parts.shift(),\n ssrcs: parts.map(ssrc => parseInt(ssrc, 10)),\n };\n};\n\n// Extracts the MID (RFC 5888) from a media section.\n// Returns the MID or undefined if no mid line was found.\nSDPUtils.getMid = function(mediaSection) {\n const mid = SDPUtils.matchPrefix(mediaSection, 'a=mid:')[0];\n if (mid) {\n return mid.substr(6);\n }\n};\n\n// Parses a fingerprint line for DTLS-SRTP.\nSDPUtils.parseFingerprint = function(line) {\n const parts = line.substr(14).split(' ');\n return {\n algorithm: parts[0].toLowerCase(), // algorithm is case-sensitive in Edge.\n value: parts[1].toUpperCase(), // the definition is upper-case in RFC 4572.\n };\n};\n\n// Extracts DTLS parameters from SDP media section or sessionpart.\n// FIXME: for consistency with other functions this should only\n// get the fingerprint line as input. See also getIceParameters.\nSDPUtils.getDtlsParameters = function(mediaSection, sessionpart) {\n const lines = SDPUtils.matchPrefix(mediaSection + sessionpart,\n 'a=fingerprint:');\n // Note: a=setup line is ignored since we use the 'auto' role in Edge.\n return {\n role: 'auto',\n fingerprints: lines.map(SDPUtils.parseFingerprint),\n };\n};\n\n// Serializes DTLS parameters to SDP.\nSDPUtils.writeDtlsParameters = function(params, setupType) {\n let sdp = 'a=setup:' + setupType + '\\r\\n';\n params.fingerprints.forEach(fp => {\n sdp += 'a=fingerprint:' + fp.algorithm + ' ' + fp.value + '\\r\\n';\n });\n return sdp;\n};\n\n// Parses a=crypto lines into\n// https://rawgit.com/aboba/edgertc/master/msortc-rs4.html#dictionary-rtcsrtpsdesparameters-members\nSDPUtils.parseCryptoLine = function(line) {\n const parts = line.substr(9).split(' ');\n return {\n tag: parseInt(parts[0], 10),\n cryptoSuite: parts[1],\n keyParams: parts[2],\n sessionParams: parts.slice(3),\n };\n};\n\nSDPUtils.writeCryptoLine = function(parameters) {\n return 'a=crypto:' + parameters.tag + ' ' +\n parameters.cryptoSuite + ' ' +\n (typeof parameters.keyParams === 'object'\n ? SDPUtils.writeCryptoKeyParams(parameters.keyParams)\n : parameters.keyParams) +\n (parameters.sessionParams ? ' ' + parameters.sessionParams.join(' ') : '') +\n '\\r\\n';\n};\n\n// Parses the crypto key parameters into\n// https://rawgit.com/aboba/edgertc/master/msortc-rs4.html#rtcsrtpkeyparam*\nSDPUtils.parseCryptoKeyParams = function(keyParams) {\n if (keyParams.indexOf('inline:') !== 0) {\n return null;\n }\n const parts = keyParams.substr(7).split('|');\n return {\n keyMethod: 'inline',\n keySalt: parts[0],\n lifeTime: parts[1],\n mkiValue: parts[2] ? parts[2].split(':')[0] : undefined,\n mkiLength: parts[2] ? parts[2].split(':')[1] : undefined,\n };\n};\n\nSDPUtils.writeCryptoKeyParams = function(keyParams) {\n return keyParams.keyMethod + ':'\n + keyParams.keySalt +\n (keyParams.lifeTime ? '|' + keyParams.lifeTime : '') +\n (keyParams.mkiValue && keyParams.mkiLength\n ? '|' + keyParams.mkiValue + ':' + keyParams.mkiLength\n : '');\n};\n\n// Extracts all SDES parameters.\nSDPUtils.getCryptoParameters = function(mediaSection, sessionpart) {\n const lines = SDPUtils.matchPrefix(mediaSection + sessionpart,\n 'a=crypto:');\n return lines.map(SDPUtils.parseCryptoLine);\n};\n\n// Parses ICE information from SDP media section or sessionpart.\n// FIXME: for consistency with other functions this should only\n// get the ice-ufrag and ice-pwd lines as input.\nSDPUtils.getIceParameters = function(mediaSection, sessionpart) {\n const ufrag = SDPUtils.matchPrefix(mediaSection + sessionpart,\n 'a=ice-ufrag:')[0];\n const pwd = SDPUtils.matchPrefix(mediaSection + sessionpart,\n 'a=ice-pwd:')[0];\n if (!(ufrag && pwd)) {\n return null;\n }\n return {\n usernameFragment: ufrag.substr(12),\n password: pwd.substr(10),\n };\n};\n\n// Serializes ICE parameters to SDP.\nSDPUtils.writeIceParameters = function(params) {\n let sdp = 'a=ice-ufrag:' + params.usernameFragment + '\\r\\n' +\n 'a=ice-pwd:' + params.password + '\\r\\n';\n if (params.iceLite) {\n sdp += 'a=ice-lite\\r\\n';\n }\n return sdp;\n};\n\n// Parses the SDP media section and returns RTCRtpParameters.\nSDPUtils.parseRtpParameters = function(mediaSection) {\n const description = {\n codecs: [],\n headerExtensions: [],\n fecMechanisms: [],\n rtcp: [],\n };\n const lines = SDPUtils.splitLines(mediaSection);\n const mline = lines[0].split(' ');\n for (let i = 3; i < mline.length; i++) { // find all codecs from mline[3..]\n const pt = mline[i];\n const rtpmapline = SDPUtils.matchPrefix(\n mediaSection, 'a=rtpmap:' + pt + ' ')[0];\n if (rtpmapline) {\n const codec = SDPUtils.parseRtpMap(rtpmapline);\n const fmtps = SDPUtils.matchPrefix(\n mediaSection, 'a=fmtp:' + pt + ' ');\n // Only the first a=fmtp: is considered.\n codec.parameters = fmtps.length ? SDPUtils.parseFmtp(fmtps[0]) : {};\n codec.rtcpFeedback = SDPUtils.matchPrefix(\n mediaSection, 'a=rtcp-fb:' + pt + ' ')\n .map(SDPUtils.parseRtcpFb);\n description.codecs.push(codec);\n // parse FEC mechanisms from rtpmap lines.\n switch (codec.name.toUpperCase()) {\n case 'RED':\n case 'ULPFEC':\n description.fecMechanisms.push(codec.name.toUpperCase());\n break;\n default: // only RED and ULPFEC are recognized as FEC mechanisms.\n break;\n }\n }\n }\n SDPUtils.matchPrefix(mediaSection, 'a=extmap:').forEach(line => {\n description.headerExtensions.push(SDPUtils.parseExtmap(line));\n });\n // FIXME: parse rtcp.\n return description;\n};\n\n// Generates parts of the SDP media section describing the capabilities /\n// parameters.\nSDPUtils.writeRtpDescription = function(kind, caps) {\n let sdp = '';\n\n // Build the mline.\n sdp += 'm=' + kind + ' ';\n sdp += caps.codecs.length > 0 ? '9' : '0'; // reject if no codecs.\n sdp += ' UDP/TLS/RTP/SAVPF ';\n sdp += caps.codecs.map(codec => {\n if (codec.preferredPayloadType !== undefined) {\n return codec.preferredPayloadType;\n }\n return codec.payloadType;\n }).join(' ') + '\\r\\n';\n\n sdp += 'c=IN IP4 0.0.0.0\\r\\n';\n sdp += 'a=rtcp:9 IN IP4 0.0.0.0\\r\\n';\n\n // Add a=rtpmap lines for each codec. Also fmtp and rtcp-fb.\n caps.codecs.forEach(codec => {\n sdp += SDPUtils.writeRtpMap(codec);\n sdp += SDPUtils.writeFmtp(codec);\n sdp += SDPUtils.writeRtcpFb(codec);\n });\n let maxptime = 0;\n caps.codecs.forEach(codec => {\n if (codec.maxptime > maxptime) {\n maxptime = codec.maxptime;\n }\n });\n if (maxptime > 0) {\n sdp += 'a=maxptime:' + maxptime + '\\r\\n';\n }\n\n if (caps.headerExtensions) {\n caps.headerExtensions.forEach(extension => {\n sdp += SDPUtils.writeExtmap(extension);\n });\n }\n // FIXME: write fecMechanisms.\n return sdp;\n};\n\n// Parses the SDP media section and returns an array of\n// RTCRtpEncodingParameters.\nSDPUtils.parseRtpEncodingParameters = function(mediaSection) {\n const encodingParameters = [];\n const description = SDPUtils.parseRtpParameters(mediaSection);\n const hasRed = description.fecMechanisms.indexOf('RED') !== -1;\n const hasUlpfec = description.fecMechanisms.indexOf('ULPFEC') !== -1;\n\n // filter a=ssrc:... cname:, ignore PlanB-msid\n const ssrcs = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:')\n .map(line => SDPUtils.parseSsrcMedia(line))\n .filter(parts => parts.attribute === 'cname');\n const primarySsrc = ssrcs.length > 0 && ssrcs[0].ssrc;\n let secondarySsrc;\n\n const flows = SDPUtils.matchPrefix(mediaSection, 'a=ssrc-group:FID')\n .map(line => {\n const parts = line.substr(17).split(' ');\n return parts.map(part => parseInt(part, 10));\n });\n if (flows.length > 0 && flows[0].length > 1 && flows[0][0] === primarySsrc) {\n secondarySsrc = flows[0][1];\n }\n\n description.codecs.forEach(codec => {\n if (codec.name.toUpperCase() === 'RTX' && codec.parameters.apt) {\n let encParam = {\n ssrc: primarySsrc,\n codecPayloadType: parseInt(codec.parameters.apt, 10),\n };\n if (primarySsrc && secondarySsrc) {\n encParam.rtx = {ssrc: secondarySsrc};\n }\n encodingParameters.push(encParam);\n if (hasRed) {\n encParam = JSON.parse(JSON.stringify(encParam));\n encParam.fec = {\n ssrc: primarySsrc,\n mechanism: hasUlpfec ? 'red+ulpfec' : 'red',\n };\n encodingParameters.push(encParam);\n }\n }\n });\n if (encodingParameters.length === 0 && primarySsrc) {\n encodingParameters.push({\n ssrc: primarySsrc,\n });\n }\n\n // we support both b=AS and b=TIAS but interpret AS as TIAS.\n let bandwidth = SDPUtils.matchPrefix(mediaSection, 'b=');\n if (bandwidth.length) {\n if (bandwidth[0].indexOf('b=TIAS:') === 0) {\n bandwidth = parseInt(bandwidth[0].substr(7), 10);\n } else if (bandwidth[0].indexOf('b=AS:') === 0) {\n // use formula from JSEP to convert b=AS to TIAS value.\n bandwidth = parseInt(bandwidth[0].substr(5), 10) * 1000 * 0.95\n - (50 * 40 * 8);\n } else {\n bandwidth = undefined;\n }\n encodingParameters.forEach(params => {\n params.maxBitrate = bandwidth;\n });\n }\n return encodingParameters;\n};\n\n// parses http://draft.ortc.org/#rtcrtcpparameters*\nSDPUtils.parseRtcpParameters = function(mediaSection) {\n const rtcpParameters = {};\n\n // Gets the first SSRC. Note that with RTX there might be multiple\n // SSRCs.\n const remoteSsrc = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:')\n .map(line => SDPUtils.parseSsrcMedia(line))\n .filter(obj => obj.attribute === 'cname')[0];\n if (remoteSsrc) {\n rtcpParameters.cname = remoteSsrc.value;\n rtcpParameters.ssrc = remoteSsrc.ssrc;\n }\n\n // Edge uses the compound attribute instead of reducedSize\n // compound is !reducedSize\n const rsize = SDPUtils.matchPrefix(mediaSection, 'a=rtcp-rsize');\n rtcpParameters.reducedSize = rsize.length > 0;\n rtcpParameters.compound = rsize.length === 0;\n\n // parses the rtcp-mux attrіbute.\n // Note that Edge does not support unmuxed RTCP.\n const mux = SDPUtils.matchPrefix(mediaSection, 'a=rtcp-mux');\n rtcpParameters.mux = mux.length > 0;\n\n return rtcpParameters;\n};\n\nSDPUtils.writeRtcpParameters = function(rtcpParameters) {\n let sdp = '';\n if (rtcpParameters.reducedSize) {\n sdp += 'a=rtcp-rsize\\r\\n';\n }\n if (rtcpParameters.mux) {\n sdp += 'a=rtcp-mux\\r\\n';\n }\n if (rtcpParameters.ssrc !== undefined && rtcpParameters.cname) {\n sdp += 'a=ssrc:' + rtcpParameters.ssrc +\n ' cname:' + rtcpParameters.cname + '\\r\\n';\n }\n return sdp;\n};\n\n\n// parses either a=msid: or a=ssrc:... msid lines and returns\n// the id of the MediaStream and MediaStreamTrack.\nSDPUtils.parseMsid = function(mediaSection) {\n let parts;\n const spec = SDPUtils.matchPrefix(mediaSection, 'a=msid:');\n if (spec.length === 1) {\n parts = spec[0].substr(7).split(' ');\n return {stream: parts[0], track: parts[1]};\n }\n const planB = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:')\n .map(line => SDPUtils.parseSsrcMedia(line))\n .filter(msidParts => msidParts.attribute === 'msid');\n if (planB.length > 0) {\n parts = planB[0].value.split(' ');\n return {stream: parts[0], track: parts[1]};\n }\n};\n\n// SCTP\n// parses draft-ietf-mmusic-sctp-sdp-26 first and falls back\n// to draft-ietf-mmusic-sctp-sdp-05\nSDPUtils.parseSctpDescription = function(mediaSection) {\n const mline = SDPUtils.parseMLine(mediaSection);\n const maxSizeLine = SDPUtils.matchPrefix(mediaSection, 'a=max-message-size:');\n let maxMessageSize;\n if (maxSizeLine.length > 0) {\n maxMessageSize = parseInt(maxSizeLine[0].substr(19), 10);\n }\n if (isNaN(maxMessageSize)) {\n maxMessageSize = 65536;\n }\n const sctpPort = SDPUtils.matchPrefix(mediaSection, 'a=sctp-port:');\n if (sctpPort.length > 0) {\n return {\n port: parseInt(sctpPort[0].substr(12), 10),\n protocol: mline.fmt,\n maxMessageSize,\n };\n }\n const sctpMapLines = SDPUtils.matchPrefix(mediaSection, 'a=sctpmap:');\n if (sctpMapLines.length > 0) {\n const parts = sctpMapLines[0]\n .substr(10)\n .split(' ');\n return {\n port: parseInt(parts[0], 10),\n protocol: parts[1],\n maxMessageSize,\n };\n }\n};\n\n// SCTP\n// outputs the draft-ietf-mmusic-sctp-sdp-26 version that all browsers\n// support by now receiving in this format, unless we originally parsed\n// as the draft-ietf-mmusic-sctp-sdp-05 format (indicated by the m-line\n// protocol of DTLS/SCTP -- without UDP/ or TCP/)\nSDPUtils.writeSctpDescription = function(media, sctp) {\n let output = [];\n if (media.protocol !== 'DTLS/SCTP') {\n output = [\n 'm=' + media.kind + ' 9 ' + media.protocol + ' ' + sctp.protocol + '\\r\\n',\n 'c=IN IP4 0.0.0.0\\r\\n',\n 'a=sctp-port:' + sctp.port + '\\r\\n',\n ];\n } else {\n output = [\n 'm=' + media.kind + ' 9 ' + media.protocol + ' ' + sctp.port + '\\r\\n',\n 'c=IN IP4 0.0.0.0\\r\\n',\n 'a=sctpmap:' + sctp.port + ' ' + sctp.protocol + ' 65535\\r\\n',\n ];\n }\n if (sctp.maxMessageSize !== undefined) {\n output.push('a=max-message-size:' + sctp.maxMessageSize + '\\r\\n');\n }\n return output.join('');\n};\n\n// Generate a session ID for SDP.\n// https://tools.ietf.org/html/draft-ietf-rtcweb-jsep-20#section-5.2.1\n// recommends using a cryptographically random +ve 64-bit value\n// but right now this should be acceptable and within the right range\nSDPUtils.generateSessionId = function() {\n return Math.random().toString().substr(2, 21);\n};\n\n// Write boiler plate for start of SDP\n// sessId argument is optional - if not supplied it will\n// be generated randomly\n// sessVersion is optional and defaults to 2\n// sessUser is optional and defaults to 'thisisadapterortc'\nSDPUtils.writeSessionBoilerplate = function(sessId, sessVer, sessUser) {\n let sessionId;\n const version = sessVer !== undefined ? sessVer : 2;\n if (sessId) {\n sessionId = sessId;\n } else {\n sessionId = SDPUtils.generateSessionId();\n }\n const user = sessUser || 'thisisadapterortc';\n // FIXME: sess-id should be an NTP timestamp.\n return 'v=0\\r\\n' +\n 'o=' + user + ' ' + sessionId + ' ' + version +\n ' IN IP4 127.0.0.1\\r\\n' +\n 's=-\\r\\n' +\n 't=0 0\\r\\n';\n};\n\n// Gets the direction from the mediaSection or the sessionpart.\nSDPUtils.getDirection = function(mediaSection, sessionpart) {\n // Look for sendrecv, sendonly, recvonly, inactive, default to sendrecv.\n const lines = SDPUtils.splitLines(mediaSection);\n for (let i = 0; i < lines.length; i++) {\n switch (lines[i]) {\n case 'a=sendrecv':\n case 'a=sendonly':\n case 'a=recvonly':\n case 'a=inactive':\n return lines[i].substr(2);\n default:\n // FIXME: What should happen here?\n }\n }\n if (sessionpart) {\n return SDPUtils.getDirection(sessionpart);\n }\n return 'sendrecv';\n};\n\nSDPUtils.getKind = function(mediaSection) {\n const lines = SDPUtils.splitLines(mediaSection);\n const mline = lines[0].split(' ');\n return mline[0].substr(2);\n};\n\nSDPUtils.isRejected = function(mediaSection) {\n return mediaSection.split(' ', 2)[1] === '0';\n};\n\nSDPUtils.parseMLine = function(mediaSection) {\n const lines = SDPUtils.splitLines(mediaSection);\n const parts = lines[0].substr(2).split(' ');\n return {\n kind: parts[0],\n port: parseInt(parts[1], 10),\n protocol: parts[2],\n fmt: parts.slice(3).join(' '),\n };\n};\n\nSDPUtils.parseOLine = function(mediaSection) {\n const line = SDPUtils.matchPrefix(mediaSection, 'o=')[0];\n const parts = line.substr(2).split(' ');\n return {\n username: parts[0],\n sessionId: parts[1],\n sessionVersion: parseInt(parts[2], 10),\n netType: parts[3],\n addressType: parts[4],\n address: parts[5],\n };\n};\n\n// a very naive interpretation of a valid SDP.\nSDPUtils.isValidSDP = function(blob) {\n if (typeof blob !== 'string' || blob.length === 0) {\n return false;\n }\n const lines = SDPUtils.splitLines(blob);\n for (let i = 0; i < lines.length; i++) {\n if (lines[i].length < 2 || lines[i].charAt(1) !== '=') {\n return false;\n }\n // TODO: check the modifier a bit more.\n }\n return true;\n};\n\n// Expose public methods.\nif (typeof module === 'object') {\n module.exports = SDPUtils;\n}\n","/**\n * The errors for the conference.\n */\n\nexport enum JitsiConferenceErrors {\n /**\n * Indicates that client must be authenticated to create the conference.\n */\n AUTHENTICATION_REQUIRED = 'conference.authenticationRequired',\n\n /**\n * Indicates that chat error occurred.\n */\n CHAT_ERROR = 'conference.chatError',\n\n /**\n * Indicates that a settings error occurred.\n */\n SETTINGS_ERROR = 'conference.settingsError',\n\n /**\n * Indicates that conference has been destroyed.\n */\n CONFERENCE_DESTROYED = 'conference.destroyed',\n\n /**\n * Indicates that max users limit has been reached.\n */\n CONFERENCE_MAX_USERS = 'conference.max_users',\n\n /**\n * Indicates that a connection error occurred when trying to join a conference.\n */\n CONNECTION_ERROR = 'conference.connectionError',\n\n /**\n * Indicates that the client has been forced to restart by jicofo when the\n * conference was migrated from one bridge to another.\n */\n CONFERENCE_RESTARTED = 'conference.restarted',\n\n /**\n * Indicates that a connection error is due to not allowed,\n * occurred when trying to join a conference.\n */\n NOT_ALLOWED_ERROR = 'conference.connectionError.notAllowed',\n\n /**\n * Indicates that a connection error is due to not allowed,\n * occurred when trying to join a conference, only approved members are allowed to join.\n */\n MEMBERS_ONLY_ERROR = 'conference.connectionError.membersOnly',\n\n /**\n * Indicates that a connection error is due to denied access to the room,\n * occurred after joining a lobby room and access is denied by the room moderators.\n */\n CONFERENCE_ACCESS_DENIED = 'conference.connectionError.accessDenied',\n\n /**\n * Indicates that focus error happened.\n */\n FOCUS_DISCONNECTED = 'conference.focusDisconnected',\n\n /**\n * Indicates that focus left the conference.\n */\n FOCUS_LEFT = 'conference.focusLeft',\n\n /**\n * Indicates that graceful shutdown happened.\n */\n GRACEFUL_SHUTDOWN = 'conference.gracefulShutdown',\n\n /**\n * Indicates that the media connection has failed.\n */\n ICE_FAILED = 'conference.iceFailed',\n\n /**\n * Indicates that the versions of the server side components are incompatible\n * with the client side.\n */\n INCOMPATIBLE_SERVER_VERSIONS = 'conference.incompatible_server_versions',\n\n /**\n * Indicates that offer/answer had failed.\n */\n OFFER_ANSWER_FAILED = 'conference.offerAnswerFailed',\n\n /**\n * Indicates that password cannot be set for this conference.\n */\n PASSWORD_NOT_SUPPORTED = 'conference.passwordNotSupported',\n\n /**\n * Indicates that a password is required in order to join the conference.\n */\n PASSWORD_REQUIRED = 'conference.passwordRequired',\n\n /**\n * Indicates that reservation system returned error.\n */\n RESERVATION_ERROR = 'conference.reservationError',\n\n /**\n * Indicates that there is no available videobridge.\n */\n VIDEOBRIDGE_NOT_AVAILABLE = 'conference.videobridgeNotAvailable'\n};\n\n// exported for backward compatibility\nexport const AUTHENTICATION_REQUIRED = JitsiConferenceErrors.AUTHENTICATION_REQUIRED;\nexport const CHAT_ERROR = JitsiConferenceErrors.CHAT_ERROR;\nexport const SETTINGS_ERROR = JitsiConferenceErrors.SETTINGS_ERROR;\nexport const CONFERENCE_DESTROYED = JitsiConferenceErrors.CONFERENCE_DESTROYED;\nexport const CONFERENCE_MAX_USERS = JitsiConferenceErrors.CONFERENCE_MAX_USERS;\nexport const CONNECTION_ERROR = JitsiConferenceErrors.CONNECTION_ERROR;\nexport const CONFERENCE_RESTARTED = JitsiConferenceErrors.CONFERENCE_RESTARTED;\nexport const NOT_ALLOWED_ERROR = JitsiConferenceErrors.NOT_ALLOWED_ERROR;\nexport const MEMBERS_ONLY_ERROR = JitsiConferenceErrors.MEMBERS_ONLY_ERROR;\nexport const CONFERENCE_ACCESS_DENIED = JitsiConferenceErrors.CONFERENCE_ACCESS_DENIED;\nexport const FOCUS_DISCONNECTED = JitsiConferenceErrors.FOCUS_DISCONNECTED;\nexport const FOCUS_LEFT = JitsiConferenceErrors.FOCUS_LEFT;\nexport const GRACEFUL_SHUTDOWN = JitsiConferenceErrors.GRACEFUL_SHUTDOWN;\nexport const ICE_FAILED = JitsiConferenceErrors.ICE_FAILED;\nexport const INCOMPATIBLE_SERVER_VERSIONS = JitsiConferenceErrors.INCOMPATIBLE_SERVER_VERSIONS;\nexport const OFFER_ANSWER_FAILED = JitsiConferenceErrors.OFFER_ANSWER_FAILED;\nexport const PASSWORD_NOT_SUPPORTED = JitsiConferenceErrors.PASSWORD_NOT_SUPPORTED;\nexport const PASSWORD_REQUIRED = JitsiConferenceErrors.PASSWORD_REQUIRED;\nexport const RESERVATION_ERROR = JitsiConferenceErrors.RESERVATION_ERROR;\nexport const VIDEOBRIDGE_NOT_AVAILABLE = JitsiConferenceErrors.VIDEOBRIDGE_NOT_AVAILABLE;\n","/**\n * The events for the conference.\n */\n\nexport enum JitsiConferenceEvents {\n /**\n * Event indicates that the current conference audio input switched between audio\n * input states,i.e. with or without audio input.\n */\n AUDIO_INPUT_STATE_CHANGE = 'conference.audio_input_state_changed',\n\n /**\n * Event indicates that the permission for unmuting audio has changed based on the number of audio senders in the call\n * and the audio sender limit configured in Jicofo.\n */\n AUDIO_UNMUTE_PERMISSIONS_CHANGED = 'conference.audio_unmute_permissions_changed',\n\n /**\n * Indicates that authentication status changed.\n */\n AUTH_STATUS_CHANGED = 'conference.auth_status_changed',\n\n /**\n * Fired just before the statistics module is disposed and it's the last chance\n * to submit some logs to the statistics service (ex. CallStats if enabled),\n * before it's disconnected.\n */\n BEFORE_STATISTICS_DISPOSED = 'conference.beforeStatisticsDisposed',\n\n /**\n * Indicates that an error occurred.\n */\n CONFERENCE_ERROR = 'conference.error',\n\n /**\n * Indicates that conference failed.\n */\n CONFERENCE_FAILED = 'conference.failed',\n\n /**\n * Indicates that conference is in progress of joining.\n */\n CONFERENCE_JOIN_IN_PROGRESS = 'conference.join_in_progress',\n\n /**\n * Indicates that conference has been joined. The event does NOT provide any\n * parameters to its listeners.\n */\n CONFERENCE_JOINED = 'conference.joined',\n\n /**\n * Indicates that conference has been left.\n */\n CONFERENCE_LEFT = 'conference.left',\n\n /**\n * Indicates that the conference unique identifier has been set.\n */\n CONFERENCE_UNIQUE_ID_SET = 'conference.unique_id_set',\n\n /**\n * Indicates that the connection to the conference has been established\n * XXX This is currently fired when the *ICE* connection enters 'connected'\n * state for the first time.\n */\n CONNECTION_ESTABLISHED = 'conference.connectionEstablished',\n\n /**\n * Indicates that the connection to the conference has been interrupted for some\n * reason.\n * XXX This is currently fired when the *ICE* connection is interrupted.\n */\n CONNECTION_INTERRUPTED = 'conference.connectionInterrupted',\n\n /**\n * Indicates that the connection to the conference has been restored.\n * XXX This is currently fired when the *ICE* connection is restored.\n */\n CONNECTION_RESTORED = 'conference.connectionRestored',\n\n /**\n * A connection to the video bridge's data channel has been established.\n */\n DATA_CHANNEL_OPENED = 'conference.dataChannelOpened',\n\n /**\n * A user has changed it display name\n */\n DISPLAY_NAME_CHANGED = 'conference.displayNameChanged',\n\n /**\n * The dominant speaker was changed.\n */\n DOMINANT_SPEAKER_CHANGED = 'conference.dominantSpeaker',\n\n /**\n * UTC conference timestamp when first participant joined.\n */\n CONFERENCE_CREATED_TIMESTAMP = 'conference.createdTimestamp',\n\n /**\n * Indicates that DTMF support changed.\n */\n DTMF_SUPPORT_CHANGED = 'conference.dtmfSupportChanged',\n\n /**\n * Indicates that a message from another participant is received on data\n * channel.\n */\n ENDPOINT_MESSAGE_RECEIVED = 'conference.endpoint_message_received',\n\n /**\n * Indicates that a message for the remote endpoint statistics has been received on the bridge channel.\n */\n ENDPOINT_STATS_RECEIVED = 'conference.endpoint_stats_received',\n\n /**\n * NOTE This is lib-jitsi-meet internal event and can be removed at any time !\n *\n * Event emitted when conference transits, between one to one and multiparty JVB\n * conference. If the conference switches to P2P it's neither one to one nor\n * a multiparty JVB conference, but P2P (the status argument of this event will\n * be false).\n *\n * The first argument is a boolean which carries the previous value and\n * the seconds argument is a boolean with the new status. The event is emitted\n * only if the previous and the new values are different.\n *\n * @type {string}\n */\n JVB121_STATUS = 'conference.jvb121Status',\n\n /**\n * You are kicked from the conference.\n * @param {JitsiParticipant} the participant that initiated the kick.\n */\n KICKED = 'conference.kicked',\n\n /**\n * Participant was kicked from the conference.\n * @param {JitsiParticipant} the participant that initiated the kick.\n * @param {JitsiParticipant} the participant that was kicked.\n */\n PARTICIPANT_KICKED = 'conference.participant_kicked',\n\n /**\n * The Last N set is changed.\n *\n * @param {Array|null} leavingEndpointIds the ids of all the endpoints\n * which are leaving Last N\n * @param {Array|null} enteringEndpointIds the ids of all the endpoints\n * which are entering Last N\n */\n LAST_N_ENDPOINTS_CHANGED = 'conference.lastNEndpointsChanged',\n\n /**\n * The forwarded sources set is changed.\n *\n * @param {Array} leavingForwardedSources the sourceNames of all the tracks which are leaving forwarded\n * sources\n * @param {Array} enteringForwardedSources the sourceNames of all the tracks which are entering forwarded\n * sources\n */\n FORWARDED_SOURCES_CHANGED = 'conference.forwardedSourcesChanged',\n\n /**\n * Indicates that the room has been locked or unlocked.\n */\n LOCK_STATE_CHANGED = 'conference.lock_state_changed',\n\n /**\n * Indicates that the region of the media server (jitsi-videobridge) that we\n * are connected to changed (or was initially set).\n * @type {string} the region.\n */\n SERVER_REGION_CHANGED = 'conference.server_region_changed',\n\n /**\n * An event(library-private) fired when a new media session is added to the conference.\n * @type {string}\n * @private\n */\n _MEDIA_SESSION_STARTED = 'conference.media_session.started',\n\n /**\n * An event(library-private) fired when the conference switches the currently active media session.\n * @type {string}\n * @private\n */\n _MEDIA_SESSION_ACTIVE_CHANGED = 'conference.media_session.active_changed',\n\n /**\n * Indicates that the conference had changed to members only enabled/disabled.\n * The first argument of this event is a boolean which when set to\n * true means that the conference is running in members only mode.\n * You may need to use Lobby if supported to ask for permissions to enter the conference.\n */\n MEMBERS_ONLY_CHANGED = 'conference.membersOnlyChanged',\n\n /**\n * New text message was received.\n */\n MESSAGE_RECEIVED = 'conference.messageReceived',\n\n /**\n * Event indicates that the current selected input device has no signal\n */\n NO_AUDIO_INPUT = 'conference.no_audio_input',\n\n /**\n * Event indicates that the current microphone used by the conference is noisy.\n */\n NOISY_MIC = 'conference.noisy_mic',\n\n /**\n * Indicates that a message from the local user or from the Prosody backend\n * was received on the data channel.\n */\n NON_PARTICIPANT_MESSAGE_RECEIVED = 'conference.non_participant_message_received',\n\n /**\n * New private text message was received.\n */\n PRIVATE_MESSAGE_RECEIVED = 'conference.privateMessageReceived',\n\n /**\n * Event fired when JVB sends notification about interrupted/restored user's\n * ICE connection status or we detect local problem with the video track.\n * First argument is the ID of the participant and\n * the seconds is a string indicating if the connection is currently\n * - active - the connection is active\n * - inactive - the connection is inactive, was intentionally interrupted by\n * the bridge\n * - interrupted - a network problem occurred\n * - restoring - the connection was inactive and is restoring now\n *\n * The current status value can be obtained by calling\n * JitsiParticipant.getConnectionStatus().\n */\n PARTICIPANT_CONN_STATUS_CHANGED = 'conference.participant_conn_status_changed',\n\n /**\n * Indicates that the features of the participant has been changed.\n * TODO: there is a spelling mistake in this event name and associated constants\n */\n PARTCIPANT_FEATURES_CHANGED = 'conference.partcipant_features_changed',\n\n /**\n * Indicates that a the value of a specific property of a specific participant\n * has changed.\n */\n PARTICIPANT_PROPERTY_CHANGED = 'conference.participant_property_changed',\n\n /**\n * Indicates that the conference has switched between JVB and P2P connections.\n * The first argument of this event is a boolean which when set to\n * true means that the conference is running on the P2P connection.\n */\n P2P_STATUS = 'conference.p2pStatus',\n\n /**\n * Indicates that phone number changed.\n */\n PHONE_NUMBER_CHANGED = 'conference.phoneNumberChanged',\n\n /**\n * The conference properties changed.\n * @type {string}\n */\n PROPERTIES_CHANGED = 'conference.propertiesChanged',\n\n /**\n * Indicates that recording state changed.\n */\n RECORDER_STATE_CHANGED = 'conference.recorderStateChanged',\n\n /**\n * Indicates that video SIP GW state changed.\n * @param {VideoSIPGWConstants} status.\n */\n VIDEO_SIP_GW_AVAILABILITY_CHANGED = 'conference.videoSIPGWAvailabilityChanged',\n\n /**\n * Indicates that video SIP GW Session state changed.\n * @param {options} event - {\n * {string} address,\n * {VideoSIPGWConstants} oldState,\n * {VideoSIPGWConstants} newState,\n * {string} displayName}\n * }.\n */\n VIDEO_SIP_GW_SESSION_STATE_CHANGED = 'conference.videoSIPGWSessionStateChanged',\n\n /**\n * Indicates that start muted settings changed.\n */\n START_MUTED_POLICY_CHANGED = 'conference.start_muted_policy_changed',\n\n /**\n * Indicates that the local user has started muted.\n */\n STARTED_MUTED = 'conference.started_muted',\n\n /**\n * Indicates that subject of the conference has changed.\n */\n SUBJECT_CHANGED = 'conference.subjectChanged',\n\n /**\n * Indicates that DTMF support changed.\n */\n SUSPEND_DETECTED = 'conference.suspendDetected',\n\n /**\n * Event indicates that local user is talking while he muted himself\n */\n TALK_WHILE_MUTED = 'conference.talk_while_muted',\n\n /**\n * A new media track was added to the conference. The event provides the\n * following parameters to its listeners:\n *\n * @param {JitsiTrack} track the added JitsiTrack\n */\n TRACK_ADDED = 'conference.trackAdded',\n\n /**\n * Audio levels of a media track ( attached to the conference) was changed.\n */\n TRACK_AUDIO_LEVEL_CHANGED = 'conference.audioLevelsChanged',\n\n /**\n * A media track ( attached to the conference) mute status was changed.\n * @param {JitsiParticipant|null} the participant that initiated the mute\n * if it is a remote mute.\n */\n TRACK_MUTE_CHANGED = 'conference.trackMuteChanged',\n\n /**\n * The media track was removed from the conference. The event provides the\n * following parameters to its listeners:\n *\n * @param {JitsiTrack} track the removed JitsiTrack\n */\n TRACK_REMOVED = 'conference.trackRemoved',\n\n /**\n * The source-add for unmuting of a media track was rejected by Jicofo.\n *\n */\n TRACK_UNMUTE_REJECTED = 'conference.trackUnmuteRejected',\n\n /**\n * Notifies for transcription status changes. The event provides the\n * following parameters to its listeners:\n *\n * @param {String} status - The new status.\n */\n TRANSCRIPTION_STATUS_CHANGED = 'conference.transcriptionStatusChanged',\n\n /**\n * A new user joined the conference.\n */\n USER_JOINED = 'conference.userJoined',\n\n /**\n * A user has left the conference.\n */\n USER_LEFT = 'conference.userLeft',\n\n /**\n * User role changed.\n */\n USER_ROLE_CHANGED = 'conference.roleChanged',\n\n /**\n * User status changed.\n */\n USER_STATUS_CHANGED = 'conference.statusChanged',\n\n /**\n * Event indicates that the permission for unmuting video has changed based on the number of video senders in the call\n * and the video sender limit configured in Jicofo.\n */\n VIDEO_UNMUTE_PERMISSIONS_CHANGED = 'conference.video_unmute_permissions_changed',\n\n /**\n * Event indicates that the bot participant type changed.\n */\n BOT_TYPE_CHANGED = 'conference.bot_type_changed',\n\n /**\n * A new user joined the lobby room.\n */\n LOBBY_USER_JOINED = 'conference.lobby.userJoined',\n\n /**\n * A user from the lobby room has been update.\n */\n LOBBY_USER_UPDATED = 'conference.lobby.userUpdated',\n\n /**\n * A user left the lobby room.\n */\n LOBBY_USER_LEFT = 'conference.lobby.userLeft',\n\n /**\n * The local participant was approved to be able to unmute.\n * @param {options} event - {\n * {MediaType} mediaType\n * }.\n */\n AV_MODERATION_APPROVED = 'conference.av_moderation.approved',\n\n /**\n * The local participant was blocked to be able to unmute.\n * @param {options} event - {\n * {MediaType} mediaType\n * }.\n */\n AV_MODERATION_REJECTED = 'conference.av_moderation.rejected',\n\n /**\n * AV Moderation was enabled/disabled. The actor is the participant that is currently in the meeting,\n * or undefined if that participant has left the meeting.\n *\n * @param {options} event - {\n * {boolean} enabled,\n * {MediaType} mediaType,\n * {JitsiParticipant} actor\n * }.\n */\n AV_MODERATION_CHANGED = 'conference.av_moderation.changed',\n\n /**\n * AV Moderation, report for user being approved to unmute.\n * @param {options} event - {\n * {JitsiParticipant} participant,\n * {MediaType} mediaType\n * }.\n */\n AV_MODERATION_PARTICIPANT_APPROVED = 'conference.av_moderation.participant.approved',\n\n /**\n * AV Moderation, report for user being blocked to unmute.\n * @param {options} event - {\n * {JitsiParticipant} participant,\n * {MediaType} mediaType\n * }.\n */\n AV_MODERATION_PARTICIPANT_REJECTED = 'conference.av_moderation.participant.rejected',\n\n /**\n * A new facial expression is added with its duration for a participant\n */\n FACIAL_EXPRESSION_ADDED = 'conference.facial_expression.added',\n\n /**\n * Event fired when a participant is requested to join a given (breakout) room.\n */\n BREAKOUT_ROOMS_MOVE_TO_ROOM = 'conference.breakout-rooms.move-to-room',\n\n /**\n * Event fired when the breakout rooms data was updated.\n */\n BREAKOUT_ROOMS_UPDATED = 'conference.breakout-rooms.updated'\n};\n\n// exported for backward compatibility\nexport const AUDIO_INPUT_STATE_CHANGE = JitsiConferenceEvents.AUDIO_INPUT_STATE_CHANGE;\nexport const AUDIO_UNMUTE_PERMISSIONS_CHANGED = JitsiConferenceEvents.AUDIO_UNMUTE_PERMISSIONS_CHANGED;\nexport const AUTH_STATUS_CHANGED = JitsiConferenceEvents.AUTH_STATUS_CHANGED;\nexport const BEFORE_STATISTICS_DISPOSED = JitsiConferenceEvents.BEFORE_STATISTICS_DISPOSED;\nexport const CONFERENCE_ERROR = JitsiConferenceEvents.CONFERENCE_ERROR;\nexport const CONFERENCE_FAILED = JitsiConferenceEvents.CONFERENCE_FAILED;\nexport const CONFERENCE_JOIN_IN_PROGRESS = JitsiConferenceEvents.CONFERENCE_JOIN_IN_PROGRESS;\nexport const CONFERENCE_JOINED = JitsiConferenceEvents.CONFERENCE_JOINED;\nexport const CONFERENCE_LEFT = JitsiConferenceEvents.CONFERENCE_LEFT;\nexport const CONFERENCE_UNIQUE_ID_SET = JitsiConferenceEvents.CONFERENCE_UNIQUE_ID_SET;\nexport const CONNECTION_ESTABLISHED = JitsiConferenceEvents.CONNECTION_ESTABLISHED;\nexport const CONNECTION_INTERRUPTED = JitsiConferenceEvents.CONNECTION_INTERRUPTED;\nexport const CONNECTION_RESTORED = JitsiConferenceEvents.CONNECTION_RESTORED;\nexport const DATA_CHANNEL_OPENED = JitsiConferenceEvents.DATA_CHANNEL_OPENED;\nexport const DISPLAY_NAME_CHANGED = JitsiConferenceEvents.DISPLAY_NAME_CHANGED;\nexport const DOMINANT_SPEAKER_CHANGED = JitsiConferenceEvents.DOMINANT_SPEAKER_CHANGED;\nexport const CONFERENCE_CREATED_TIMESTAMP = JitsiConferenceEvents.CONFERENCE_CREATED_TIMESTAMP;\nexport const DTMF_SUPPORT_CHANGED = JitsiConferenceEvents.DTMF_SUPPORT_CHANGED;\nexport const ENDPOINT_MESSAGE_RECEIVED = JitsiConferenceEvents.ENDPOINT_MESSAGE_RECEIVED;\nexport const ENDPOINT_STATS_RECEIVED = JitsiConferenceEvents.ENDPOINT_STATS_RECEIVED;\nexport const JVB121_STATUS = JitsiConferenceEvents.JVB121_STATUS;\nexport const KICKED = JitsiConferenceEvents.KICKED;\nexport const PARTICIPANT_KICKED = JitsiConferenceEvents.PARTICIPANT_KICKED;\nexport const LAST_N_ENDPOINTS_CHANGED = JitsiConferenceEvents.LAST_N_ENDPOINTS_CHANGED;\nexport const FORWARDED_SOURCES_CHANGED = JitsiConferenceEvents.FORWARDED_SOURCES_CHANGED;\nexport const LOCK_STATE_CHANGED = JitsiConferenceEvents.LOCK_STATE_CHANGED;\nexport const SERVER_REGION_CHANGED = JitsiConferenceEvents.SERVER_REGION_CHANGED;\nexport const _MEDIA_SESSION_STARTED = JitsiConferenceEvents._MEDIA_SESSION_STARTED;\nexport const _MEDIA_SESSION_ACTIVE_CHANGED = JitsiConferenceEvents._MEDIA_SESSION_ACTIVE_CHANGED;\nexport const MEMBERS_ONLY_CHANGED = JitsiConferenceEvents.MEMBERS_ONLY_CHANGED;\nexport const MESSAGE_RECEIVED = JitsiConferenceEvents.MESSAGE_RECEIVED;\nexport const NO_AUDIO_INPUT = JitsiConferenceEvents.NO_AUDIO_INPUT;\nexport const NOISY_MIC = JitsiConferenceEvents.NOISY_MIC;\nexport const NON_PARTICIPANT_MESSAGE_RECEIVED = JitsiConferenceEvents.NON_PARTICIPANT_MESSAGE_RECEIVED;\nexport const PRIVATE_MESSAGE_RECEIVED = JitsiConferenceEvents.PRIVATE_MESSAGE_RECEIVED;\nexport const PARTICIPANT_CONN_STATUS_CHANGED = JitsiConferenceEvents.PARTICIPANT_CONN_STATUS_CHANGED;\nexport const PARTCIPANT_FEATURES_CHANGED = JitsiConferenceEvents.PARTCIPANT_FEATURES_CHANGED;\nexport const PARTICIPANT_PROPERTY_CHANGED = JitsiConferenceEvents.PARTICIPANT_PROPERTY_CHANGED;\nexport const P2P_STATUS = JitsiConferenceEvents.P2P_STATUS;\nexport const PHONE_NUMBER_CHANGED = JitsiConferenceEvents.PHONE_NUMBER_CHANGED;\nexport const PROPERTIES_CHANGED = JitsiConferenceEvents.PROPERTIES_CHANGED;\nexport const RECORDER_STATE_CHANGED = JitsiConferenceEvents.RECORDER_STATE_CHANGED;\nexport const VIDEO_SIP_GW_AVAILABILITY_CHANGED = JitsiConferenceEvents.VIDEO_SIP_GW_AVAILABILITY_CHANGED;\nexport const VIDEO_SIP_GW_SESSION_STATE_CHANGED = JitsiConferenceEvents.VIDEO_SIP_GW_SESSION_STATE_CHANGED;\nexport const START_MUTED_POLICY_CHANGED = JitsiConferenceEvents.START_MUTED_POLICY_CHANGED;\nexport const STARTED_MUTED = JitsiConferenceEvents.STARTED_MUTED;\nexport const SUBJECT_CHANGED = JitsiConferenceEvents.SUBJECT_CHANGED;\nexport const SUSPEND_DETECTED = JitsiConferenceEvents.SUSPEND_DETECTED;\nexport const TALK_WHILE_MUTED = JitsiConferenceEvents.TALK_WHILE_MUTED;\nexport const TRACK_ADDED = JitsiConferenceEvents.TRACK_ADDED;\nexport const TRACK_AUDIO_LEVEL_CHANGED = JitsiConferenceEvents.TRACK_AUDIO_LEVEL_CHANGED;\nexport const TRACK_MUTE_CHANGED = JitsiConferenceEvents.TRACK_MUTE_CHANGED;\nexport const TRACK_REMOVED = JitsiConferenceEvents.TRACK_REMOVED;\nexport const TRACK_UNMUTE_REJECTED = JitsiConferenceEvents.TRACK_UNMUTE_REJECTED;\nexport const TRANSCRIPTION_STATUS_CHANGED = JitsiConferenceEvents.TRANSCRIPTION_STATUS_CHANGED;\nexport const USER_JOINED = JitsiConferenceEvents.USER_JOINED;\nexport const USER_LEFT = JitsiConferenceEvents.USER_LEFT;\nexport const USER_ROLE_CHANGED = JitsiConferenceEvents.USER_ROLE_CHANGED;\nexport const USER_STATUS_CHANGED = JitsiConferenceEvents.USER_STATUS_CHANGED;\nexport const VIDEO_UNMUTE_PERMISSIONS_CHANGED = JitsiConferenceEvents.VIDEO_UNMUTE_PERMISSIONS_CHANGED;\nexport const BOT_TYPE_CHANGED = JitsiConferenceEvents.BOT_TYPE_CHANGED;\nexport const LOBBY_USER_JOINED = JitsiConferenceEvents.LOBBY_USER_JOINED;\nexport const LOBBY_USER_UPDATED = JitsiConferenceEvents.LOBBY_USER_UPDATED;\nexport const LOBBY_USER_LEFT = JitsiConferenceEvents.LOBBY_USER_LEFT;\nexport const AV_MODERATION_APPROVED = JitsiConferenceEvents.AV_MODERATION_APPROVED;\nexport const AV_MODERATION_REJECTED = JitsiConferenceEvents.AV_MODERATION_REJECTED;\nexport const AV_MODERATION_CHANGED = JitsiConferenceEvents.AV_MODERATION_CHANGED;\nexport const AV_MODERATION_PARTICIPANT_APPROVED = JitsiConferenceEvents.AV_MODERATION_PARTICIPANT_APPROVED;\nexport const AV_MODERATION_PARTICIPANT_REJECTED = JitsiConferenceEvents.AV_MODERATION_PARTICIPANT_REJECTED;\nexport const FACIAL_EXPRESSION_ADDED = JitsiConferenceEvents.FACIAL_EXPRESSION_ADDED;\nexport const BREAKOUT_ROOMS_MOVE_TO_ROOM = JitsiConferenceEvents.BREAKOUT_ROOMS_MOVE_TO_ROOM;\nexport const BREAKOUT_ROOMS_UPDATED = JitsiConferenceEvents.BREAKOUT_ROOMS_UPDATED;\n","export const CALLSTATS_SCRIPT_URL = 'https://api.callstats.io/static/callstats-ws.min.js';\n\n/**\n * The number of remote speakers for which the audio levels will be calculated using\n * RTCRtpReceiver#getSynchronizationSources. Limit the number of endpoints to save cpu on the client as this API call\n * is known to take longer to execute when there are many audio receivers.\n */\nexport const SPEAKERS_AUDIO_LEVELS = 5;\n","/**\n * The errors for the JitsiTrack objects.\n */\n\nexport enum JitsiTrackErrors {\n /**\n * An error which indicates that some of requested constraints in\n * getUserMedia call were not satisfied.\n */\n CONSTRAINT_FAILED = 'gum.constraint_failed',\n\n /**\n * A generic error which indicates an error occurred while selecting\n * a DesktopCapturerSource from the electron app.\n */\n ELECTRON_DESKTOP_PICKER_ERROR = 'gum.electron_desktop_picker_error',\n\n /**\n * An error which indicates a custom desktop picker could not be detected\n * for the electron app.\n */\n ELECTRON_DESKTOP_PICKER_NOT_FOUND = 'gum.electron_desktop_picker_not_found',\n\n /**\n * Generic getUserMedia error.\n */\n GENERAL = 'gum.general',\n\n /**\n * An error which indicates that requested device was not found.\n */\n NOT_FOUND = 'gum.not_found',\n\n /**\n * An error which indicates that user denied permission to share requested\n * device.\n */\n PERMISSION_DENIED = 'gum.permission_denied',\n\n /**\n * Generic error for screensharing failure.\n */\n SCREENSHARING_GENERIC_ERROR = 'gum.screensharing_generic_error',\n\n /**\n * An error which indicates that user canceled screen sharing window\n * selection dialog.\n */\n SCREENSHARING_USER_CANCELED = 'gum.screensharing_user_canceled',\n\n /**\n * Indicates that the timeout passed to the obtainAudioAndVideoPermissions has expired without GUM resolving.\n */\n TIMEOUT = 'gum.timeout',\n\n /**\n * An error which indicates that track has been already disposed and cannot\n * be longer used.\n */\n TRACK_IS_DISPOSED = 'track.track_is_disposed',\n\n /**\n * An error which indicates that track has no MediaStream associated.\n */\n TRACK_NO_STREAM_FOUND = 'track.no_stream_found',\n\n /**\n * An error which indicates that requested video resolution is not supported\n * by a webcam.\n */\n UNSUPPORTED_RESOLUTION = 'gum.unsupported_resolution'\n}\n\n// exported for backward compatibility\nexport const CONSTRAINT_FAILED = JitsiTrackErrors.CONSTRAINT_FAILED;\nexport const ELECTRON_DESKTOP_PICKER_ERROR = JitsiTrackErrors.ELECTRON_DESKTOP_PICKER_ERROR;\nexport const ELECTRON_DESKTOP_PICKER_NOT_FOUND = JitsiTrackErrors.ELECTRON_DESKTOP_PICKER_NOT_FOUND;\nexport const GENERAL = JitsiTrackErrors.GENERAL;\nexport const NOT_FOUND = JitsiTrackErrors.NOT_FOUND;\nexport const PERMISSION_DENIED = JitsiTrackErrors.PERMISSION_DENIED;\nexport const SCREENSHARING_GENERIC_ERROR = JitsiTrackErrors.SCREENSHARING_GENERIC_ERROR;\nexport const SCREENSHARING_USER_CANCELED = JitsiTrackErrors.SCREENSHARING_USER_CANCELED;\nexport const TIMEOUT = JitsiTrackErrors.TIMEOUT;\nexport const TRACK_IS_DISPOSED = JitsiTrackErrors.TRACK_IS_DISPOSED;\nexport const TRACK_NO_STREAM_FOUND = JitsiTrackErrors.TRACK_NO_STREAM_FOUND;\nexport const UNSUPPORTED_RESOLUTION = JitsiTrackErrors.UNSUPPORTED_RESOLUTION;\n","import * as JitsiTrackErrors from './JitsiTrackErrors';\n\nconst TRACK_ERROR_TO_MESSAGE_MAP = {};\n\nTRACK_ERROR_TO_MESSAGE_MAP[JitsiTrackErrors.UNSUPPORTED_RESOLUTION]\n = 'Video resolution is not supported: ';\nTRACK_ERROR_TO_MESSAGE_MAP[JitsiTrackErrors.SCREENSHARING_USER_CANCELED]\n = 'User canceled screen sharing prompt';\nTRACK_ERROR_TO_MESSAGE_MAP[JitsiTrackErrors.SCREENSHARING_GENERIC_ERROR]\n = 'Unknown error from screensharing';\nTRACK_ERROR_TO_MESSAGE_MAP[JitsiTrackErrors.ELECTRON_DESKTOP_PICKER_ERROR]\n = 'Unkown error from desktop picker';\nTRACK_ERROR_TO_MESSAGE_MAP[JitsiTrackErrors.ELECTRON_DESKTOP_PICKER_NOT_FOUND]\n = 'Failed to detect desktop picker';\nTRACK_ERROR_TO_MESSAGE_MAP[JitsiTrackErrors.GENERAL]\n = 'Generic getUserMedia error';\nTRACK_ERROR_TO_MESSAGE_MAP[JitsiTrackErrors.PERMISSION_DENIED]\n = 'User denied permission to use device(s): ';\nTRACK_ERROR_TO_MESSAGE_MAP[JitsiTrackErrors.NOT_FOUND]\n = 'Requested device(s) was/were not found: ';\nTRACK_ERROR_TO_MESSAGE_MAP[JitsiTrackErrors.CONSTRAINT_FAILED]\n = 'Constraint could not be satisfied: ';\nTRACK_ERROR_TO_MESSAGE_MAP[JitsiTrackErrors.TIMEOUT]\n = 'Could not start media source. Timeout occured!';\nTRACK_ERROR_TO_MESSAGE_MAP[JitsiTrackErrors.TRACK_IS_DISPOSED]\n = 'Track has been already disposed';\nTRACK_ERROR_TO_MESSAGE_MAP[JitsiTrackErrors.TRACK_NO_STREAM_FOUND]\n = 'Track does not have an associated Media Stream';\n\n// FIXME: Using prototype inheritance because otherwise instanceof is not\n// working properly (see https://github.com/babel/babel/issues/3083)\n\n/**\n *\n * Represents an error that occurred to a JitsiTrack. Can represent various\n * types of errors. For error descriptions (@see JitsiTrackErrors).\n *\n * @extends Error\n *\n *\n * @constructor\n * @param {Object|string} error - error object or error name\n * @param {Object|string} (options) - getUserMedia constraints object or\n * error message\n * @param {('audio'|'video'|'desktop'|'screen'|'audiooutput')[]} (devices) -\n * list of getUserMedia requested devices\n */\nfunction JitsiTrackError(error, options, devices) {\n if (typeof error === 'object' && typeof error.name !== 'undefined') {\n /**\n * Additional information about original getUserMedia error\n * and constraints.\n * @type {{\n * error: Object,\n * constraints: Object,\n * devices: Array.<'audio'|'video'|'desktop'|'screen'>\n * }}\n */\n this.gum = {\n error,\n constraints: options,\n devices: devices && Array.isArray(devices)\n ? devices.slice(0)\n : undefined\n };\n\n switch (error.name) {\n case 'NotAllowedError':\n case 'PermissionDeniedError':\n case 'SecurityError':\n this.name = JitsiTrackErrors.PERMISSION_DENIED;\n this.message\n = TRACK_ERROR_TO_MESSAGE_MAP[this.name]\n + (this.gum.devices || []).join(', ');\n break;\n case 'DevicesNotFoundError':\n case 'NotFoundError':\n this.name = JitsiTrackErrors.NOT_FOUND;\n this.message\n = TRACK_ERROR_TO_MESSAGE_MAP[this.name]\n + (this.gum.devices || []).join(', ');\n break;\n case 'ConstraintNotSatisfiedError':\n case 'OverconstrainedError': {\n const constraintName = error.constraintName || error.constraint;\n\n // we treat deviceId as unsupported resolution, as we want to\n // retry and finally if everything fails to remove deviceId from\n // mandatory constraints\n if (options\n && options.video\n && (!devices || devices.indexOf('video') > -1)\n && (constraintName === 'minWidth'\n || constraintName === 'maxWidth'\n || constraintName === 'minHeight'\n || constraintName === 'maxHeight'\n || constraintName === 'width'\n || constraintName === 'height'\n || constraintName === 'deviceId')) {\n this.name = JitsiTrackErrors.UNSUPPORTED_RESOLUTION;\n this.message\n = TRACK_ERROR_TO_MESSAGE_MAP[this.name]\n + getResolutionFromFailedConstraint(\n constraintName,\n options);\n } else {\n this.name = JitsiTrackErrors.CONSTRAINT_FAILED;\n this.message\n = TRACK_ERROR_TO_MESSAGE_MAP[this.name]\n + error.constraintName;\n }\n break;\n }\n\n default:\n this.name = JitsiTrackErrors.GENERAL;\n this.message\n = error.message || TRACK_ERROR_TO_MESSAGE_MAP[this.name];\n break;\n }\n } else if (typeof error === 'string') {\n if (TRACK_ERROR_TO_MESSAGE_MAP[error]) {\n this.name = error;\n this.message = options || TRACK_ERROR_TO_MESSAGE_MAP[error];\n } else {\n // this is some generic error that do not fit any of our\n // pre-defined errors, so don't give it any specific name, just\n // store message\n this.message = error;\n }\n } else {\n throw new Error('Invalid arguments');\n }\n\n this.stack = error.stack || new Error().stack;\n}\n\nJitsiTrackError.prototype = Object.create(Error.prototype);\nJitsiTrackError.prototype.constructor = JitsiTrackError;\n\n/**\n * Gets failed resolution constraint from corresponding object.\n * @param {string} failedConstraintName\n * @param {Object} constraints\n * @returns {string|number}\n */\nfunction getResolutionFromFailedConstraint(failedConstraintName, constraints) {\n if (constraints && constraints.video && constraints.video.mandatory) {\n switch (failedConstraintName) {\n case 'width':\n return constraints.video.mandatory.minWidth;\n case 'height':\n return constraints.video.mandatory.minHeight;\n default:\n return constraints.video.mandatory[failedConstraintName] || '';\n }\n }\n\n return '';\n}\n\nexport default JitsiTrackError;\n","/**\n * This class exports constants and factory methods related to the analytics\n * API provided by AnalyticsAdapter. In order for entries in a database to be\n * somewhat easily traceable back to the code which produced them, events sent\n * through analytics should be defined here.\n *\n * Since the AnalyticsAdapter API can be used in different ways, for some events\n * it is more convenient to just define the event name as a constant. For other\n * events a factory function is easier.\n *\n * A general approach for adding a new event:\n * 1. Determine the event type: track, UI, page, or operational. If in doubt use\n * operational.\n * 2. Determine whether the event is related to other existing events, and\n * which fields are desired to be set: name, action, actionSubject, source.\n * 3. If the name is sufficient (the other fields are not important), use a\n * constant. Otherwise use a factory function.\n *\n * Note that the AnalyticsAdapter uses the events passed to its functions for\n * its own purposes, and might modify them. Because of this, factory functions\n * should create new objects.\n *\n */\n\n/**\n * The constant which identifies an event of type \"operational\".\n * @type {string}\n */\nexport const TYPE_OPERATIONAL = 'operational';\n\n/**\n * The constant which identifies an event of type \"page\".\n * @type {string}\n */\nexport const TYPE_PAGE = 'page';\n\n/**\n * The constant which identifies an event of type \"track\".\n * @type {string}\n */\nexport const TYPE_TRACK = 'track';\n\n/**\n * The constant which identifies an event of type \"ui\".\n * @type {string}\n */\nexport const TYPE_UI = 'ui';\n\n/**\n * The \"action\" value for Jingle events which indicates that the Jingle session\n * was restarted (TODO: verify/fix the documentation)\n * @type {string}\n */\nexport const ACTION_JINGLE_RESTART = 'restart';\n\n/**\n * The \"action\" value for Jingle events which indicates that a session-accept\n * timed out (TODO: verify/fix the documentation)\n * @type {string}\n */\nexport const ACTION_JINGLE_SA_TIMEOUT = 'session-accept.timeout';\n\n/**\n * The \"action\" value for Jingle events which indicates that a session-initiate\n * was received.\n * @type {string}\n */\nexport const ACTION_JINGLE_SI_RECEIVED = 'session-initiate.received';\n\n/**\n * The \"action\" value for Jingle events which indicates that a session-initiate\n * not arrived within a timeout (the value is specified in\n * the {@link JingleSessionPC}.\n * @type {string}\n */\nexport const ACTION_JINGLE_SI_TIMEOUT = 'session-initiate.timeout';\n\n/**\n * A constant for the \"terminate\" action for Jingle events. TODO: verify/fix\n * the documentation)\n * @type {string}\n */\nexport const ACTION_JINGLE_TERMINATE = 'terminate';\n\n/**\n * The \"action\" value for Jingle events which indicates that a transport-replace\n * was received.\n * @type {string}\n */\nexport const ACTION_JINGLE_TR_RECEIVED\n = 'transport-replace.received';\n\n/**\n * The \"action\" value for Jingle events which indicates that a transport-replace\n * succeeded (TODO: verify/fix the documentation)\n * @type {string}\n */\nexport const ACTION_JINGLE_TR_SUCCESS\n = 'transport-replace.success';\n\n/**\n * The \"action\" value for P2P events which indicates that P2P session initiate message has been rejected by the client\n * because the mandatory requirements were not met.\n * @type {string}\n */\nexport const ACTION_P2P_DECLINED = 'decline';\n\n/**\n * The \"action\" value for P2P events which indicates that a connection was\n * established (TODO: verify/fix the documentation)\n * @type {string}\n */\nexport const ACTION_P2P_ESTABLISHED = 'established';\n\n/**\n * The \"action\" value for P2P events which indicates that something failed.\n * @type {string}\n */\nexport const ACTION_P2P_FAILED = 'failed';\n\n/**\n * The \"action\" value for P2P events which indicates that a switch to\n * jitsi-videobridge happened.\n * @type {string}\n */\nexport const ACTION_P2P_SWITCH_TO_JVB = 'switch.to.jvb';\n\n/**\n * The name of an event which indicates an available device. We send one such\n * event per available device once when the available devices are first known,\n * and every time that they change\n * @type {string}\n *\n * Properties:\n * audio_input_device_count: the number of audio input devices available at\n * the time the event was sent.\n * audio_output_device_count: the number of audio output devices available\n * at the time the event was sent.\n * video_input_device_count: the number of video input devices available at\n * the time the event was sent.\n * video_output_device_count: the number of video output devices available\n * at the time the event was sent.\n * device_id: an identifier of the device described in this event.\n * device_group_id:\n * device_kind: one of 'audioinput', 'audiooutput', 'videoinput' or\n * 'videooutput'.\n * device_label: a string which describes the device.\n */\nexport const AVAILABLE_DEVICE = 'available.device';\n\n/**\n * This appears to be fired only in certain cases when the XMPP connection\n * disconnects (and it was intentional?). It is currently never observed to\n * fire in production.\n *\n * TODO: document\n *\n * Properties:\n * message: an error message\n */\nexport const CONNECTION_DISCONNECTED = 'connection.disconnected';\n\n/**\n * Indicates that the user of the application provided feedback in terms of a\n * rating (an integer from 1 to 5) and an optional comment.\n * Properties:\n * value: the user's rating (an integer from 1 to 5)\n * comment: the user's comment\n */\nexport const FEEDBACK = 'feedback';\n\n/**\n * Indicates the duration of a particular phase of the ICE connectivity\n * establishment.\n *\n * Properties:\n * phase: the ICE phase (e.g. 'gathering', 'checking', 'establishment')\n * value: the duration in milliseconds.\n * p2p: whether the associated ICE connection is p2p or towards a\n * jitsi-videobridge\n * initiator: whether the local Jingle peer is the initiator or responder\n * in the Jingle session. XXX we probably actually care about the ICE\n * role (controlling vs controlled), and we assume that this correlates\n * with the Jingle initiator.\n */\nexport const ICE_DURATION = 'ice.duration';\n\n/**\n * Indicates the difference in milliseconds between the ICE establishment time\n * for the P2P and JVB connections (e.g. a value of 10 would indicate that the\n * P2P connection took 10ms more than JVB connection to establish).\n *\n * Properties:\n * value: the difference in establishment durations in milliseconds.\n *\n */\nexport const ICE_ESTABLISHMENT_DURATION_DIFF\n = 'ice.establishment.duration.diff';\n\n/**\n * Indicates that the ICE state has changed.\n *\n * Properties:\n * state: the ICE state which was entered (e.g. 'checking', 'connected',\n * 'completed', etc).\n * value: the time in milliseconds (as reported by\n * window.performance.now()) that the state change occurred.\n * p2p: whether the associated ICE connection is p2p or towards a\n * jitsi-videobridge\n * signalingState: The signaling state of the associated PeerConnection\n * reconnect: whether the associated Jingle session is in the process of\n * reconnecting (or is it ICE? TODO: verify/fix the documentation)\n */\nexport const ICE_STATE_CHANGED = 'ice.state.changed';\n\n/**\n * Indicates that no bytes have been sent for the track.\n *\n * Properties:\n * mediaType: the media type of the local track ('audio' or 'video').\n */\nexport const NO_BYTES_SENT = 'track.no-bytes-sent';\n\n/**\n * Indicates that a track was unmuted (?).\n *\n * Properties:\n * mediaType: the media type of the local track ('audio' or 'video').\n * trackType: the type of the track ('local' or 'remote').\n * value: TODO: document\n */\nexport const TRACK_UNMUTED = 'track.unmuted';\n\n/**\n * Creates an operational event which indicates that we have received a\n * \"bridge down\" event from jicofo.\n */\nexport const createBridgeDownEvent = function() {\n const bridgeDown = 'bridge.down';\n\n return {\n action: bridgeDown,\n actionSubject: bridgeDown,\n type: TYPE_OPERATIONAL\n };\n};\n\n/**\n * Creates an event which indicates that the XMPP connection failed\n * @param errorType TODO\n * @param errorMessage TODO\n * @param detail connection failed details.\n */\nexport const createConnectionFailedEvent\n = function(errorType, errorMessage, details) {\n return {\n type: TYPE_OPERATIONAL,\n action: 'connection.failed',\n attributes: {\n 'error_type': errorType,\n 'error_message': errorMessage,\n ...details\n }\n };\n };\n\n/**\n * Creates a conference event.\n *\n * @param {string} action - The action of the event.\n * @param {Object} attributes - The attributes to be added to the event.\n * @returns {{type: string, source: string, action: string, attributes: object}}\n */\nexport function createConferenceEvent(action, attributes) {\n return {\n action,\n attributes,\n source: 'conference',\n type: TYPE_OPERATIONAL\n };\n}\n\n/**\n * Creates an operational event which indicates that a particular connection\n * stage was reached (i.e. the XMPP connection transitioned to the \"connected\"\n * state).\n *\n * @param stage the stage which was reached\n * @param attributes additional attributes for the event. This should be an\n * object with a \"value\" property indicating a timestamp in milliseconds\n * relative to the beginning of the document's lifetime.\n *\n */\nexport const createConnectionStageReachedEvent = function(stage, attributes) {\n const action = 'connection.stage.reached';\n\n return {\n action,\n actionSubject: stage,\n attributes,\n source: action,\n type: TYPE_OPERATIONAL\n };\n};\n\n/**\n * Creates an operational event for the end-to-end round trip time to a\n * specific remote participant.\n * @param participantId the ID of the remote participant.\n * @param region the region of the remote participant\n * @param rtt the rtt\n */\nexport const createE2eRttEvent = function(participantId, region, rtt) {\n const attributes = {\n 'participant_id': participantId,\n region,\n rtt\n };\n\n return {\n attributes,\n name: 'e2e_rtt',\n type: TYPE_OPERATIONAL\n };\n};\n\n/**\n * Creates an event which indicates that the focus has left the MUC.\n */\nexport const createFocusLeftEvent = function() {\n const action = 'focus.left';\n\n return {\n action,\n actionSubject: action,\n type: TYPE_OPERATIONAL\n };\n};\n\n/**\n * Creates an event related to a getUserMedia call.\n *\n * @param action the type of the result that the event represents: 'error',\n * 'success', 'warning', etc.\n * @param attributes the attributes to attach to the event.\n * @returns {{type: string, source: string, name: string}}\n */\nexport const createGetUserMediaEvent = function(action, attributes = {}) {\n return {\n type: TYPE_OPERATIONAL,\n source: 'get.user.media',\n action,\n attributes\n };\n};\n\n/**\n * Creates an event related to remote participant connection status changes.\n *\n * @param attributes the attributes to attach to the event.\n * @returns {{type: string, source: string, name: string}}\n */\nexport const createParticipantConnectionStatusEvent = function(attributes = {}) {\n const action = 'duration';\n\n return {\n type: TYPE_OPERATIONAL,\n source: 'peer.conn.status',\n action,\n attributes\n };\n};\n\n/**\n * Creates an event related to remote track streaming status changes.\n *\n * @param attributes the attributes to attach to the event.\n * @returns {{type: string, source: string, name: string}}\n */\nexport const createTrackStreamingStatusEvent = function(attributes = {}) {\n const action = 'duration';\n\n return {\n type: TYPE_OPERATIONAL,\n source: 'track.streaming.status',\n action,\n attributes\n };\n};\n\n/**\n * Creates an event for a Jingle-related event.\n * @param action the action of the event\n * @param attributes attributes to add to the event.\n */\nexport const createJingleEvent = function(action, attributes = {}) {\n return {\n type: TYPE_OPERATIONAL,\n action,\n source: 'jingle',\n attributes\n };\n};\n\n/**\n * Creates an event which indicates that a local track was not able to read\n * data from its source (a camera or a microphone).\n *\n * @param mediaType {String} the media type of the local track ('audio' or\n * 'video').\n */\nexport const createNoDataFromSourceEvent = function(mediaType, value) {\n return {\n attributes: {\n 'media_type': mediaType,\n value\n },\n action: 'track.no.data.from.source',\n type: TYPE_OPERATIONAL\n };\n};\n\n/**\n * Creates an event for a p2p-related event.\n * @param action the action of the event\n * @param attributes attributes to add to the event.\n */\nexport const createP2PEvent = function(action, attributes = {}) {\n return {\n type: TYPE_OPERATIONAL,\n action,\n source: 'p2p',\n attributes\n };\n};\n\n/**\n * Indicates that we received a remote command to mute.\n */\nexport const createRemotelyMutedEvent = function(mediaType) {\n return {\n type: TYPE_OPERATIONAL,\n action: 'remotely.muted',\n mediaType\n };\n};\n\n/**\n * Creates an event which contains RTP statistics such as RTT and packet loss.\n *\n * All average RTP stats are currently reported under 1 event name, but with\n * different properties that allows to distinguish between a P2P call, a\n * call relayed through TURN or the JVB, and multiparty vs 1:1.\n *\n * The structure of the event is:\n *\n * {\n * p2p: true,\n * conferenceSize: 2,\n * localCandidateType: \"relay\",\n * remoteCandidateType: \"relay\",\n * transportType: \"udp\",\n *\n * // Average RTT of 200ms\n * \"rtt.avg\": 200,\n * \"rtt.samples\": \"[100, 200, 300]\",\n *\n * // Average packet loss of 10%\n * \"packet.loss.avg\": 10,\n * \"packet.loss.samples\": '[5, 10, 15]'\n *\n * // Difference in milliseconds in the end-to-end RTT between p2p and jvb.\n * // The e2e RTT through jvb is 15ms shorter:\n * \"rtt.diff\": 15,\n *\n * // End-to-end RTT through JVB is ms.\n * \"end2end.rtt.avg\" = 100\n * }\n *\n * Note that the value of the \"samples\" properties are (JSON encoded) strings,\n * and not JSON arrays, as events' attributes can not be nested. The samples are\n * currently included for debug purposes only and can be removed anytime soon\n * from the structure.\n *\n * Also note that not all of values are present in each event, as values are\n * obtained and calculated as part of different process/event pipe. For example\n * {@link ConnectionAvgStats} instances are doing the reports for each\n * {@link TraceablePeerConnection} and work independently from the main stats\n * pipe.\n */\nexport const createRtpStatsEvent = function(attributes) {\n return {\n type: TYPE_OPERATIONAL,\n action: 'rtp.stats',\n attributes\n };\n};\n\n/**\n * Creates an event which contains the round trip time (RTT) to a set of\n * regions.\n *\n * @param attributes\n * @returns {{type: string, action: string, attributes: *}}\n */\nexport const createRttByRegionEvent = function(attributes) {\n return {\n type: TYPE_OPERATIONAL,\n action: 'rtt.by.region',\n attributes\n };\n};\n\n/**\n * Creates an event which contains the local and remote ICE candidate types\n * for the transport that is currently selected.\n *\n * @param attributes\n * @returns {{type: string, action: string, attributes: *}}\n */\nexport const createTransportStatsEvent = function(attributes) {\n return {\n type: TYPE_OPERATIONAL,\n action: 'transport.stats',\n attributes\n };\n};\n\n/**\n * Creates an event which contains information about the audio output problem (the user id of the affected participant,\n * the local audio levels and the remote audio levels that triggered the event).\n *\n * @param {string} userID - The user id of the affected participant.\n * @param {*} localAudioLevels - The local audio levels.\n * @param {*} remoteAudioLevels - The audio levels received from the participant.\n */\nexport function createAudioOutputProblemEvent(userID, localAudioLevels, remoteAudioLevels) {\n return {\n type: TYPE_OPERATIONAL,\n action: 'audio.output.problem',\n attributes: {\n userID,\n localAudioLevels,\n remoteAudioLevels\n }\n };\n}\n\n/**\n * Creates an event which contains an information related to the bridge channel close event.\n *\n * @param {string} code - A code from {@link https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent}\n * @param {string} reason - A string which describes the reason for closing the bridge channel.\n * @returns {{type: string, action: string, attributes: { code: string, reason: string }}}\n */\nexport const createBridgeChannelClosedEvent = function(code, reason) {\n return {\n type: TYPE_OPERATIONAL,\n action: 'bridge-channel.error',\n attributes: {\n code,\n reason\n }\n };\n};\n\n/**\n * Creates an event which indicates the Time To First Media (TTFM).\n * It is measured in milliseconds relative to the beginning of the document's\n * lifetime (i.e. the origin used by window.performance.now()), and it excludes\n * the following:\n * 1. The delay due to getUserMedia()\n * 2. The period between the MUC being joined and the reception of the Jingle\n * session-initiate from jicofo. This is because jicofo will not start a Jingle\n * session until there are at least 2 participants in the room.\n *\n * @param attributes the attributes to add to the event. Currently used fields:\n * mediaType: the media type of the local track ('audio' or 'video').\n * muted: whether the track has ever been muted (?)\n * value: the TTMF in milliseconds.\n */\nexport const createTtfmEvent = function(attributes) {\n return createConnectionStageReachedEvent('ttfm', attributes);\n};\n","export enum Events {\n /**\n * Notifies about audio level in RTP statistics by SSRC.\n *\n * @param ssrc - The synchronization source identifier (SSRC) of the\n * endpoint/participant whose audio level is being reported.\n * @param {number} audioLevel - The audio level of ssrc according to\n * RTP statistics.\n * @param {boolean} isLocal - true if ssrc identifies the\n * local endpoint/participant; otherwise, false.\n */\n AUDIO_LEVEL = 'statistics.audioLevel',\n\n /**\n * An event fired just before the statistics module gets disposes and it's\n * the last chance to submit some logs that will end up in stats services like\n * CallStats (if enabled).\n */\n BEFORE_DISPOSED = 'statistics.before_disposed',\n\n /**\n * An event carrying all statistics by ssrc.\n */\n BYTE_SENT_STATS = 'statistics.byte_sent_stats',\n\n /**\n * An event carrying connection statistics.\n *\n * @param {object} connectionStats - The connection statistics carried by the\n * event such as bandwidth, bitrate, packetLoss,\n * resolution, and transport.\n */\n CONNECTION_STATS = 'statistics.connectionstats',\n\n /**\n * An event carrying performance stats.\n */\n LONG_TASKS_STATS = 'statistics.long_tasks_stats'\n};\n\n// exported for backward compatibility\nexport const AUDIO_LEVEL = Events.AUDIO_LEVEL;\nexport const BEFORE_DISPOSED = Events.BEFORE_DISPOSED;\nexport const BYTE_SENT_STATS = Events.BYTE_SENT_STATS;\nexport const CONNECTION_STATS = Events.CONNECTION_STATS;\nexport const LONG_TASKS_STATS = Events.LONG_TASKS_STATS;\n","// TODO: Maybe fix the values to 'Chrome', 'Internet Explorer', etc. Currently\n// this values needs to be as they are becuse they are going to analytics,\n// callstats, etc.\n\nexport const CHROME = 'chrome';\n\nexport const OPERA = 'opera';\n\nexport const FIREFOX = 'firefox';\n\nexport const INTERNET_EXPLORER = 'iexplorer';\n\nexport const SAFARI = 'safari';\n\nexport const NWJS = 'nwjs';\n\nexport const ELECTRON = 'electron';\n\nexport const REACT_NATIVE = 'react-native';\n\nexport const UNKNOWN = 'unknown';\n","import Bowser from 'bowser';\n\nimport {\n CHROME,\n OPERA,\n FIREFOX,\n INTERNET_EXPLORER,\n SAFARI,\n NWJS,\n ELECTRON,\n REACT_NATIVE,\n UNKNOWN\n} from './browsers.js';\n\n/**\n * Maps the names of the browsers from bowser to the internal names defined in\n * ./browsers.js\n */\nconst bowserNameToJitsiName = {\n 'Chrome': CHROME,\n 'Chromium': CHROME,\n 'Opera': OPERA,\n 'Firefox': FIREFOX,\n 'Internet Explorer': INTERNET_EXPLORER,\n 'Safari': SAFARI\n};\n\n/**\n * Detects a Chromium based environent.\n *\n * NOTE: Here we cannot check solely for \"Chrome\" in the UA, because Edge has\n * it too. We need to check explicitly for chromium based Edge first and then\n * detect other chromium based browsers.\n *\n * @returns {Object|undefined} - The name (CHROME) and version.\n */\nfunction _detectChromiumBased() {\n const userAgent = navigator.userAgent;\n const browserInfo = {\n name: UNKNOWN,\n version: undefined\n };\n\n if (userAgent.match(/Chrome/) && !userAgent.match(/Edge/)) {\n // Edge is currenly supported only on desktop and android.\n if (userAgent.match(/Edg(A?)/)) {\n // Compare the underlying chromium version.\n const version = userAgent.match(/Chrome\\/([\\d.]+)/)[1];\n\n if (Number.parseInt(version, 10) > 72) {\n browserInfo.name = CHROME;\n browserInfo.version = version;\n }\n } else {\n browserInfo.name = CHROME;\n browserInfo.version = userAgent.match(/Chrome\\/([\\d.]+)/)[1];\n }\n }\n\n return browserInfo;\n}\n\n/**\n * Detects Electron environment.\n *\n * @returns {Object|undefined} - The name (ELECTRON) and version.\n */\nfunction _detectElectron() {\n const userAgent = navigator.userAgent;\n\n if (userAgent.match(/Electron/)) {\n const version = userAgent.match(/Electron(?:\\s|\\/)([\\d.]+)/)[1];\n\n return {\n name: ELECTRON,\n version\n };\n } else if (typeof window.JitsiMeetElectron !== 'undefined') {\n return {\n name: ELECTRON,\n version: undefined\n };\n }\n}\n\n/**\n * Detects NWJS environment.\n *\n * @returns {Object|undefined} - The name (NWJS) and version.\n */\nfunction _detectNWJS() {\n const userAgent = navigator.userAgent;\n\n if (userAgent.match(/JitsiMeetNW/)) {\n const version = userAgent.match(/JitsiMeetNW\\/([\\d.]+)/)[1];\n\n return {\n name: NWJS,\n version\n };\n }\n}\n\n/**\n * Detects React Native environment.\n * @returns {Object|undefined} - The name (REACT_NATIVE) and version.\n */\nfunction _detectReactNative() {\n const match\n = navigator.userAgent.match(/\\b(react[ \\t_-]*native)(?:\\/(\\S+))?/i);\n let version;\n\n // If we're remote debugging a React Native app, it may be treated as\n // Chrome. Check navigator.product as well and always return some version\n // even if we can't get the real one.\n\n if (match || navigator.product === 'ReactNative') {\n let name;\n\n if (match && match.length > 2) {\n name = match[1];\n version = match[2];\n }\n name || (name = 'react-native');\n version || (version = 'unknown');\n\n return {\n name: REACT_NATIVE,\n version\n };\n }\n}\n\n/**\n * Returns information about the current browser.\n * @param {Object} - The bowser instance.\n * @returns {Object} - The name and version of the browser.\n */\nfunction _detect(bowser) {\n let browserInfo;\n const detectors = [\n _detectReactNative,\n _detectElectron,\n _detectNWJS\n ];\n\n // Try all browser detectors\n for (let i = 0; i < detectors.length; i++) {\n browserInfo = detectors[i]();\n if (browserInfo) {\n return browserInfo;\n }\n }\n\n const name = bowser.getBrowserName();\n\n if (name in bowserNameToJitsiName) {\n return {\n name: bowserNameToJitsiName[name],\n version: bowser.getBrowserVersion()\n };\n }\n\n // Detect other browsers with the Chrome engine, such as Vivaldi and Brave.\n browserInfo = _detectChromiumBased();\n if (browserInfo) {\n return browserInfo;\n }\n\n return {\n name: UNKNOWN,\n version: undefined\n };\n}\n\n/**\n * Implements browser detection.\n */\nexport default class BrowserDetection {\n /**\n * Creates new BrowserDetection instance.\n *\n * @param {Object} [browserInfo] - Information about the browser.\n * @param {string} browserInfo.name - The name of the browser.\n * @param {string} browserInfo.version - The version of the browser.\n */\n constructor(browserInfo) {\n let name, version;\n\n this._bowser = Bowser.getParser(navigator.userAgent);\n if (typeof browserInfo === 'undefined') {\n const detectedBrowserInfo = _detect(this._bowser);\n\n name = detectedBrowserInfo.name;\n version = detectedBrowserInfo.version;\n } else if (browserInfo.name in bowserNameToJitsiName) {\n name = bowserNameToJitsiName[browserInfo.name];\n version = browserInfo.version;\n } else {\n name = UNKNOWN;\n version = undefined;\n }\n\n this._name = name;\n this._version = version;\n }\n\n /**\n * Gets current browser name.\n * @returns {string}\n */\n getName() {\n return this._name;\n }\n\n /**\n * Checks if current browser is Chrome.\n * @returns {boolean}\n */\n isChrome() {\n return this._name === CHROME;\n }\n\n /**\n * Checks if current browser is Opera.\n * @returns {boolean}\n */\n isOpera() {\n return this._name === OPERA;\n }\n\n /**\n * Checks if current browser is Firefox.\n * @returns {boolean}\n */\n isFirefox() {\n return this._name === FIREFOX;\n }\n\n /**\n * Checks if current browser is Internet Explorer.\n * @returns {boolean}\n */\n isIExplorer() {\n return this._name === INTERNET_EXPLORER;\n }\n\n /**\n * Checks if current browser is Safari.\n * @returns {boolean}\n */\n isSafari() {\n return this._name === SAFARI;\n }\n\n /**\n * Checks if current environment is NWJS.\n * @returns {boolean}\n */\n isNWJS() {\n return this._name === NWJS;\n }\n\n /**\n * Checks if current environment is Electron.\n * @returns {boolean}\n */\n isElectron() {\n return this._name === ELECTRON;\n }\n\n /**\n * Checks if current environment is React Native.\n * @returns {boolean}\n */\n isReactNative() {\n return this._name === REACT_NATIVE;\n }\n\n /**\n * Returns the version of the current browser.\n * @returns {string}\n */\n getVersion() {\n return this._version;\n }\n\n /**\n * Check if the parsed browser matches the passed condition.\n *\n * @param {Object} checkTree - It's one or two layered object, which can include a\n * platform or an OS on the first layer and should have browsers specs on the\n * bottom layer.\n * Eg. { chrome: '>71.1.0' }\n * { windows: { chrome: '<70.2' } }\n * @returns {boolean | undefined} - Returns true if the browser satisfies the set\n * conditions, false if not and undefined when the browser is not defined in the\n * checktree object or when the current browser's version is unknown.\n * @private\n */\n _checkCondition(checkTree) {\n if (this._version) {\n return this._bowser.satisfies(checkTree);\n }\n }\n\n /**\n * Compares the passed version with the current browser version.\n *\n * @param {*} version - The version to compare with. Anything different\n * than string will be converted to string.\n * @returns {boolean|undefined} - Returns true if the current version is\n * greater than the passed version and false otherwise. Returns undefined if\n * the current browser version is unknown.\n */\n isVersionGreaterThan(version) {\n return this._checkCondition({ [this._name]: `>${version}` });\n }\n\n /**\n * Compares the passed version with the current browser version.\n *\n * @param {*} version - The version to compare with. Anything different\n * than string will be converted to string.\n * @returns {boolean|undefined} - Returns true if the current version is\n * lower than the passed version and false otherwise. Returns undefined if\n * the current browser version is unknown.\n */\n isVersionLessThan(version) {\n return this._checkCondition({ [this._name]: `<${version}` });\n }\n\n /**\n * Compares the passed version with the current browser version.\n *\n * @param {*} version - The version to compare with. Anything different\n * than string will be converted to string.\n * @returns {boolean|undefined} - Returns true if the current version is\n * equal to the passed version and false otherwise. Returns undefined if\n * the current browser version is unknown.\n * A loose-equality operator is used here so that it matches the sub-versions as well.\n */\n isVersionEqualTo(version) {\n return this._checkCondition({ [this._name]: `~${version}` });\n }\n}\n","import EventEmitter from 'events';\n\n/**\n * Dummy implementation of Storage interface.\n */\nclass DummyLocalStorage extends EventEmitter {\n\n /**\n * The object used for storage.\n */\n _storage = {};\n\n /**\n * Empties all keys out of the storage.\n *\n * @returns {void}\n */\n clear() {\n this._storage = {};\n }\n\n /**\n * Returns the number of data items stored in the Storage object.\n *\n * @returns {number} - The number of data items stored in the Storage object.\n */\n get length() {\n return Object.keys(this._storage).length;\n }\n\n /**\n * Will return that key's value associated to the passed key name.\n *\n * @param {string} keyName - The key name.\n * @returns {*} - The key value.\n */\n getItem(keyName) {\n return this._storage[keyName];\n }\n\n /**\n * When passed a key name and value, will add that key to the storage,\n * or update that key's value if it already exists.\n *\n * @param {string} keyName - The key name.\n * @param {*} keyValue - The key value.\n * @returns {void}\n */\n setItem(keyName, keyValue) {\n this._storage[keyName] = keyValue;\n }\n\n /**\n * When passed a key name, will remove that key from the storage.\n *\n * @param {string} keyName - The key name.\n * @returns {void}\n */\n removeItem(keyName) {\n delete this._storage[keyName];\n }\n\n /**\n * When passed a number n, this method will return the name of the nth key in the storage.\n *\n * @param {number} idx - The index of the key.\n * @returns {string} - The nth key name.\n */\n key(n) {\n const keys = Object.keys(this._storage);\n\n if (keys.length <= n) {\n return undefined;\n }\n\n return keys[n];\n }\n\n /**\n * Serializes the content of the storage.\n *\n * @returns {string} - The serialized content.\n */\n serialize() {\n return JSON.stringify(this._storage);\n }\n}\n\n/**\n * Wrapper class for browser's local storage object.\n */\nclass JitsiLocalStorage extends EventEmitter {\n /**\n * @constructor\n * @param {Storage} storage browser's local storage object.\n */\n constructor() {\n super();\n\n try {\n this._storage = window.localStorage;\n this._localStorageDisabled = false;\n } catch (ignore) {\n // localStorage throws an exception.\n }\n\n if (!this._storage) { // Handles the case when window.localStorage is undefined or throws an exception.\n console.warn('Local storage is disabled.');\n this._storage = new DummyLocalStorage();\n this._localStorageDisabled = true;\n }\n }\n\n /**\n * Returns true if window.localStorage is disabled and false otherwise.\n *\n * @returns {boolean} - True if window.localStorage is disabled and false otherwise.\n */\n isLocalStorageDisabled() {\n return this._localStorageDisabled;\n }\n\n /**\n * Empties all keys out of the storage.\n *\n * @returns {void}\n */\n clear() {\n this._storage.clear();\n this.emit('changed');\n }\n\n /**\n * Returns the number of data items stored in the Storage object.\n *\n * @returns {number} - The number of data items stored in the Storage object.\n */\n get length() {\n return this._storage.length;\n }\n\n /**\n * Returns that passed key's value.\n * @param {string} keyName the name of the key you want to retrieve\n * the value of.\n * @returns {String|null} the value of the key. If the key does not exist,\n * null is returned.\n */\n getItem(keyName) {\n return this._storage.getItem(keyName);\n }\n\n /**\n * Adds a key to the storage, or update key's value if it already exists.\n * @param {string} keyName - the name of the key you want to create/update.\n * @param {string} keyValue - the value you want to give the key you are\n * creating/updating.\n * @param {boolean} dontEmitChangedEvent - If true a changed event won't be emitted.\n */\n setItem(keyName, keyValue, dontEmitChangedEvent = false) {\n this._storage.setItem(keyName, keyValue);\n\n if (!dontEmitChangedEvent) {\n this.emit('changed');\n }\n }\n\n /**\n * Remove a key from the storage.\n * @param {string} keyName the name of the key you want to remove.\n */\n removeItem(keyName) {\n this._storage.removeItem(keyName);\n this.emit('changed');\n }\n\n /**\n * Returns the name of the nth key in the list, or null if n is greater\n * than or equal to the number of key/value pairs in the object.\n *\n * @param {number} i - The index of the key in the list.\n * @returns {string}\n */\n key(i) {\n return this._storage.key(i);\n }\n\n /**\n * Serializes the content of the storage.\n *\n * @returns {string} - The serialized content.\n */\n serialize() {\n if (this.isLocalStorageDisabled()) {\n return this._storage.serialize();\n }\n\n const length = this._storage.length;\n const localStorageContent = {};\n\n for (let i = 0; i < length; i++) {\n const key = this._storage.key(i);\n\n localStorageContent[key] = this._storage.getItem(key);\n }\n\n return JSON.stringify(localStorageContent);\n }\n}\n\nexport const jitsiLocalStorage = new JitsiLocalStorage();\n","import { BrowserDetection } from '@jitsi/js-utils';\nimport { getLogger } from '@jitsi/logger';\n\nconst logger = getLogger(__filename);\n\n/* Minimum required Chrome / Chromium version. This applies also to derivatives. */\nconst MIN_REQUIRED_CHROME_VERSION = 72;\nconst MIN_REQUIRED_SAFARI_VERSION = 14;\nconst MIN_REQUIRED_IOS_VERSION = 14;\n\n// TODO: Move this code to js-utils.\n\n// NOTE: Now we are extending BrowserDetection in order to preserve\n// RTCBrowserType interface but maybe it worth exporting BrowserCapabilities\n// and BrowserDetection as separate objects in future.\n\n/**\n * Implements browser capabilities for lib-jitsi-meet.\n */\nexport default class BrowserCapabilities extends BrowserDetection {\n /**\n * Creates new BrowserCapabilities instance.\n */\n constructor() {\n super();\n logger.info(\n `This appears to be ${this.getName()}, ver: ${this.getVersion()}`);\n }\n\n /**\n * Tells whether or not the MediaStream/tt> is removed from the PeerConnection and disposed on video\n * mute (in order to turn off the camera device). This is needed on Firefox because of the following bug\n * https://bugzilla.mozilla.org/show_bug.cgi?id=1735951\n *\n * @return {boolean} true if the current browser supports this strategy or false otherwise.\n */\n doesVideoMuteByStreamRemove() {\n return this.isChromiumBased() || this.isWebKitBased() || this.isFirefox();\n }\n\n /**\n * Checks if the current browser is Chromium based, i.e., it's either Chrome / Chromium or uses it as its engine,\n * but doesn't identify as Chrome.\n *\n * This includes the following browsers:\n * - Chrome and Chromium.\n * - Other browsers which use the Chrome engine, but are detected as Chrome, such as Brave and Vivaldi.\n * - Browsers which are NOT Chrome but use it as their engine, and have custom detection code: Opera, Electron\n * and NW.JS.\n * This excludes\n * - Chrome on iOS since it uses WKWebView.\n */\n isChromiumBased() {\n return (this.isChrome()\n || this.isElectron()\n || this.isNWJS()\n || this.isOpera())\n && !this.isWebKitBased();\n }\n\n /**\n * Checks if the current platform is iOS.\n *\n * @returns {boolean}\n */\n isIosBrowser() {\n const { userAgent, maxTouchPoints, platform } = navigator;\n\n return Boolean(userAgent.match(/iP(ad|hone|od)/i))\n || (maxTouchPoints && maxTouchPoints > 2 && /MacIntel/.test(platform));\n }\n\n /**\n * Checks if the current browser is WebKit based. It's either\n * Safari or uses WebKit as its engine.\n *\n * This includes Chrome and Firefox on iOS\n *\n * @returns {boolean}\n */\n isWebKitBased() {\n // https://trac.webkit.org/changeset/236144/webkit/trunk/LayoutTests/webrtc/video-addLegacyTransceiver.html\n return this._bowser.isEngine('webkit')\n && typeof navigator.mediaDevices !== 'undefined'\n && typeof navigator.mediaDevices.getUserMedia !== 'undefined'\n && typeof window.RTCRtpTransceiver !== 'undefined'\n // eslint-disable-next-line no-undef\n && Object.keys(RTCRtpTransceiver.prototype).indexOf('currentDirection') > -1;\n }\n\n /**\n * Checks whether current running context is a Trusted Web Application.\n *\n * @returns {boolean} Whether the current context is a TWA.\n */\n isTwa() {\n return 'matchMedia' in window && window.matchMedia('(display-mode:standalone)').matches;\n }\n\n /**\n * Checks if the current browser is supported.\n *\n * @returns {boolean} true if the browser is supported, false otherwise.\n */\n isSupported() {\n if (this.isSafari() && this._getSafariVersion() < MIN_REQUIRED_SAFARI_VERSION) {\n return false;\n }\n\n return (this.isChromiumBased() && this._getChromiumBasedVersion() >= MIN_REQUIRED_CHROME_VERSION)\n || this.isFirefox()\n || this.isReactNative()\n || this.isWebKitBased();\n }\n\n /**\n * Returns whether the browser is supported for Android\n * @returns {boolean} true if the browser is supported for Android devices\n */\n isSupportedAndroidBrowser() {\n return this.isChromiumBased() || this.isFirefox();\n }\n\n /**\n * Returns whether the browser is supported for iOS\n * @returns {boolean} true if the browser is supported for iOS devices\n */\n isSupportedIOSBrowser() {\n return this._getIOSVersion() >= MIN_REQUIRED_IOS_VERSION;\n }\n\n /**\n * Returns whether or not the current environment needs a user interaction\n * with the page before any unmute can occur.\n *\n * @returns {boolean}\n */\n isUserInteractionRequiredForUnmute() {\n return this.isFirefox() && this.isVersionLessThan('68');\n }\n\n /**\n * Checks if the current browser triggers 'onmute'/'onunmute' events when\n * user's connection is interrupted and the video stops playback.\n * @returns {*|boolean} 'true' if the event is supported or 'false'\n * otherwise.\n */\n supportsVideoMuteOnConnInterrupted() {\n return this.isChromiumBased() || this.isReactNative();\n }\n\n /**\n * Checks if the current browser reports upload and download bandwidth\n * statistics.\n * @return {boolean}\n */\n supportsBandwidthStatistics() {\n // FIXME bandwidth stats are currently not implemented for FF on our\n // side, but not sure if not possible ?\n return !this.isFirefox() && !this.isWebKitBased();\n }\n\n /**\n * Checks if the current browser supports setting codec preferences on the transceiver.\n * @returns {boolean}\n */\n supportsCodecPreferences() {\n return Boolean(window.RTCRtpTransceiver\n && 'setCodecPreferences' in window.RTCRtpTransceiver.prototype\n && window.RTCRtpReceiver\n && typeof window.RTCRtpReceiver.getCapabilities !== 'undefined')\n\n // this is not working on Safari because of the following bug\n // https://bugs.webkit.org/show_bug.cgi?id=215567\n && !this.isWebKitBased();\n }\n\n /**\n * Checks if the current browser support the device change event.\n * @return {boolean}\n */\n supportsDeviceChangeEvent() {\n return navigator.mediaDevices\n && typeof navigator.mediaDevices.ondevicechange !== 'undefined'\n && typeof navigator.mediaDevices.addEventListener !== 'undefined';\n }\n\n /**\n * Checks if the current browser supports RTT statistics for srflx local\n * candidates through the legacy getStats() API.\n */\n supportsLocalCandidateRttStatistics() {\n return this.isChromiumBased() || this.isReactNative() || this.isWebKitBased();\n }\n\n /**\n * Checks if the current browser supports the Long Tasks API that lets us observe\n * performance measurement events and be notified of tasks that take longer than\n * 50ms to execute on the main thread.\n */\n supportsPerformanceObserver() {\n return typeof window.PerformanceObserver !== 'undefined'\n && PerformanceObserver.supportedEntryTypes.indexOf('longtask') > -1;\n }\n\n /**\n * Checks if the current browser supports audio level stats on the receivers.\n */\n supportsReceiverStats() {\n return typeof window.RTCRtpReceiver !== 'undefined'\n && Object.keys(RTCRtpReceiver.prototype).indexOf('getSynchronizationSources') > -1\n\n // Disable this on Safari because it is reporting 0.000001 as the audio levels for all\n // remote audio tracks.\n && !this.isWebKitBased();\n }\n\n /**\n * Checks if the current browser reports round trip time statistics for\n * the ICE candidate pair.\n * @return {boolean}\n */\n supportsRTTStatistics() {\n // Firefox does not seem to report RTT for ICE candidate pair:\n // eslint-disable-next-line max-len\n // https://www.w3.org/TR/webrtc-stats/#dom-rtcicecandidatepairstats-currentroundtriptime\n // It does report mozRTT for RTP streams, but at the time of this\n // writing it's value does not make sense most of the time\n // (is reported as 1):\n // https://bugzilla.mozilla.org/show_bug.cgi?id=1241066\n // For Chrome and others we rely on 'googRtt'.\n return !this.isFirefox();\n }\n\n /**\n * Returns true if VP9 is supported by the client on the browser. VP9 is currently disabled on Firefox and Safari\n * because of issues with rendering. Please check https://bugzilla.mozilla.org/show_bug.cgi?id=1492500,\n * https://bugs.webkit.org/show_bug.cgi?id=231071 and https://bugs.webkit.org/show_bug.cgi?id=231074 for details.\n */\n supportsVP9() {\n return this.isChromiumBased() || this.isReactNative();\n }\n\n /**\n * Checks if the browser uses SDP munging for turning on simulcast.\n *\n * @returns {boolean}\n */\n usesSdpMungingForSimulcast() {\n return this.isChromiumBased() || this.isReactNative() || this.isWebKitBased();\n }\n\n /**\n * Checks if the browser uses webrtc-adapter. All browsers except React Native do.\n *\n * @returns {boolean}\n */\n usesAdapter() {\n return !this.isReactNative();\n }\n\n /**\n * Checks if the browser uses RIDs/MIDs for siganling the simulcast streams\n * to the bridge instead of the ssrcs.\n */\n usesRidsForSimulcast() {\n return false;\n }\n\n /**\n * Checks if the browser supports getDisplayMedia.\n * @returns {boolean} {@code true} if the browser supports getDisplayMedia.\n */\n supportsGetDisplayMedia() {\n return typeof navigator.getDisplayMedia !== 'undefined'\n || (typeof navigator.mediaDevices !== 'undefined'\n && typeof navigator.mediaDevices.getDisplayMedia\n !== 'undefined');\n }\n\n /**\n * Checks if the browser supports WebRTC Encoded Transform, an alternative\n * to insertable streams.\n *\n * NOTE: At the time of this writing the only browser supporting this is\n * Safari / WebKit, behind a flag.\n *\n * @returns {boolean} {@code true} if the browser supports it.\n */\n supportsEncodedTransform() {\n return Boolean(window.RTCRtpScriptTransform);\n }\n\n /**\n * Checks if the browser supports insertable streams, needed for E2EE.\n * @returns {boolean} {@code true} if the browser supports insertable streams.\n */\n supportsInsertableStreams() {\n if (!(typeof window.RTCRtpSender !== 'undefined'\n && window.RTCRtpSender.prototype.createEncodedStreams)) {\n return false;\n }\n\n // Feature-detect transferable streams which we need to operate in a worker.\n // See https://groups.google.com/a/chromium.org/g/blink-dev/c/1LStSgBt6AM/m/hj0odB8pCAAJ\n const stream = new ReadableStream();\n\n try {\n window.postMessage(stream, '*', [ stream ]);\n\n return true;\n } catch {\n return false;\n }\n }\n\n /**\n * Whether the browser supports the RED format for audio.\n */\n supportsAudioRed() {\n return Boolean(window.RTCRtpSender\n && window.RTCRtpSender.getCapabilities\n && window.RTCRtpSender.getCapabilities('audio').codecs.some(codec => codec.mimeType === 'audio/red')\n && window.RTCRtpReceiver\n && window.RTCRtpReceiver.getCapabilities\n && window.RTCRtpReceiver.getCapabilities('audio').codecs.some(codec => codec.mimeType === 'audio/red'));\n }\n\n /**\n * Checks if the browser supports unified plan.\n *\n * @returns {boolean}\n */\n supportsUnifiedPlan() {\n return !this.isReactNative();\n }\n\n /**\n * Checks if the browser supports voice activity detection via the @type {VADAudioAnalyser} service.\n *\n * @returns {boolean}\n */\n supportsVADDetection() {\n return this.isChromiumBased();\n }\n\n /**\n * Check if the browser supports the RTP RTX feature (and it is usable).\n *\n * @returns {boolean}\n */\n supportsRTX() {\n // Disable RTX on Firefox up to 96 because we prefer simulcast over RTX\n // see https://bugzilla.mozilla.org/show_bug.cgi?id=1738504\n return !(this.isFirefox() && this.isVersionLessThan('96'));\n }\n\n /**\n * Returns the version of a Chromium based browser.\n *\n * @returns {Number}\n */\n _getChromiumBasedVersion() {\n if (this.isChromiumBased()) {\n // NW.JS doesn't expose the Chrome version in the UA string.\n if (this.isNWJS()) {\n // eslint-disable-next-line no-undef\n return Number.parseInt(process.versions.chromium, 10);\n }\n\n // Here we process all browsers which use the Chrome engine but\n // don't necessarily identify as Chrome. We cannot use the version\n // comparing functions because the Electron, Opera and NW.JS\n // versions are inconsequential here, as we need to know the actual\n // Chrome engine version.\n const ua = navigator.userAgent;\n\n if (ua.match(/Chrome/)) {\n const version\n = Number.parseInt(ua.match(/Chrome\\/([\\d.]+)/)[1], 10);\n\n return version;\n }\n }\n\n return -1;\n }\n\n /**\n * Returns the version of a Safari browser.\n *\n * @returns {Number}\n */\n _getSafariVersion() {\n if (this.isSafari()) {\n return Number.parseInt(this.getVersion(), 10);\n }\n\n return -1;\n }\n\n /**\n * Returns the version of an ios browser.\n *\n * @returns {Number}\n */\n _getIOSVersion() {\n if (this.isWebKitBased()) {\n return Number.parseInt(this.getVersion(), 10);\n }\n\n return -1;\n }\n}\n","import BrowserCapabilities from './BrowserCapabilities';\n\nexport default new BrowserCapabilities();\n","import { getLogger } from '@jitsi/logger';\n\nimport {\n TYPE_OPERATIONAL,\n TYPE_PAGE,\n TYPE_TRACK,\n TYPE_UI\n} from '../../service/statistics/AnalyticsEvents';\nimport browser from '../browser';\n\nconst MAX_CACHE_SIZE = 100;\n\n// eslist-disable-line no-undef\nconst logger = getLogger(__filename);\n\n/**\n * This class provides an API to lib-jitsi-meet and its users for sending\n * analytics events. It serves as a bridge to different backend implementations\n * (\"analytics handlers\") and a cache for events attempted to be sent before\n * the analytics handlers were enabled.\n *\n * The API is designed to be an easy replacement for the previous version of\n * this adapter, and is meant to be extended with more convenience methods.\n *\n *\n * The API calls are translated to objects with the following structure, which\n * are then passed to the sendEvent(event) function of the underlying handlers:\n *\n * {\n * type,\n *\n * action,\n * actionSubject,\n * actionSubjectId,\n * attributes,\n * categories,\n * containerId,\n * containerType,\n * name,\n * objectId,\n * objectType,\n * source,\n * tags\n * }\n *\n * The 'type' is one of 'operational', 'page', 'track' or 'ui', and some of the\n * other properties are considered required according to the type.\n *\n * For events with type 'page', the required properties are: name.\n *\n * For events with type 'operational' and 'ui', the required properties are:\n * action, actionSubject, source\n *\n * For events with type 'page', the required properties are:\n * action, actionSubject, source, containerType, containerId, objectType,\n * objectId\n */\nclass AnalyticsAdapter {\n /**\n * Creates new AnalyticsAdapter instance.\n */\n constructor() {\n this.reset();\n }\n\n /**\n * Reset the state to the initial one.\n *\n * @returns {void}\n */\n reset() {\n /**\n * Whether this AnalyticsAdapter has been disposed of or not. Once this\n * is set to true, the AnalyticsAdapter is disabled and does not accept\n * any more events, and it can not be re-enabled.\n * @type {boolean}\n */\n this.disposed = false;\n\n /**\n * The set of handlers to which events will be sent.\n * @type {Set}\n */\n this.analyticsHandlers = new Set();\n\n /**\n * The cache of events which are not sent yet. The cache is enabled\n * while this field is truthy, and disabled otherwise.\n * @type {Array}\n */\n this.cache = [];\n\n /**\n * Map of properties that will be added to every event. Note that the\n * keys will be prefixed with \"permanent.\".\n */\n this.permanentProperties = {};\n\n /**\n * The name of the conference that this AnalyticsAdapter is associated\n * with.\n * @type {null}\n */\n this.conferenceName = '';\n\n this.addPermanentProperties({\n 'user_agent': navigator.userAgent,\n 'browser_name': browser.getName()\n });\n }\n\n /**\n * Dispose analytics. Clears all handlers.\n */\n dispose() {\n logger.warn('Disposing of analytics adapter.');\n\n if (this.analyticsHandlers && this.analyticsHandlers.size > 0) {\n this.analyticsHandlers.forEach(handler => {\n if (typeof handler.dispose === 'function') {\n handler.dispose();\n }\n });\n }\n\n this.setAnalyticsHandlers([]);\n this.disposed = true;\n }\n\n /**\n * Sets the handlers that are going to be used to send analytics. Sends any\n * cached events.\n * @param {Array} handlers the handlers\n */\n setAnalyticsHandlers(handlers) {\n if (this.disposed) {\n return;\n }\n\n this.analyticsHandlers = new Set(handlers);\n\n this._setUserProperties();\n\n // Note that we disable the cache even if the set of handlers is empty.\n const cache = this.cache;\n\n this.cache = null;\n if (cache) {\n cache.forEach(event => this._sendEvent(event));\n }\n }\n\n /**\n * Set the user properties to the analytics handlers.\n *\n * @returns {void}\n */\n _setUserProperties() {\n this.analyticsHandlers.forEach(handler => {\n try {\n handler.setUserProperties(this.permanentProperties);\n } catch (error) {\n logger.warn('Error in setUserProperties method of one of the '\n + `analytics handlers: ${error}`);\n }\n });\n }\n\n /**\n * Adds a set of permanent properties to this this AnalyticsAdapter.\n * Permanent properties will be added as \"attributes\" to events sent to\n * the underlying \"analytics handlers\", and their keys will be prefixed\n * by \"permanent_\", i.e. adding a permanent property {key: \"value\"} will\n * result in {\"permanent_key\": \"value\"} object to be added to the\n * \"attributes\" field of events.\n *\n * @param {Object} properties the properties to add\n */\n addPermanentProperties(properties) {\n this.permanentProperties = {\n ...this.permanentProperties,\n ...properties\n };\n\n this._setUserProperties();\n }\n\n /**\n * Sets the name of the conference that this AnalyticsAdapter is associated\n * with.\n * @param name the name to set.\n */\n setConferenceName(name) {\n this.conferenceName = name;\n this.addPermanentProperties({ 'conference_name': name });\n }\n\n /**\n * Sends an event with a given name and given properties. The first\n * parameter is either a string or an object. If it is a string, it is used\n * as the event name and the second parameter is used at the attributes to\n * attach to the event. If it is an object, it represents the whole event,\n * including any desired attributes, and the second parameter is ignored.\n *\n * @param {String|Object} eventName either a string to be used as the name\n * of the event, or an event object. If an event object is passed, the\n * properties parameters is ignored.\n * @param {Object} properties the properties/attributes to attach to the\n * event, if eventName is a string.\n */\n sendEvent(eventName, properties = {}) {\n if (this.disposed) {\n return;\n }\n\n let event = null;\n\n if (typeof eventName === 'string') {\n event = {\n type: TYPE_OPERATIONAL,\n action: eventName,\n actionSubject: eventName,\n source: eventName,\n attributes: properties\n };\n } else if (typeof eventName === 'object') {\n event = eventName;\n }\n\n if (!this._verifyRequiredFields(event)) {\n logger.error(\n `Dropping a mis-formatted event: ${JSON.stringify(event)}`);\n\n return;\n }\n\n this._sendEvent(event);\n }\n\n /**\n * Checks whether an event has all of the required fields set, and tries\n * to fill in some of the missing fields with reasonable default values.\n * Returns true if after this operation the event has all of the required\n * fields set, and false otherwise (if some of the required fields were not\n * set and the attempt to fill them in with a default failed).\n *\n * @param event the event object.\n * @return {boolean} true if the event (after the call to this function)\n * contains all of the required fields, and false otherwise.\n * @private\n */\n _verifyRequiredFields(event) {\n if (!event) {\n return false;\n }\n\n if (!event.type) {\n event.type = TYPE_OPERATIONAL;\n }\n\n const type = event.type;\n\n if (type !== TYPE_OPERATIONAL && type !== TYPE_PAGE\n && type !== TYPE_UI && type !== TYPE_TRACK) {\n logger.error(`Unknown event type: ${type}`);\n\n return false;\n }\n\n if (type === TYPE_PAGE) {\n return Boolean(event.name);\n }\n\n // Try to set some reasonable default values in case some of the\n // parameters required by the handler API are missing.\n event.action = event.action || event.name || event.actionSubject;\n event.actionSubject = event.actionSubject || event.name || event.action;\n event.source = event.source || event.name || event.action\n || event.actionSubject;\n\n if (!event.action || !event.actionSubject || !event.source) {\n logger.error(\n 'Required field missing (action, actionSubject or source)');\n\n return false;\n }\n\n // Track events have additional required fields.\n if (type === TYPE_TRACK) {\n event.objectType = event.objectType || 'generic-object-type';\n event.containerType = event.containerType || 'conference';\n if (event.containerType === 'conference' && !event.containerId) {\n event.containerId = this.conferenceName;\n }\n\n\n if (!event.objectType || !event.objectId\n || !event.containerType || !event.containerId) {\n logger.error(\n 'Required field missing (containerId, containerType, '\n + 'objectId or objectType)');\n\n return false;\n }\n }\n\n return true;\n }\n\n /**\n * Saves an event to the cache, if the cache is enabled.\n * @param event the event to save.\n * @returns {boolean} true if the event was saved, and false otherwise (i.e.\n * if the cache was disabled).\n * @private\n */\n _maybeCacheEvent(event) {\n if (this.cache) {\n this.cache.push(event);\n\n // We limit the size of the cache, in case the user fails to ever\n // set the analytics handlers.\n if (this.cache.length > MAX_CACHE_SIZE) {\n this.cache.splice(0, 1);\n }\n\n return true;\n }\n\n return false;\n\n }\n\n /**\n *\n * @param event\n * @private\n */\n _sendEvent(event) {\n if (this._maybeCacheEvent(event)) {\n // The event was consumed by the cache.\n } else {\n this.analyticsHandlers.forEach(handler => {\n try {\n handler.sendEvent(event);\n } catch (e) {\n logger.warn(`Error sending analytics event: ${e}`);\n }\n });\n }\n }\n}\n\nexport default new AnalyticsAdapter();\n","/* global callstats */\n\nimport browser from '../browser';\nimport GlobalOnErrorHandler from '../util/GlobalOnErrorHandler';\n\nconst logger = require('@jitsi/logger').getLogger(__filename);\n\n/**\n * We define enumeration of wrtcFuncNames as we need them before\n * callstats is initialized to queue events.\n * @const\n * @see http://www.callstats.io/api/#enumeration-of-wrtcfuncnames\n */\nconst wrtcFuncNames = {\n createOffer: 'createOffer',\n createAnswer: 'createAnswer',\n setLocalDescription: 'setLocalDescription',\n setRemoteDescription: 'setRemoteDescription',\n addIceCandidate: 'addIceCandidate',\n getUserMedia: 'getUserMedia',\n iceConnectionFailure: 'iceConnectionFailure',\n signalingError: 'signalingError',\n applicationLog: 'applicationLog'\n};\n\n/**\n * We define enumeration of fabricEvent as we need them before\n * callstats is initialized to queue events.\n * @const\n * @see http://www.callstats.io/api/#enumeration-of-fabricevent\n */\nconst fabricEvent = {\n fabricHold: 'fabricHold',\n fabricResume: 'fabricResume',\n audioMute: 'audioMute',\n audioUnmute: 'audioUnmute',\n videoPause: 'videoPause',\n videoResume: 'videoResume',\n fabricUsageEvent: 'fabricUsageEvent',\n fabricStats: 'fabricStats',\n fabricTerminated: 'fabricTerminated',\n screenShareStart: 'screenShareStart',\n screenShareStop: 'screenShareStop',\n dominantSpeaker: 'dominantSpeaker',\n activeDeviceList: 'activeDeviceList'\n};\n\n/**\n * The user id to report to callstats as destination.\n * @type {string}\n */\nconst DEFAULT_REMOTE_USER = 'jitsi';\n\n/**\n * Type of pending reports, can be event or an error.\n * @type {{ERROR: string, EVENT: string}}\n */\nconst reportType = {\n ERROR: 'error',\n EVENT: 'event',\n MST_WITH_USERID: 'mstWithUserID'\n};\n\n/**\n * Set of currently existing {@link CallStats} instances.\n * @type {Set}\n */\nlet _fabrics;\n\n/**\n * An instance of this class is a wrapper for the CallStats API fabric. A fabric\n * reports one peer connection to the CallStats backend and is allocated with\n * {@link callstats.addNewFabric}. It has a bunch of instance methods for\n * reporting various events. A fabric is considered disposed when\n * {@link CallStats.sendTerminateEvent} is executed.\n *\n * Currently only one backend instance can be created ever and it's done using\n * {@link CallStats.initBackend}. At the time of this writing there is no way to\n * explicitly shutdown the backend, but it's supposed to close it's connection\n * automatically, after all fabrics have been terminated.\n */\nexport default class CallStats {\n /**\n * A callback passed to {@link callstats.addNewFabric}.\n * @param {string} error 'success' means ok\n * @param {string} msg some more details\n * @private\n */\n static _addNewFabricCallback(error, msg) {\n if (CallStats.backend && error !== 'success') {\n logger.error(`Monitoring status: ${error} msg: ${msg}`);\n }\n }\n\n /**\n * Callback passed to {@link callstats.initialize} (backend initialization)\n * @param {string} error 'success' means ok\n * @param {String} msg\n * @private\n */\n static _initCallback(error, msg) {\n logger.log(`CallStats Status: err=${error} msg=${msg}`);\n\n // there is no lib, nothing to report to\n if (error !== 'success') {\n return;\n }\n\n CallStats.backendInitialized = true;\n\n // I hate that\n let atLeastOneFabric = false;\n let defaultInstance = null;\n\n for (const callStatsInstance of CallStats.fabrics.values()) {\n if (!callStatsInstance.hasFabric) {\n logger.debug('addNewFabric - initCallback');\n if (callStatsInstance._addNewFabric()) {\n atLeastOneFabric = true;\n if (!defaultInstance) {\n defaultInstance = callStatsInstance;\n }\n }\n }\n }\n\n if (!atLeastOneFabric) {\n return;\n }\n\n CallStats._emptyReportQueue(defaultInstance);\n }\n\n /**\n * Empties report queue.\n *\n * @param {CallStats} csInstance - The callstats instance.\n * @private\n */\n static _emptyReportQueue(csInstance) {\n // There is no conference ID nor a PeerConnection available when some of\n // the events are scheduled on the reportsQueue, so those will be\n // reported on the first initialized fabric.\n const defaultConfID = csInstance.confID;\n const defaultPC = csInstance.peerconnection;\n\n // notify callstats about failures if there were any\n for (const report of CallStats.reportsQueue) {\n if (report.type === reportType.ERROR) {\n const errorData = report.data;\n\n CallStats._reportError(\n csInstance,\n errorData.type,\n errorData.error,\n errorData.pc || defaultPC);\n } else if (report.type === reportType.EVENT) {\n // if we have and event to report and we failed to add\n // fabric this event will not be reported anyway, returning\n // an error\n const eventData = report.data;\n\n CallStats.backend.sendFabricEvent(\n report.pc || defaultPC,\n eventData.event,\n defaultConfID,\n eventData.eventData);\n } else if (report.type === reportType.MST_WITH_USERID) {\n const data = report.data;\n\n CallStats.backend.associateMstWithUserID(\n report.pc || defaultPC,\n data.callStatsId,\n defaultConfID,\n data.ssrc,\n data.usageLabel,\n data.containerId\n );\n }\n }\n CallStats.reportsQueue.length = 0;\n }\n\n /* eslint-disable max-params */\n /**\n * Reports an error to callstats.\n *\n * @param {CallStats} [cs]\n * @param type the type of the error, which will be one of the wrtcFuncNames\n * @param error the error\n * @param pc the peerconnection\n * @private\n */\n static _reportError(cs, type, error, pc) {\n let _error = error;\n\n if (!_error) {\n logger.warn('No error is passed!');\n _error = new Error('Unknown error');\n }\n if (CallStats.backendInitialized && cs) {\n CallStats.backend.reportError(pc, cs.confID, type, _error);\n } else {\n CallStats.reportsQueue.push({\n type: reportType.ERROR,\n data: {\n error: _error,\n pc,\n type\n }\n });\n }\n\n // else just ignore it\n }\n\n /* eslint-enable max-params */\n\n /**\n * Reports an error to callstats.\n *\n * @param {CallStats} cs\n * @param event the type of the event, which will be one of the fabricEvent\n * @param eventData additional data to pass to event\n * @private\n */\n static _reportEvent(cs, event, eventData) {\n const pc = cs && cs.peerconnection;\n const confID = cs && cs.confID;\n\n if (CallStats.backendInitialized && cs) {\n CallStats.backend.sendFabricEvent(pc, event, confID, eventData);\n } else {\n CallStats.reportsQueue.push({\n confID,\n pc,\n type: reportType.EVENT,\n data: { event,\n eventData }\n });\n }\n }\n\n /**\n * Wraps some of the CallStats API method and logs their calls with\n * arguments on the debug logging level. Also wraps some of the backend\n * methods execution into try catch blocks to not crash the app in case\n * there is a problem with the backend itself.\n * @param {callstats} theBackend\n * @private\n */\n static _traceAndCatchBackendCalls(theBackend) {\n const tryCatchMethods = [\n 'associateMstWithUserID',\n 'sendFabricEvent',\n 'sendUserFeedback'\n\n // 'reportError', - this one needs special handling - see code below\n ];\n\n for (const methodName of tryCatchMethods) {\n const originalMethod = theBackend[methodName];\n\n theBackend[methodName] = function(...theArguments) {\n try {\n return originalMethod.apply(theBackend, theArguments);\n } catch (e) {\n GlobalOnErrorHandler.callErrorHandler(e);\n }\n };\n }\n const debugMethods = [\n 'associateMstWithUserID',\n 'sendFabricEvent',\n 'sendUserFeedback'\n\n // 'reportError', - this one needs special handling - see code below\n ];\n\n for (const methodName of debugMethods) {\n const originalMethod = theBackend[methodName];\n\n theBackend[methodName] = function(...theArguments) {\n logger.debug(methodName, theArguments);\n originalMethod.apply(theBackend, theArguments);\n };\n }\n const originalReportError = theBackend.reportError;\n\n /* eslint-disable max-params */\n theBackend.reportError = function(pc, cs, type, ...args) {\n // Logs from the logger are submitted on the applicationLog event\n // \"type\". Logging the arguments on the logger will create endless\n // loop, because it will put all the logs to the logger queue again.\n if (type === wrtcFuncNames.applicationLog) {\n // NOTE otherArguments are not logged to the console on purpose\n // to not log the whole log batch\n // FIXME check the current logging level (currently not exposed\n // by the logger implementation)\n // NOTE it is not safe to log whole objects on react-native as\n // those contain too many circular references and may crash\n // the app.\n if (!browser.isReactNative()) {\n console && console.debug('reportError', pc, cs, type);\n }\n } else {\n logger.debug('reportError', pc, cs, type, ...args);\n }\n try {\n originalReportError.call(theBackend, pc, cs, type, ...args);\n } catch (exception) {\n if (type === wrtcFuncNames.applicationLog) {\n console && console.error('reportError', exception);\n } else {\n GlobalOnErrorHandler.callErrorHandler(exception);\n }\n }\n };\n\n /* eslint-enable max-params */\n }\n\n /**\n * Returns the Set with the currently existing {@link CallStats} instances.\n * Lazily initializes the Set to allow any Set polyfills to be applied.\n * @type {Set}\n */\n static get fabrics() {\n if (!_fabrics) {\n _fabrics = new Set();\n }\n\n return _fabrics;\n }\n\n /**\n * Initializes the CallStats backend. Should be called only if\n * {@link CallStats.isBackendInitialized} returns false.\n * @param {object} options\n * @param {String} options.callStatsID CallStats credentials - ID\n * @param {String} options.callStatsSecret CallStats credentials - secret\n * @param {string} options.aliasName the aliasName part of\n * the userID aka endpoint ID, see CallStats docs for more info.\n * @param {string} options.userName the userName part of\n * the userID aka display name, see CallStats docs for more info.\n * @param {object} options.configParams the set of parameters\n * to enable/disable certain features in the library. See CallStats docs for more info.\n *\n */\n static initBackend(options) {\n if (CallStats.backend) {\n throw new Error('CallStats backend has been initialized already!');\n }\n try {\n const CallStatsBackend = callstats;\n\n CallStats.backend = new CallStatsBackend();\n CallStats._traceAndCatchBackendCalls(CallStats.backend);\n CallStats.userID = {\n aliasName: options.aliasName,\n userName: options.userName\n };\n CallStats.callStatsID = options.callStatsID;\n CallStats.callStatsSecret = options.callStatsSecret;\n\n const configParams = { ...options.configParams };\n\n if (options.applicationName) {\n configParams.applicationVersion = `${options.applicationName} (${browser.getName()})`;\n }\n\n if (options.confID) {\n // we first check is there a tenant in the confID\n const match = options.confID.match(/.*\\/(.*)\\/.*/);\n\n // if there is no tenant, we will just set '/'\n configParams.siteID = options.siteID || (match && match[1]) || '/';\n }\n\n // userID is generated or given by the origin server\n CallStats.backend.initialize(\n CallStats.callStatsID,\n CallStats.callStatsSecret,\n CallStats.userID,\n CallStats._initCallback,\n undefined,\n configParams);\n\n const getWiFiStatsMethod = options.getWiFiStatsMethod;\n\n if (getWiFiStatsMethod) {\n CallStats.backend.attachWifiStatsHandler(getWiFiStatsMethod);\n\n getWiFiStatsMethod().then(result => {\n if (result) {\n logger.info('Reported wifi addresses:'\n , JSON.parse(result).addresses);\n }\n })\n .catch(() => {});// eslint-disable-line no-empty-function\n }\n\n return true;\n } catch (e) {\n // The callstats.io API failed to initialize (e.g. because its\n // download did not succeed in general or on time). Further attempts\n // to utilize it cannot possibly succeed.\n GlobalOnErrorHandler.callErrorHandler(e);\n CallStats.backend = null;\n logger.error(e);\n\n return false;\n }\n }\n\n /**\n * Checks if the CallStats backend has been created. It does not mean that\n * it has been initialized, but only that the API instance has been\n * allocated successfully.\n * @return {boolean} true if backend exists or false\n * otherwise\n */\n static isBackendInitialized() {\n return Boolean(CallStats.backend);\n }\n\n /**\n * Notifies CallStats about active device.\n * @param {{deviceList: {String:String}}} devicesData list of devices with\n * their data\n * @param {CallStats} cs callstats instance related to the event\n */\n static sendActiveDeviceListEvent(devicesData, cs) {\n CallStats._reportEvent(cs, fabricEvent.activeDeviceList, devicesData);\n }\n\n /**\n * Notifies CallStats that there is a log we want to report.\n *\n * @param {Error} e error to send or {String} message\n * @param {CallStats} cs callstats instance related to the error (optional)\n */\n static sendApplicationLog(e, cs) {\n try {\n CallStats._reportError(\n cs,\n wrtcFuncNames.applicationLog,\n e,\n cs && cs.peerconnection);\n } catch (error) {\n // If sendApplicationLog fails it should not be printed to\n // the logger, because it will try to push the logs again\n // (through sendApplicationLog) and an endless loop is created.\n if (console && (typeof console.error === 'function')) {\n // FIXME send analytics event as well\n console.error('sendApplicationLog failed', error);\n }\n }\n }\n\n /**\n * Sends the given feedback through CallStats.\n *\n * @param {string} conferenceID the conference ID for which the feedback\n * will be reported.\n * @param overall an integer between 1 and 5 indicating the\n * user feedback\n * @param comment detailed feedback from the user.\n */\n static sendFeedback(conferenceID, overall, comment) {\n return new Promise((resolve, reject) => {\n if (CallStats.backend) {\n CallStats.backend.sendUserFeedback(\n conferenceID,\n {\n userID: CallStats.userID,\n overall,\n comment\n },\n (status, message) => {\n if (status === 'success') {\n resolve(message);\n } else {\n reject(message);\n }\n });\n } else {\n const reason = 'Failed to submit feedback to CallStats - no backend';\n\n logger.error(reason);\n reject(reason);\n }\n });\n }\n\n /**\n * Notifies CallStats that getUserMedia failed.\n *\n * @param {Error} e error to send\n * @param {CallStats} cs callstats instance related to the error (optional)\n */\n static sendGetUserMediaFailed(e, cs) {\n CallStats._reportError(cs, wrtcFuncNames.getUserMedia, e, null);\n }\n\n /**\n * Notifies CallStats for mute events\n * @param mute {boolean} true for muted and false for not muted\n * @param type {String} \"audio\"/\"video\"\n * @param {CallStats} cs callstats instance related to the event\n */\n static sendMuteEvent(mute, type, cs) {\n let event;\n\n if (type === 'video') {\n event = mute ? fabricEvent.videoPause : fabricEvent.videoResume;\n } else {\n event = mute ? fabricEvent.audioMute : fabricEvent.audioUnmute;\n }\n\n CallStats._reportEvent(cs, event);\n }\n\n /**\n * Creates new CallStats instance that handles all callstats API calls for\n * given {@link TraceablePeerConnection}. Each instance is meant to handle\n * one CallStats fabric added with 'addFabric' API method for the\n * {@link TraceablePeerConnection} instance passed in the constructor.\n * @param {TraceablePeerConnection} tpc\n * @param {Object} options\n * @param {string} options.confID the conference ID that wil be used to\n * report the session.\n * @param {string} [options.remoteUserID='jitsi'] the remote user ID to\n * which given tpc is connected.\n */\n constructor(tpc, options) {\n this.confID = options.confID;\n this.tpc = tpc;\n this.peerconnection = tpc.peerconnection;\n this.remoteUserID = options.remoteUserID || DEFAULT_REMOTE_USER;\n this.hasFabric = false;\n\n CallStats.fabrics.add(this);\n\n if (CallStats.backendInitialized) {\n this._addNewFabric();\n\n // if this is the first fabric let's try to empty the\n // report queue. Reports all events that we recorded between\n // backend initialization and receiving the first fabric\n if (CallStats.fabrics.size === 1) {\n CallStats._emptyReportQueue(this);\n }\n }\n }\n\n /**\n * Initializes CallStats fabric by calling \"addNewFabric\" for\n * the peer connection associated with this instance.\n * @return {boolean} true if the call was successful or false otherwise.\n */\n _addNewFabric() {\n logger.info('addNewFabric', this.remoteUserID);\n try {\n const fabricAttributes = {\n remoteEndpointType:\n this.tpc.isP2P\n ? CallStats.backend.endpointType.peer\n : CallStats.backend.endpointType.server\n };\n const ret\n = CallStats.backend.addNewFabric(\n this.peerconnection,\n this.remoteUserID,\n CallStats.backend.fabricUsage.multiplex,\n this.confID,\n fabricAttributes,\n CallStats._addNewFabricCallback);\n\n this.hasFabric = true;\n\n const success = ret.status === 'success';\n\n if (!success) {\n logger.error('callstats fabric not initilized', ret.message);\n }\n\n return success;\n\n } catch (error) {\n GlobalOnErrorHandler.callErrorHandler(error);\n\n return false;\n }\n }\n\n /* eslint-disable max-params */\n\n /**\n * Lets CallStats module know where is given SSRC rendered by providing\n * renderer tag ID.\n * If the lib is not initialized yet queue the call for later, when it's\n * ready.\n * @param {number} ssrc the SSRC of the stream\n * @param {boolean} isLocal indicates whether this the stream is local\n * @param {string|null} streamEndpointId if the stream is not local the it\n * needs to contain the stream owner's ID\n * @param {string} usageLabel meaningful usage label of this stream like\n * 'microphone', 'camera' or 'screen'.\n * @param {string} containerId the id of media 'audio' or 'video' tag which\n * renders the stream.\n */\n associateStreamWithVideoTag(\n ssrc,\n isLocal,\n streamEndpointId,\n usageLabel,\n containerId) {\n if (!CallStats.backend) {\n return;\n }\n\n const callStatsId = isLocal ? CallStats.userID : streamEndpointId;\n\n if (CallStats.backendInitialized) {\n CallStats.backend.associateMstWithUserID(\n this.peerconnection,\n callStatsId,\n this.confID,\n ssrc,\n usageLabel,\n containerId);\n } else {\n CallStats.reportsQueue.push({\n type: reportType.MST_WITH_USERID,\n pc: this.peerconnection,\n data: {\n callStatsId,\n containerId,\n ssrc,\n usageLabel\n }\n });\n }\n }\n\n /* eslint-enable max-params */\n\n /**\n * Notifies CallStats that we are the new dominant speaker in the\n * conference.\n */\n sendDominantSpeakerEvent() {\n CallStats._reportEvent(this, fabricEvent.dominantSpeaker);\n }\n\n /**\n * Notifies CallStats that the fabric for the underlying peerconnection was\n * closed and no evens should be reported, after this call.\n */\n sendTerminateEvent() {\n if (CallStats.backendInitialized) {\n CallStats.backend.sendFabricEvent(\n this.peerconnection,\n CallStats.backend.fabricEvent.fabricTerminated,\n this.confID);\n }\n CallStats.fabrics.delete(this);\n }\n\n /**\n * Notifies CallStats for ice connection failed\n */\n sendIceConnectionFailedEvent() {\n CallStats._reportError(\n this,\n wrtcFuncNames.iceConnectionFailure,\n null,\n this.peerconnection);\n }\n\n /**\n * Notifies CallStats that peer connection failed to create offer.\n *\n * @param {Error} e error to send\n */\n sendCreateOfferFailed(e) {\n CallStats._reportError(\n this, wrtcFuncNames.createOffer, e, this.peerconnection);\n }\n\n /**\n * Notifies CallStats that peer connection failed to create answer.\n *\n * @param {Error} e error to send\n */\n sendCreateAnswerFailed(e) {\n CallStats._reportError(\n this, wrtcFuncNames.createAnswer, e, this.peerconnection);\n }\n\n /**\n * Sends either resume or hold event for the fabric associated with\n * the underlying peerconnection.\n * @param {boolean} isResume true to resume or false to hold\n */\n sendResumeOrHoldEvent(isResume) {\n CallStats._reportEvent(\n this,\n isResume ? fabricEvent.fabricResume : fabricEvent.fabricHold);\n }\n\n /**\n * Notifies CallStats for screen sharing events\n * @param {boolean} start true for starting screen sharing and\n * false for not stopping\n * @param {string|null} ssrc - optional ssrc value, used only when\n * starting screen sharing.\n */\n sendScreenSharingEvent(start, ssrc) {\n let eventData;\n\n if (ssrc) {\n eventData = { ssrc };\n }\n\n CallStats._reportEvent(\n this,\n start ? fabricEvent.screenShareStart : fabricEvent.screenShareStop,\n eventData);\n }\n\n /**\n * Notifies CallStats that peer connection failed to set local description.\n *\n * @param {Error} e error to send\n */\n sendSetLocalDescFailed(e) {\n CallStats._reportError(\n this, wrtcFuncNames.setLocalDescription, e, this.peerconnection);\n }\n\n /**\n * Notifies CallStats that peer connection failed to set remote description.\n *\n * @param {Error} e error to send\n */\n sendSetRemoteDescFailed(e) {\n CallStats._reportError(\n this, wrtcFuncNames.setRemoteDescription, e, this.peerconnection);\n }\n\n /**\n * Notifies CallStats that peer connection failed to add ICE candidate.\n *\n * @param {Error} e error to send\n */\n sendAddIceCandidateFailed(e) {\n CallStats._reportError(\n this, wrtcFuncNames.addIceCandidate, e, this.peerconnection);\n }\n}\n\n/**\n * The CallStats API backend instance\n * @type {callstats}\n */\nCallStats.backend = null;\n\n// some errors/events may happen before CallStats init\n// in this case we accumulate them in this array\n// and send them to callstats on init\nCallStats.reportsQueue = [];\n\n/**\n * Whether the library was successfully initialized(the backend) using its\n * initialize method.\n * @type {boolean}\n */\nCallStats.backendInitialized = false;\n\n/**\n * Part of the CallStats credentials - application ID\n * @type {string}\n */\nCallStats.callStatsID = null;\n\n/**\n * Part of the CallStats credentials - application secret\n * @type {string}\n */\nCallStats.callStatsSecret = null;\n\n/**\n * Local CallStats user ID structure. Can be set only once when\n * {@link backend} is initialized, so it's static for the time being.\n * See CallStats API for more info:\n * https://www.callstats.io/api/#userid\n * @type {object}\n */\nCallStats.userID = null;\n","/**\n * Provides statistics for the local stream.\n */\n\n/**\n * Size of the webaudio analyzer buffer.\n * @type {number}\n */\nconst WEBAUDIO_ANALYZER_FFT_SIZE = 2048;\n\n/**\n * Value of the webaudio analyzer smoothing time parameter.\n * @type {number}\n */\nconst WEBAUDIO_ANALYZER_SMOOTING_TIME = 0.8;\n\nwindow.AudioContext = window.AudioContext || window.webkitAudioContext;\n\nlet context = null;\n\nif (window.AudioContext) {\n context = new AudioContext();\n\n // XXX Not all browsers define a suspend method on AudioContext. As the\n // invocation is at the (ES6 module) global execution level, it breaks the\n // loading of the lib-jitsi-meet library in such browsers and, consequently,\n // the loading of the very Web app that uses the lib-jitsi-meet library. For\n // example, Google Chrome 40 on Android does not define the method but we\n // still want to be able to load the lib-jitsi-meet library there and\n // display a page which notifies the user that the Web app is not supported\n // there.\n context.suspend && context.suspend();\n}\n\n/**\n * Converts time domain data array to audio level.\n * @param samples the time domain data array.\n * @returns {number} the audio level\n */\nfunction timeDomainDataToAudioLevel(samples) {\n\n let maxVolume = 0;\n\n const length = samples.length;\n\n for (let i = 0; i < length; i++) {\n if (maxVolume < samples[i]) {\n maxVolume = samples[i];\n }\n }\n\n return parseFloat(((maxVolume - 127) / 128).toFixed(3));\n}\n\n/**\n * Animates audio level change\n * @param newLevel the new audio level\n * @param lastLevel the last audio level\n * @returns {Number} the audio level to be set\n */\nfunction animateLevel(newLevel, lastLevel) {\n let value = 0;\n const diff = lastLevel - newLevel;\n\n if (diff > 0.2) {\n value = lastLevel - 0.2;\n } else if (diff < -0.4) {\n value = lastLevel + 0.4;\n } else {\n value = newLevel;\n }\n\n return parseFloat(value.toFixed(3));\n}\n\n\n/**\n * LocalStatsCollector calculates statistics for the local stream.\n *\n * @param stream the local stream\n * @param interval stats refresh interval given in ms.\n * @param callback function that receives the audio levels.\n * @constructor\n */\nexport default function LocalStatsCollector(stream, interval, callback) {\n this.stream = stream;\n this.intervalId = null;\n this.intervalMilis = interval;\n this.audioLevel = 0;\n this.callback = callback;\n}\n\n/**\n * Starts the collecting the statistics.\n */\nLocalStatsCollector.prototype.start = function() {\n if (!LocalStatsCollector.isLocalStatsSupported()) {\n return;\n }\n context.resume();\n const analyser = context.createAnalyser();\n\n analyser.smoothingTimeConstant = WEBAUDIO_ANALYZER_SMOOTING_TIME;\n analyser.fftSize = WEBAUDIO_ANALYZER_FFT_SIZE;\n\n const source = context.createMediaStreamSource(this.stream);\n\n source.connect(analyser);\n\n this.intervalId = setInterval(\n () => {\n const array = new Uint8Array(analyser.frequencyBinCount);\n\n analyser.getByteTimeDomainData(array);\n const audioLevel = timeDomainDataToAudioLevel(array);\n\n // Set the audio levels always as NoAudioSignalDetection now\n // uses audio levels from LocalStatsCollector and waits for\n // atleast 4 secs for a no audio signal before displaying the\n // notification on the UI.\n this.audioLevel = animateLevel(audioLevel, this.audioLevel);\n this.callback(this.audioLevel);\n },\n this.intervalMilis\n );\n};\n\n/**\n * Stops collecting the statistics.\n */\nLocalStatsCollector.prototype.stop = function() {\n if (this.intervalId) {\n clearInterval(this.intervalId);\n this.intervalId = null;\n }\n};\n\n/**\n * Checks if the environment has the necessary conditions to support\n * collecting stats from local streams.\n *\n * @returns {boolean}\n */\nLocalStatsCollector.isLocalStatsSupported = function() {\n return Boolean(context);\n};\n","\n/**\n * The method will increase the given number by 1. If the given counter is equal\n * or greater to {@link Number.MAX_SAFE_INTEGER} then it will be rolled back to\n * 1.\n * @param {number} number - An integer counter value to be incremented.\n * @return {number} the next counter value increased by 1 (see the description\n * above for exception).\n */\nexport function safeCounterIncrement(number) {\n let nextValue = number;\n\n if (number >= Number.MAX_SAFE_INTEGER) {\n nextValue = 0;\n }\n\n return nextValue + 1;\n}\n\n/**\n * Calculates the average value of am Array of numbers.\n *\n * @param {Float32Array} valueArray - Array of numbers.\n * @returns {number} - Number array average.\n */\nexport function calculateAverage(valueArray) {\n return valueArray.length > 0 ? valueArray.reduce((a, b) => a + b) / valueArray.length : 0;\n}\n\n/**\n * Calculates a unique hash for a given string similar to Java's\n * implementation of String.hashCode()\n *\n * @param {String} string - String whose hash has to be calculated.\n * @returns {number} - Unique hash code calculated.\n */\nexport function hashString(string) {\n let hash = 0;\n\n for (let i = 0; i < string.length; i++) {\n hash += Math.pow(string.charCodeAt(i) * 31, string.length - i);\n\n /* eslint-disable no-bitwise */\n hash = hash & hash; // Convert to 32bit integer\n }\n\n return Math.abs(hash);\n}\n\n/**\n * Returns only the positive values from an array of numbers.\n *\n * @param {Float32Array} valueArray - Array of vad scores.\n * @returns {Array} - Array of positive numbers.\n */\nexport function filterPositiveValues(valueArray) {\n return valueArray.filter(value => value >= 0);\n}\n\n/**\n * This class calculates a simple running average that continually changes\n * as more data points are collected and added.\n */\nexport class RunningAverage {\n /**\n * Creates an instance of the running average calculator.\n */\n constructor() {\n this.average = 0;\n this.n = 0;\n }\n\n /**\n * Adds a new data point to the existing set of values and recomputes\n * the running average.\n * @param {number} value\n * @returns {void}\n */\n addNext(value) {\n if (typeof value !== 'number') {\n return;\n }\n this.n += 1;\n this.average = this.average + ((value - this.average) / this.n);\n }\n\n /**\n * Obtains the average value for the current subset of values.\n * @returns {number} - computed average.\n */\n getAverage() {\n return this.average;\n }\n}\n","\nimport { getLogger } from '@jitsi/logger';\n\nimport * as StatisticsEvents from '../../service/statistics/Events';\nimport { RunningAverage } from '../util/MathUtil';\n\nconst logger = getLogger(__filename);\nconst MILLI_SECONDS = 1000;\nconst SECONDS = 60;\n\n/**\n * This class creates an observer that monitors browser's performance measurement events\n * as they are recorded in the browser's performance timeline and computes an average and\n * a maximum value for the long task events. Tasks are classified as long tasks if they take\n * longer than 50ms to execute on the main thread.\n */\nexport class PerformanceObserverStats {\n /**\n * Creates a new instance of Performance observer statistics.\n *\n * @param {*} emitter Event emitter for emitting stats periodically\n * @param {*} statsInterval interval for calculating the stats\n */\n constructor(emitter, statsInterval) {\n this.eventEmitter = emitter;\n this.longTasks = 0;\n this.maxDuration = 0;\n this.performanceStatsInterval = statsInterval;\n this.stats = new RunningAverage();\n }\n\n /**\n * Obtains the average rate of long tasks observed per min and the\n * duration of the longest task recorded by the observer.\n * @returns {Object}\n */\n getLongTasksStats() {\n return {\n avgRatePerMinute: (this.stats.getAverage() * SECONDS).toFixed(2), // calc rate per min\n maxDurationMs: this.maxDuration\n };\n }\n\n /**\n * Starts the performance observer by registering the callback function\n * that calculates the performance statistics periodically.\n * @returns {void}\n */\n startObserver() {\n // Create a handler for when the long task event is fired.\n this.longTaskEventHandler = list => {\n const entries = list.getEntries();\n\n for (const task of entries) {\n this.longTasks++;\n this.maxDuration = Math.max(this.maxDuration, task.duration).toFixed(3);\n }\n };\n\n // Create an observer for monitoring long tasks.\n logger.info('Creating a Performance Observer for monitoring Long Tasks');\n this.observer = new PerformanceObserver(this.longTaskEventHandler);\n this.observer.observe({ type: 'longtask',\n buffered: true });\n const startTime = Date.now();\n\n // Calculate the average # of events/sec and emit a stats event.\n this.longTasksIntervalId = setInterval(() => {\n const now = Date.now();\n const interval = this._lastTimeStamp\n ? (now - this._lastTimeStamp) / MILLI_SECONDS\n : (now - startTime) / MILLI_SECONDS;\n const rate = this.longTasks / interval;\n\n this.stats.addNext(rate);\n this.eventEmitter.emit(\n StatisticsEvents.LONG_TASKS_STATS, this.getLongTasksStats());\n\n // Reset the counter and start counting events again.\n this.longTasks = 0;\n this._lastTimeStamp = Date.now();\n }, this.performanceStatsInterval);\n }\n\n /**\n * Stops the performance observer.\n * @returns {void}\n */\n stopObserver() {\n this.observer && this.observer.disconnect();\n this.longTaskEventHandler = null;\n if (this.longTasksIntervalId) {\n clearInterval(this.longTasksIntervalId);\n this.longTasksIntervalId = null;\n }\n }\n}\n","/**\n * The audio type.\n */\nexport const AUDIO = 'audio';\n\n/**\n * The presenter type.\n */\nexport const PRESENTER = 'presenter';\n\n/**\n * The video type.\n */\nexport const VIDEO = 'video';\n","import { getLogger } from '@jitsi/logger';\n\nimport * as MediaType from '../../service/RTC/MediaType';\nimport * as StatisticsEvents from '../../service/statistics/Events';\nimport browser from '../browser';\n\nconst GlobalOnErrorHandler = require('../util/GlobalOnErrorHandler');\n\nconst logger = getLogger(__filename);\n\n/**\n * Calculates packet lost percent using the number of lost packets and the\n * number of all packet.\n * @param lostPackets the number of lost packets\n * @param totalPackets the number of all packets.\n * @returns {number} packet loss percent\n */\nfunction calculatePacketLoss(lostPackets, totalPackets) {\n if (!totalPackets || totalPackets <= 0\n || !lostPackets || lostPackets <= 0) {\n return 0;\n }\n\n return Math.round((lostPackets / totalPackets) * 100);\n}\n\n/**\n * Holds \"statistics\" for a single SSRC.\n * @constructor\n */\nfunction SsrcStats() {\n this.loss = {};\n this.bitrate = {\n download: 0,\n upload: 0\n };\n this.resolution = {};\n this.framerate = 0;\n this.codec = '';\n}\n\n/**\n * Sets the \"loss\" object.\n * @param loss the value to set.\n */\nSsrcStats.prototype.setLoss = function(loss) {\n this.loss = loss || {};\n};\n\n/**\n * Sets resolution that belong to the ssrc represented by this instance.\n * @param resolution new resolution value to be set.\n */\nSsrcStats.prototype.setResolution = function(resolution) {\n this.resolution = resolution || {};\n};\n\n/**\n * Adds the \"download\" and \"upload\" fields from the \"bitrate\" parameter to\n * the respective fields of the \"bitrate\" field of this object.\n * @param bitrate an object holding the values to add.\n */\nSsrcStats.prototype.addBitrate = function(bitrate) {\n this.bitrate.download += bitrate.download;\n this.bitrate.upload += bitrate.upload;\n};\n\n/**\n * Resets the bit rate for given ssrc that belong to the peer\n * represented by this instance.\n */\nSsrcStats.prototype.resetBitrate = function() {\n this.bitrate.download = 0;\n this.bitrate.upload = 0;\n};\n\n/**\n * Sets the \"framerate\".\n * @param framerate the value to set.\n */\nSsrcStats.prototype.setFramerate = function(framerate) {\n this.framerate = framerate || 0;\n};\n\nSsrcStats.prototype.setCodec = function(codec) {\n this.codec = codec || '';\n};\n\n/**\n *\n */\nfunction ConferenceStats() {\n\n /**\n * The bandwidth\n * @type {{}}\n */\n this.bandwidth = {};\n\n /**\n * The bit rate\n * @type {{}}\n */\n this.bitrate = {};\n\n /**\n * The packet loss rate\n * @type {{}}\n */\n this.packetLoss = null;\n\n /**\n * Array with the transport information.\n * @type {Array}\n */\n this.transport = [];\n}\n\n/* eslint-disable max-params */\n\n/**\n * StatsCollector registers for stats updates of given\n * peerconnection in given interval. On each update particular\n * stats are extracted and put in {@link SsrcStats} objects. Once the processing\n * is done audioLevelsUpdateCallback is called with this\n * instance as an event source.\n *\n * @param peerconnection WebRTC PeerConnection object.\n * @param audioLevelsInterval\n * @param statsInterval stats refresh interval given in ms.\n * @param eventEmitter\n * @constructor\n */\nexport default function StatsCollector(peerconnection, audioLevelsInterval, statsInterval, eventEmitter) {\n this.peerconnection = peerconnection;\n this.baselineAudioLevelsReport = null;\n this.currentAudioLevelsReport = null;\n this.currentStatsReport = null;\n this.previousStatsReport = null;\n this.audioLevelReportHistory = {};\n this.audioLevelsIntervalId = null;\n this.eventEmitter = eventEmitter;\n this.conferenceStats = new ConferenceStats();\n\n // Updates stats interval\n this.audioLevelsIntervalMilis = audioLevelsInterval;\n\n this.speakerList = [];\n this.statsIntervalId = null;\n this.statsIntervalMilis = statsInterval;\n\n /**\n * Maps SSRC numbers to {@link SsrcStats}.\n * @type {Map} speakerList - Endpoint ids.\n * @returns {void}\n */\nStatsCollector.prototype.setSpeakerList = function(speakerList) {\n this.speakerList = speakerList;\n};\n\n/**\n * Stops stats updates.\n */\nStatsCollector.prototype.stop = function() {\n if (this.audioLevelsIntervalId) {\n clearInterval(this.audioLevelsIntervalId);\n this.audioLevelsIntervalId = null;\n }\n\n if (this.statsIntervalId) {\n clearInterval(this.statsIntervalId);\n this.statsIntervalId = null;\n }\n};\n\n/**\n * Callback passed to getStats method.\n * @param error an error that occurred on getStats call.\n */\nStatsCollector.prototype.errorCallback = function(error) {\n GlobalOnErrorHandler.callErrorHandler(error);\n logger.error('Get stats error', error);\n this.stop();\n};\n\n/**\n * Starts stats updates.\n */\nStatsCollector.prototype.start = function(startAudioLevelStats) {\n if (startAudioLevelStats) {\n if (browser.supportsReceiverStats()) {\n logger.info('Using RTCRtpSynchronizationSource for remote audio levels');\n }\n this.audioLevelsIntervalId = setInterval(\n () => {\n if (browser.supportsReceiverStats()) {\n const audioLevels = this.peerconnection.getAudioLevels(this.speakerList);\n\n for (const ssrc in audioLevels) {\n if (audioLevels.hasOwnProperty(ssrc)) {\n // Use a scaling factor of 2.5 to report the same\n // audio levels that getStats reports.\n const audioLevel = audioLevels[ssrc] * 2.5;\n\n this.eventEmitter.emit(\n StatisticsEvents.AUDIO_LEVEL,\n this.peerconnection,\n Number.parseInt(ssrc, 10),\n audioLevel,\n false /* isLocal */);\n }\n }\n } else {\n // Interval updates\n this.peerconnection.getStats()\n .then(report => {\n this.currentAudioLevelsReport = typeof report?.result === 'function'\n ? report.result()\n : report;\n this.processAudioLevelReport();\n this.baselineAudioLevelsReport = this.currentAudioLevelsReport;\n })\n .catch(error => this.errorCallback(error));\n }\n },\n this.audioLevelsIntervalMilis\n );\n }\n\n const processStats = () => {\n // Interval updates\n this.peerconnection.getStats()\n .then(report => {\n this.currentStatsReport = typeof report?.result === 'function'\n ? report.result()\n : report;\n\n try {\n this.processStatsReport();\n } catch (error) {\n GlobalOnErrorHandler.callErrorHandler(error);\n logger.error('Processing of RTP stats failed:', error);\n }\n this.previousStatsReport = this.currentStatsReport;\n })\n .catch(error => this.errorCallback(error));\n };\n\n processStats();\n this.statsIntervalId = setInterval(processStats, this.statsIntervalMilis);\n};\n\n/**\n *\n */\nStatsCollector.prototype._processAndEmitReport = function() {\n // process stats\n const totalPackets = {\n download: 0,\n upload: 0\n };\n const lostPackets = {\n download: 0,\n upload: 0\n };\n let bitrateDownload = 0;\n let bitrateUpload = 0;\n const resolutions = {};\n const framerates = {};\n const codecs = {};\n let audioBitrateDownload = 0;\n let audioBitrateUpload = 0;\n let audioCodec;\n let videoBitrateDownload = 0;\n let videoBitrateUpload = 0;\n let videoCodec;\n\n for (const [ ssrc, ssrcStats ] of this.ssrc2stats) {\n // process packet loss stats\n const loss = ssrcStats.loss;\n const type = loss.isDownloadStream ? 'download' : 'upload';\n\n totalPackets[type] += loss.packetsTotal;\n lostPackets[type] += loss.packetsLost;\n\n // process bitrate stats\n bitrateDownload += ssrcStats.bitrate.download;\n bitrateUpload += ssrcStats.bitrate.upload;\n\n // collect resolutions and framerates\n const track = this.peerconnection.getTrackBySSRC(ssrc);\n\n if (track) {\n if (track.isAudioTrack()) {\n audioBitrateDownload += ssrcStats.bitrate.download;\n audioBitrateUpload += ssrcStats.bitrate.upload;\n audioCodec = ssrcStats.codec;\n } else {\n videoBitrateDownload += ssrcStats.bitrate.download;\n videoBitrateUpload += ssrcStats.bitrate.upload;\n videoCodec = ssrcStats.codec;\n }\n\n const participantId = track.getParticipantId();\n\n if (participantId) {\n const resolution = ssrcStats.resolution;\n\n if (resolution.width\n && resolution.height\n && resolution.width !== -1\n && resolution.height !== -1) {\n const userResolutions = resolutions[participantId] || {};\n\n userResolutions[ssrc] = resolution;\n resolutions[participantId] = userResolutions;\n }\n if (ssrcStats.framerate !== 0) {\n const userFramerates = framerates[participantId] || {};\n\n userFramerates[ssrc] = ssrcStats.framerate;\n framerates[participantId] = userFramerates;\n }\n if (audioCodec && videoCodec) {\n const codecDesc = {\n 'audio': audioCodec,\n 'video': videoCodec\n };\n\n const userCodecs = codecs[participantId] || {};\n\n userCodecs[ssrc] = codecDesc;\n codecs[participantId] = userCodecs;\n }\n } else {\n logger.error(`No participant ID returned by ${track}`);\n }\n }\n\n ssrcStats.resetBitrate();\n }\n\n this.conferenceStats.bitrate = {\n 'upload': bitrateUpload,\n 'download': bitrateDownload\n };\n\n this.conferenceStats.bitrate.audio = {\n 'upload': audioBitrateUpload,\n 'download': audioBitrateDownload\n };\n\n this.conferenceStats.bitrate.video = {\n 'upload': videoBitrateUpload,\n 'download': videoBitrateDownload\n };\n\n this.conferenceStats.packetLoss = {\n total:\n calculatePacketLoss(\n lostPackets.download + lostPackets.upload,\n totalPackets.download + totalPackets.upload),\n download:\n calculatePacketLoss(lostPackets.download, totalPackets.download),\n upload:\n calculatePacketLoss(lostPackets.upload, totalPackets.upload)\n };\n\n const avgAudioLevels = {};\n let localAvgAudioLevels;\n\n Object.keys(this.audioLevelReportHistory).forEach(ssrc => {\n const { data, isLocal } = this.audioLevelReportHistory[ssrc];\n const avgAudioLevel = data.reduce((sum, currentValue) => sum + currentValue) / data.length;\n\n if (isLocal) {\n localAvgAudioLevels = avgAudioLevel;\n } else {\n const track = this.peerconnection.getTrackBySSRC(Number(ssrc));\n\n if (track) {\n const participantId = track.getParticipantId();\n\n if (participantId) {\n avgAudioLevels[participantId] = avgAudioLevel;\n }\n }\n }\n });\n this.audioLevelReportHistory = {};\n\n this.eventEmitter.emit(\n StatisticsEvents.CONNECTION_STATS,\n this.peerconnection,\n {\n 'bandwidth': this.conferenceStats.bandwidth,\n 'bitrate': this.conferenceStats.bitrate,\n 'packetLoss': this.conferenceStats.packetLoss,\n 'resolution': resolutions,\n 'framerate': framerates,\n 'codec': codecs,\n 'transport': this.conferenceStats.transport,\n localAvgAudioLevels,\n avgAudioLevels\n });\n this.conferenceStats.transport = [];\n};\n\n/**\n * Converts the value to a non-negative number.\n * If the value is either invalid or negative then 0 will be returned.\n * @param {*} v\n * @return {number}\n * @private\n */\nStatsCollector.prototype.getNonNegativeValue = function(v) {\n let value = v;\n\n if (typeof value !== 'number') {\n value = Number(value);\n }\n\n if (isNaN(value)) {\n return 0;\n }\n\n return Math.max(0, value);\n};\n\n/**\n * Calculates bitrate between before and now using a supplied field name and its\n * value in the stats.\n * @param {RTCInboundRtpStreamStats|RTCSentRtpStreamStats} now the current stats\n * @param {RTCInboundRtpStreamStats|RTCSentRtpStreamStats} before the\n * previous stats.\n * @param fieldName the field to use for calculations.\n * @return {number} the calculated bitrate between now and before.\n * @private\n */\nStatsCollector.prototype._calculateBitrate = function(now, before, fieldName) {\n const bytesNow = this.getNonNegativeValue(now[fieldName]);\n const bytesBefore = this.getNonNegativeValue(before[fieldName]);\n const bytesProcessed = Math.max(0, bytesNow - bytesBefore);\n\n const timeMs = now.timestamp - before.timestamp;\n let bitrateKbps = 0;\n\n if (timeMs > 0) {\n // TODO is there any reason to round here?\n bitrateKbps = Math.round((bytesProcessed * 8) / timeMs);\n }\n\n return bitrateKbps;\n};\n\n/**\n * Stats processing for spec-compliant RTCPeerConnection#getStats.\n */\nStatsCollector.prototype.processStatsReport = function() {\n if (!this.previousStatsReport) {\n return;\n }\n const byteSentStats = {};\n\n this.currentStatsReport.forEach(now => {\n // RTCIceCandidatePairStats - https://w3c.github.io/webrtc-stats/#candidatepair-dict*\n if (now.type === 'candidate-pair' && now.nominated && now.state === 'succeeded') {\n const availableIncomingBitrate = now.availableIncomingBitrate;\n const availableOutgoingBitrate = now.availableOutgoingBitrate;\n\n if (availableIncomingBitrate || availableOutgoingBitrate) {\n this.conferenceStats.bandwidth = {\n 'download': Math.round(availableIncomingBitrate / 1000),\n 'upload': Math.round(availableOutgoingBitrate / 1000)\n };\n }\n\n const remoteUsedCandidate = this.currentStatsReport.get(now.remoteCandidateId);\n const localUsedCandidate = this.currentStatsReport.get(now.localCandidateId);\n\n // RTCIceCandidateStats\n // https://w3c.github.io/webrtc-stats/#icecandidate-dict*\n if (remoteUsedCandidate && localUsedCandidate) {\n const remoteIpAddress = browser.isChromiumBased()\n ? remoteUsedCandidate.ip\n : remoteUsedCandidate.address;\n const remotePort = remoteUsedCandidate.port;\n const ip = `${remoteIpAddress}:${remotePort}`;\n\n const localIpAddress = browser.isChromiumBased()\n ? localUsedCandidate.ip\n : localUsedCandidate.address;\n const localPort = localUsedCandidate.port;\n const localip = `${localIpAddress}:${localPort}`;\n const type = remoteUsedCandidate.protocol;\n\n // Save the address unless it has been saved already.\n const conferenceStatsTransport = this.conferenceStats.transport;\n\n if (!conferenceStatsTransport.some(t =>\n t.ip === ip\n && t.type === type\n && t.localip === localip)) {\n conferenceStatsTransport.push({\n ip,\n type,\n localip,\n p2p: this.peerconnection.isP2P,\n localCandidateType: localUsedCandidate.candidateType,\n remoteCandidateType: remoteUsedCandidate.candidateType,\n networkType: localUsedCandidate.networkType,\n rtt: now.currentRoundTripTime * 1000\n });\n }\n }\n\n // RTCReceivedRtpStreamStats\n // https://w3c.github.io/webrtc-stats/#receivedrtpstats-dict*\n // RTCSentRtpStreamStats\n // https://w3c.github.io/webrtc-stats/#sentrtpstats-dict*\n } else if (now.type === 'inbound-rtp' || now.type === 'outbound-rtp') {\n const before = this.previousStatsReport.get(now.id);\n const ssrc = this.getNonNegativeValue(now.ssrc);\n\n if (!before || !ssrc) {\n return;\n }\n\n let ssrcStats = this.ssrc2stats.get(ssrc);\n\n if (!ssrcStats) {\n ssrcStats = new SsrcStats();\n this.ssrc2stats.set(ssrc, ssrcStats);\n }\n\n let isDownloadStream = true;\n let key = 'packetsReceived';\n\n if (now.type === 'outbound-rtp') {\n isDownloadStream = false;\n key = 'packetsSent';\n }\n\n let packetsNow = now[key];\n\n if (!packetsNow || packetsNow < 0) {\n packetsNow = 0;\n }\n\n const packetsBefore = this.getNonNegativeValue(before[key]);\n const packetsDiff = Math.max(0, packetsNow - packetsBefore);\n\n const packetsLostNow = this.getNonNegativeValue(now.packetsLost);\n const packetsLostBefore = this.getNonNegativeValue(before.packetsLost);\n const packetsLostDiff = Math.max(0, packetsLostNow - packetsLostBefore);\n\n ssrcStats.setLoss({\n packetsTotal: packetsDiff + packetsLostDiff,\n packetsLost: packetsLostDiff,\n isDownloadStream\n });\n\n // Get the resolution and framerate for only remote video sources here. For the local video sources,\n // 'track' stats will be used since they have the updated resolution based on the simulcast streams\n // currently being sent. Promise based getStats reports three 'outbound-rtp' streams and there will be\n // more calculations needed to determine what is the highest resolution stream sent by the client if the\n // 'outbound-rtp' stats are used.\n if (now.type === 'inbound-rtp') {\n const resolution = {\n height: now.frameHeight,\n width: now.frameWidth\n };\n const frameRate = now.framesPerSecond;\n\n if (resolution.height && resolution.width) {\n ssrcStats.setResolution(resolution);\n }\n ssrcStats.setFramerate(Math.round(frameRate || 0));\n\n ssrcStats.addBitrate({\n 'download': this._calculateBitrate(now, before, 'bytesReceived'),\n 'upload': 0\n });\n } else {\n byteSentStats[ssrc] = this.getNonNegativeValue(now.bytesSent);\n ssrcStats.addBitrate({\n 'download': 0,\n 'upload': this._calculateBitrate(now, before, 'bytesSent')\n });\n }\n\n const codec = this.currentStatsReport.get(now.codecId);\n\n if (codec) {\n /**\n * The mime type has the following form: video/VP8 or audio/ISAC,\n * so we what to keep just the type after the '/', audio and video\n * keys will be added on the processing side.\n */\n const codecShortType = codec.mimeType.split('/')[1];\n\n codecShortType && ssrcStats.setCodec(codecShortType);\n }\n\n // Use track stats for resolution and framerate of the local video source.\n // RTCVideoHandlerStats - https://w3c.github.io/webrtc-stats/#vststats-dict*\n // RTCMediaHandlerStats - https://w3c.github.io/webrtc-stats/#mststats-dict*\n } else if (now.type === 'track' && now.kind === MediaType.VIDEO && !now.remoteSource) {\n const resolution = {\n height: now.frameHeight,\n width: now.frameWidth\n };\n const localVideoTracks = this.peerconnection.getLocalTracks(MediaType.VIDEO);\n\n if (!localVideoTracks?.length) {\n return;\n }\n\n const ssrc = this.peerconnection.getLocalSSRC(localVideoTracks[0]);\n\n if (!ssrc) {\n return;\n }\n let ssrcStats = this.ssrc2stats.get(ssrc);\n\n if (!ssrcStats) {\n ssrcStats = new SsrcStats();\n this.ssrc2stats.set(ssrc, ssrcStats);\n }\n if (resolution.height && resolution.width) {\n ssrcStats.setResolution(resolution);\n }\n\n // Calculate the frame rate. 'framesSent' is the total aggregate value for all the simulcast streams.\n // Therefore, it needs to be divided by the total number of active simulcast streams.\n let frameRate = now.framesPerSecond;\n\n if (!frameRate) {\n const before = this.previousStatsReport.get(now.id);\n\n if (before) {\n const timeMs = now.timestamp - before.timestamp;\n\n if (timeMs > 0 && now.framesSent) {\n const numberOfFramesSinceBefore = now.framesSent - before.framesSent;\n\n frameRate = (numberOfFramesSinceBefore / timeMs) * 1000;\n }\n }\n\n if (!frameRate) {\n return;\n }\n }\n\n // Get the number of simulcast streams currently enabled from TPC.\n const numberOfActiveStreams = this.peerconnection.getActiveSimulcastStreams();\n\n // Reset frame rate to 0 when video is suspended as a result of endpoint falling out of last-n.\n frameRate = numberOfActiveStreams ? Math.round(frameRate / numberOfActiveStreams) : 0;\n ssrcStats.setFramerate(frameRate);\n }\n });\n\n this.eventEmitter.emit(StatisticsEvents.BYTE_SENT_STATS, this.peerconnection, byteSentStats);\n this._processAndEmitReport();\n};\n\n/**\n * Stats processing logic.\n */\nStatsCollector.prototype.processAudioLevelReport = function() {\n if (!this.baselineAudioLevelsReport) {\n return;\n }\n\n this.currentAudioLevelsReport.forEach(now => {\n if (now.type !== 'track') {\n return;\n }\n\n // Audio level\n const audioLevel = now.audioLevel;\n\n if (!audioLevel) {\n return;\n }\n\n const trackIdentifier = now.trackIdentifier;\n const ssrc = this.peerconnection.getSsrcByTrackId(trackIdentifier);\n\n if (ssrc) {\n const isLocal\n = ssrc === this.peerconnection.getLocalSSRC(\n this.peerconnection.getLocalTracks(MediaType.AUDIO));\n\n this.eventEmitter.emit(\n StatisticsEvents.AUDIO_LEVEL,\n this.peerconnection,\n ssrc,\n audioLevel,\n isLocal);\n }\n });\n};\n\n","import EventEmitter from 'events';\n\nimport * as JitsiConferenceEvents from '../../JitsiConferenceEvents';\nimport JitsiTrackError from '../../JitsiTrackError';\nimport { FEEDBACK } from '../../service/statistics/AnalyticsEvents';\nimport * as StatisticsEvents from '../../service/statistics/Events';\nimport browser from '../browser';\nimport ScriptUtil from '../util/ScriptUtil';\n\nimport analytics from './AnalyticsAdapter';\nimport CallStats from './CallStats';\nimport LocalStats from './LocalStatsCollector';\nimport { PerformanceObserverStats } from './PerformanceObserverStats';\nimport RTPStats from './RTPStatsCollector';\nimport { CALLSTATS_SCRIPT_URL } from './constants';\n\nconst logger = require('@jitsi/logger').getLogger(__filename);\n\n/**\n * Stores all active {@link Statistics} instances.\n * @type {Set}\n */\nlet _instances;\n\n/**\n * True if callstats API is loaded\n */\nlet isCallstatsLoaded = false;\n\n/**\n * Since callstats.io is a third party, we cannot guarantee the quality of their\n * service. More specifically, their server may take noticeably long time to\n * respond. Consequently, it is in our best interest (in the sense that the\n * intergration of callstats.io is pretty important to us but not enough to\n * allow it to prevent people from joining a conference) to (1) start\n * downloading their API as soon as possible and (2) do the downloading\n * asynchronously.\n *\n * @param {StatisticsOptions} options - Options to use for downloading and\n * initializing callstats backend.\n */\nfunction loadCallStatsAPI(options) {\n if (!isCallstatsLoaded) {\n ScriptUtil.loadScript(\n options.customScriptUrl || CALLSTATS_SCRIPT_URL,\n /* async */ true,\n /* prepend */ true,\n /* relativeURL */ undefined,\n /* loadCallback */ () => _initCallStatsBackend(options)\n );\n isCallstatsLoaded = true;\n }\n}\n\n/**\n * Initializes Callstats backend.\n *\n * @param {StatisticsOptions} options - The options to use for initializing\n * callstats backend.\n * @private\n */\nfunction _initCallStatsBackend(options) {\n if (CallStats.isBackendInitialized()) {\n return;\n }\n\n if (!CallStats.initBackend({\n callStatsID: options.callStatsID,\n callStatsSecret: options.callStatsSecret,\n userName: options.userName,\n aliasName: options.aliasName,\n applicationName: options.applicationName,\n getWiFiStatsMethod: options.getWiFiStatsMethod,\n confID: options.confID,\n siteID: options.siteID,\n configParams: options.configParams\n })) {\n logger.error('CallStats Backend initialization failed bad');\n }\n}\n\n/**\n * callstats strips any additional fields from Error except for \"name\", \"stack\",\n * \"message\" and \"constraintName\". So we need to bundle additional information\n * from JitsiTrackError into error passed to callstats to preserve valuable\n * information about error.\n * @param {JitsiTrackError} error\n */\nfunction formatJitsiTrackErrorForCallStats(error) {\n const err = new Error();\n\n // Just copy original stack from error\n err.stack = error.stack;\n\n // Combine name from error's name plus (possibly) name of original GUM error\n err.name = (error.name || 'Unknown error') + (error.gum && error.gum.error\n && error.gum.error.name ? ` - ${error.gum.error.name}` : '');\n\n // Put all constraints into this field. For constraint failed errors we will\n // still know which exactly constraint failed as it will be a part of\n // message.\n err.constraintName = error.gum && error.gum.constraints\n ? JSON.stringify(error.gum.constraints) : '';\n\n // Just copy error's message.\n err.message = error.message;\n\n return err;\n}\n\n/**\n * Init statistic options\n * @param options\n */\nStatistics.init = function(options) {\n Statistics.audioLevelsEnabled = !options.disableAudioLevels;\n if (typeof options.pcStatsInterval === 'number') {\n Statistics.pcStatsInterval = options.pcStatsInterval;\n }\n\n if (typeof options.audioLevelsInterval === 'number') {\n Statistics.audioLevelsInterval = options.audioLevelsInterval;\n }\n\n if (typeof options.longTasksStatsInterval === 'number') {\n Statistics.longTasksStatsInterval = options.longTasksStatsInterval;\n }\n\n Statistics.disableThirdPartyRequests = options.disableThirdPartyRequests;\n};\n\n/**\n * The options to configure Statistics.\n * @typedef {Object} StatisticsOptions\n * @property {string} applicationName - The application name to pass to\n * callstats.\n * @property {string} aliasName - The alias name to use when initializing callstats.\n * @property {string} userName - The user name to use when initializing callstats.\n * @property {string} confID - The callstats conference ID to use.\n * @property {string} callStatsID - Callstats credentials - the id.\n * @property {string} callStatsSecret - Callstats credentials - the secret.\n * @property {string} customScriptUrl - A custom lib url to use when downloading\n * callstats library.\n * @property {string} roomName - The room name we are currently in.\n * @property {string} configParams - The set of parameters\n * to enable/disable certain features in the library. See CallStats docs for more info.\n */\n/**\n *\n * @param xmpp\n * @param {StatisticsOptions} options - The options to use creating the\n * Statistics.\n */\nexport default function Statistics(xmpp, options) {\n /**\n * {@link RTPStats} mapped by {@link TraceablePeerConnection.id} which\n * collect RTP statistics for each peerconnection.\n * @type {Map}\n */\n this.callsStatsInstances = new Map();\n\n Statistics.instances.add(this);\n}\nStatistics.audioLevelsEnabled = false;\nStatistics.audioLevelsInterval = 200;\nStatistics.pcStatsInterval = 10000;\nStatistics.disableThirdPartyRequests = false;\nStatistics.analytics = analytics;\n\nObject.defineProperty(Statistics, 'instances', {\n /**\n * Returns the Set holding all active {@link Statistics} instances. Lazily\n * initializes the Set to allow any Set polyfills to be applied.\n * @type {Set}\n */\n get() {\n if (!_instances) {\n _instances = new Set();\n }\n\n return _instances;\n }\n});\n\n/**\n * Starts collecting RTP stats for given peerconnection.\n * @param {TraceablePeerConnection} peerconnection\n */\nStatistics.prototype.startRemoteStats = function(peerconnection) {\n this.stopRemoteStats(peerconnection);\n\n try {\n const rtpStats\n = new RTPStats(\n peerconnection,\n Statistics.audioLevelsInterval,\n Statistics.pcStatsInterval,\n this.eventEmitter);\n\n rtpStats.start(Statistics.audioLevelsEnabled);\n this.rtpStatsMap.set(peerconnection.id, rtpStats);\n } catch (e) {\n logger.error(`Failed to start collecting remote statistics: ${e}`);\n }\n};\n\nStatistics.localStats = [];\n\nStatistics.startLocalStats = function(stream, callback) {\n if (!Statistics.audioLevelsEnabled) {\n return;\n }\n const localStats = new LocalStats(stream, Statistics.audioLevelsInterval,\n callback);\n\n this.localStats.push(localStats);\n localStats.start();\n};\n\nStatistics.prototype.addAudioLevelListener = function(listener) {\n if (!Statistics.audioLevelsEnabled) {\n return;\n }\n this.eventEmitter.on(StatisticsEvents.AUDIO_LEVEL, listener);\n};\n\nStatistics.prototype.removeAudioLevelListener = function(listener) {\n if (!Statistics.audioLevelsEnabled) {\n return;\n }\n this.eventEmitter.removeListener(StatisticsEvents.AUDIO_LEVEL, listener);\n};\n\nStatistics.prototype.addBeforeDisposedListener = function(listener) {\n this.eventEmitter.on(StatisticsEvents.BEFORE_DISPOSED, listener);\n};\n\nStatistics.prototype.removeBeforeDisposedListener = function(listener) {\n this.eventEmitter.removeListener(\n StatisticsEvents.BEFORE_DISPOSED, listener);\n};\n\nStatistics.prototype.addConnectionStatsListener = function(listener) {\n this.eventEmitter.on(StatisticsEvents.CONNECTION_STATS, listener);\n};\n\nStatistics.prototype.removeConnectionStatsListener = function(listener) {\n this.eventEmitter.removeListener(\n StatisticsEvents.CONNECTION_STATS,\n listener);\n};\n\nStatistics.prototype.addByteSentStatsListener = function(listener) {\n this.eventEmitter.on(StatisticsEvents.BYTE_SENT_STATS, listener);\n};\n\nStatistics.prototype.removeByteSentStatsListener = function(listener) {\n this.eventEmitter.removeListener(StatisticsEvents.BYTE_SENT_STATS,\n listener);\n};\n\n/**\n * Add a listener that would be notified on a LONG_TASKS_STATS event.\n *\n * @param {Function} listener a function that would be called when notified.\n * @returns {void}\n */\nStatistics.prototype.addLongTasksStatsListener = function(listener) {\n this.eventEmitter.on(StatisticsEvents.LONG_TASKS_STATS, listener);\n};\n\n/**\n * Creates an instance of {@link PerformanceObserverStats} and starts the\n * observer that records the stats periodically.\n *\n * @returns {void}\n */\nStatistics.prototype.attachLongTasksStats = function(conference) {\n if (!browser.supportsPerformanceObserver()) {\n logger.warn('Performance observer for long tasks not supported by browser!');\n\n return;\n }\n\n this.performanceObserverStats = new PerformanceObserverStats(\n this.eventEmitter,\n Statistics.longTasksStatsInterval);\n\n conference.on(\n JitsiConferenceEvents.CONFERENCE_JOINED,\n () => this.performanceObserverStats.startObserver());\n conference.on(\n JitsiConferenceEvents.CONFERENCE_LEFT,\n () => this.performanceObserverStats.stopObserver());\n};\n\n/**\n * Obtains the current value of the LongTasks event statistics.\n *\n * @returns {Object|null} stats object if the observer has been\n * created, null otherwise.\n */\nStatistics.prototype.getLongTasksStats = function() {\n return this.performanceObserverStats\n ? this.performanceObserverStats.getLongTasksStats()\n : null;\n};\n\n/**\n * Removes the given listener for the LONG_TASKS_STATS event.\n *\n * @param {Function} listener the listener we want to remove.\n * @returns {void}\n */\nStatistics.prototype.removeLongTasksStatsListener = function(listener) {\n this.eventEmitter.removeListener(StatisticsEvents.LONG_TASKS_STATS, listener);\n};\n\n/**\n * Updates the list of speakers for which the audio levels are to be calculated. This is needed for the jvb pc only.\n *\n * @param {Array} speakerList The list of remote endpoint ids.\n * @returns {void}\n */\nStatistics.prototype.setSpeakerList = function(speakerList) {\n for (const rtpStats of Array.from(this.rtpStatsMap.values())) {\n if (!rtpStats.peerconnection.isP2P) {\n rtpStats.setSpeakerList(speakerList);\n }\n }\n};\n\nStatistics.prototype.dispose = function() {\n try {\n // NOTE Before reading this please see the comment in stopCallStats...\n //\n // Here we prevent from emitting the event twice in case it will be\n // triggered from stopCallStats.\n // If the event is triggered from here it means that the logs will not\n // be submitted anyway (because there is no CallStats instance), but\n // we're doing that for the sake of some kind of consistency.\n if (!this.callsStatsInstances.size) {\n this.eventEmitter.emit(StatisticsEvents.BEFORE_DISPOSED);\n }\n for (const callStats of this.callsStatsInstances.values()) {\n this.stopCallStats(callStats.tpc);\n }\n for (const tpcId of this.rtpStatsMap.keys()) {\n this._stopRemoteStats(tpcId);\n }\n if (this.eventEmitter) {\n this.eventEmitter.removeAllListeners();\n }\n } finally {\n Statistics.instances.delete(this);\n }\n};\n\nStatistics.stopLocalStats = function(stream) {\n if (!Statistics.audioLevelsEnabled) {\n return;\n }\n\n for (let i = 0; i < Statistics.localStats.length; i++) {\n if (Statistics.localStats[i].stream === stream) {\n const localStats = Statistics.localStats.splice(i, 1);\n\n localStats[0].stop();\n break;\n }\n }\n};\n\n/**\n * Stops remote RTP stats for given peerconnection ID.\n * @param {string} tpcId {@link TraceablePeerConnection.id}\n * @private\n */\nStatistics.prototype._stopRemoteStats = function(tpcId) {\n const rtpStats = this.rtpStatsMap.get(tpcId);\n\n if (rtpStats) {\n rtpStats.stop();\n this.rtpStatsMap.delete(tpcId);\n }\n};\n\n/**\n * Stops collecting RTP stats for given peerconnection\n * @param {TraceablePeerConnection} tpc\n */\nStatistics.prototype.stopRemoteStats = function(tpc) {\n this._stopRemoteStats(tpc.id);\n};\n\n// CALSTATS METHODS\n\n/**\n * Initializes the callstats.io API.\n * @param {TraceablePeerConnection} tpc the {@link TraceablePeerConnection}\n * instance for which CalStats will be started.\n * @param {string} remoteUserID\n */\nStatistics.prototype.startCallStats = function(tpc, remoteUserID) {\n if (!this.callStatsIntegrationEnabled) {\n return;\n } else if (this.callsStatsInstances.has(tpc.id)) {\n logger.error('CallStats instance for ${tpc} exists already');\n\n return;\n }\n\n logger.info(`Starting CallStats for ${tpc}...`);\n\n const newInstance\n = new CallStats(\n tpc,\n {\n confID: this.options.confID,\n remoteUserID\n });\n\n this.callsStatsInstances.set(tpc.id, newInstance);\n};\n\n/**\n * Obtains the list of *all* {@link CallStats} instances collected from every\n * valid {@link Statistics} instance.\n * @return {Set}\n * @private\n */\nStatistics._getAllCallStatsInstances = function() {\n const csInstances = new Set();\n\n for (const statistics of Statistics.instances) {\n for (const cs of statistics.callsStatsInstances.values()) {\n csInstances.add(cs);\n }\n }\n\n return csInstances;\n};\n\n/**\n * Removes the callstats.io instances.\n */\nStatistics.prototype.stopCallStats = function(tpc) {\n const callStatsInstance = this.callsStatsInstances.get(tpc.id);\n\n if (callStatsInstance) {\n // FIXME the original purpose of adding BEFORE_DISPOSED event was to be\n // able to submit the last log batch from jitsi-meet to CallStats. After\n // recent changes we dispose the CallStats earlier\n // (before Statistics.dispose), so we need to emit this event here to\n // give this last chance for final log batch submission.\n //\n // Eventually there should be a separate module called \"log storage\"\n // which should emit proper events when it's underlying\n // CallStats instance is going away.\n if (this.callsStatsInstances.size === 1) {\n this.eventEmitter.emit(StatisticsEvents.BEFORE_DISPOSED);\n }\n this.callsStatsInstances.delete(tpc.id);\n\n // The fabric needs to be terminated when being stopped\n callStatsInstance.sendTerminateEvent();\n }\n};\n\n/**\n * Returns true if the callstats integration is enabled, otherwise returns\n * false.\n *\n * @returns true if the callstats integration is enabled, otherwise returns\n * false.\n */\nStatistics.prototype.isCallstatsEnabled = function() {\n return this.callStatsIntegrationEnabled;\n};\n\n/**\n * Logs either resume or hold event for the given peer connection.\n * @param {TraceablePeerConnection} tpc the connection for which event will be\n * reported\n * @param {boolean} isResume true for resume or false for hold\n */\nStatistics.prototype.sendConnectionResumeOrHoldEvent = function(tpc, isResume) {\n const instance = this.callsStatsInstances.get(tpc.id);\n\n if (instance) {\n instance.sendResumeOrHoldEvent(isResume);\n }\n};\n\n/**\n * Notifies CallStats and analytics (if present) for ice connection failed\n * @param {TraceablePeerConnection} tpc connection on which failure occurred.\n */\nStatistics.prototype.sendIceConnectionFailedEvent = function(tpc) {\n const instance = this.callsStatsInstances.get(tpc.id);\n\n if (instance) {\n instance.sendIceConnectionFailedEvent();\n }\n};\n\n/**\n * Notifies CallStats for mute events\n * @param {TraceablePeerConnection} tpc connection on which failure occurred.\n * @param {boolean} muted true for muted and false for not muted\n * @param {String} type \"audio\"/\"video\"\n */\nStatistics.prototype.sendMuteEvent = function(tpc, muted, type) {\n const instance = tpc && this.callsStatsInstances.get(tpc.id);\n\n CallStats.sendMuteEvent(muted, type, instance);\n};\n\n/**\n * Notifies CallStats for screen sharing events\n * @param start {boolean} true for starting screen sharing and\n * false for not stopping\n * @param {string|null} ssrc - optional ssrc value, used only when\n * starting screen sharing.\n */\nStatistics.prototype.sendScreenSharingEvent\n = function(start, ssrc) {\n for (const cs of this.callsStatsInstances.values()) {\n cs.sendScreenSharingEvent(start, ssrc);\n }\n };\n\n/**\n * Notifies the statistics module that we are now the dominant speaker of the\n * conference.\n * @param {String} roomJid - The room jid where the speaker event occurred.\n */\nStatistics.prototype.sendDominantSpeakerEvent = function(roomJid) {\n for (const cs of this.callsStatsInstances.values()) {\n cs.sendDominantSpeakerEvent();\n }\n\n // xmpp send dominant speaker event\n this.xmpp.sendDominantSpeakerEvent(roomJid);\n};\n\n/**\n * Notifies about active device.\n * @param {{deviceList: {String:String}}} devicesData - list of devices with\n * their data\n */\nStatistics.sendActiveDeviceListEvent = function(devicesData) {\n const globalSet = Statistics._getAllCallStatsInstances();\n\n if (globalSet.size) {\n for (const cs of globalSet) {\n CallStats.sendActiveDeviceListEvent(devicesData, cs);\n }\n } else {\n CallStats.sendActiveDeviceListEvent(devicesData, null);\n }\n};\n\n/* eslint-disable max-params */\n\n/**\n * Lets the underlying statistics module know where is given SSRC rendered by\n * providing renderer tag ID.\n * @param {TraceablePeerConnection} tpc the connection to which the stream\n * belongs to\n * @param {number} ssrc the SSRC of the stream\n * @param {boolean} isLocal\n * @param {string} userId\n * @param {string} usageLabel meaningful usage label of this stream like\n * 'microphone', 'camera' or 'screen'.\n * @param {string} containerId the id of media 'audio' or 'video' tag which\n * renders the stream.\n */\nStatistics.prototype.associateStreamWithVideoTag = function(\n tpc,\n ssrc,\n isLocal,\n userId,\n usageLabel,\n containerId) {\n const instance = this.callsStatsInstances.get(tpc.id);\n\n if (instance) {\n instance.associateStreamWithVideoTag(\n ssrc,\n isLocal,\n userId,\n usageLabel,\n containerId);\n }\n};\n\n/* eslint-enable max-params */\n\n/**\n * Notifies CallStats that getUserMedia failed.\n *\n * @param {Error} e error to send\n */\nStatistics.sendGetUserMediaFailed = function(e) {\n const error\n = e instanceof JitsiTrackError\n ? formatJitsiTrackErrorForCallStats(e) : e;\n const globalSet = Statistics._getAllCallStatsInstances();\n\n if (globalSet.size) {\n for (const cs of globalSet) {\n CallStats.sendGetUserMediaFailed(error, cs);\n }\n } else {\n CallStats.sendGetUserMediaFailed(error, null);\n }\n};\n\n/**\n * Notifies CallStats that peer connection failed to create offer.\n *\n * @param {Error} e error to send\n * @param {TraceablePeerConnection} tpc connection on which failure occurred.\n */\nStatistics.prototype.sendCreateOfferFailed = function(e, tpc) {\n const instance = this.callsStatsInstances.get(tpc.id);\n\n if (instance) {\n instance.sendCreateOfferFailed(e);\n }\n};\n\n/**\n * Notifies CallStats that peer connection failed to create answer.\n *\n * @param {Error} e error to send\n * @param {TraceablePeerConnection} tpc connection on which failure occured.\n */\nStatistics.prototype.sendCreateAnswerFailed = function(e, tpc) {\n const instance = this.callsStatsInstances.get(tpc.id);\n\n if (instance) {\n instance.sendCreateAnswerFailed(e);\n }\n};\n\n/**\n * Notifies CallStats that peer connection failed to set local description.\n *\n * @param {Error} e error to send\n * @param {TraceablePeerConnection} tpc connection on which failure occurred.\n */\nStatistics.prototype.sendSetLocalDescFailed = function(e, tpc) {\n const instance = this.callsStatsInstances.get(tpc.id);\n\n if (instance) {\n instance.sendSetLocalDescFailed(e);\n }\n};\n\n/**\n * Notifies CallStats that peer connection failed to set remote description.\n *\n * @param {Error} e error to send\n * @param {TraceablePeerConnection} tpc connection on which failure occurred.\n */\nStatistics.prototype.sendSetRemoteDescFailed = function(e, tpc) {\n const instance = this.callsStatsInstances.get(tpc.id);\n\n if (instance) {\n instance.sendSetRemoteDescFailed(e);\n }\n};\n\n/**\n * Notifies CallStats that peer connection failed to add ICE candidate.\n *\n * @param {Error} e error to send\n * @param {TraceablePeerConnection} tpc connection on which failure occurred.\n */\nStatistics.prototype.sendAddIceCandidateFailed = function(e, tpc) {\n const instance = this.callsStatsInstances.get(tpc.id);\n\n if (instance) {\n instance.sendAddIceCandidateFailed(e);\n }\n};\n\n/**\n * Adds to CallStats an application log.\n *\n * @param {String} m a log message to send or an {Error} object to be reported\n */\nStatistics.sendLog = function(m) {\n const globalSubSet = new Set();\n\n // FIXME we don't want to duplicate logs over P2P instance, but\n // here we should go over instances and call this method for each\n // unique conference ID rather than selecting the first one.\n // We don't have such use case though, so leaving as is for now.\n for (const stats of Statistics.instances) {\n if (stats.callStatsApplicationLogsDisabled) {\n return;\n }\n\n if (stats.callsStatsInstances.size) {\n globalSubSet.add(stats.callsStatsInstances.values().next().value);\n }\n }\n\n if (globalSubSet.size) {\n for (const csPerStats of globalSubSet) {\n CallStats.sendApplicationLog(m, csPerStats);\n }\n } else {\n CallStats.sendApplicationLog(m, null);\n }\n};\n\n/**\n * Sends the given feedback through CallStats.\n *\n * @param overall an integer between 1 and 5 indicating the user's rating.\n * @param comment the comment from the user.\n * @returns {Promise} Resolves when callstats feedback has been submitted\n * successfully.\n */\nStatistics.prototype.sendFeedback = function(overall, comment) {\n // Statistics.analytics.sendEvent is currently fire and forget, without\n // confirmation of successful send.\n Statistics.analytics.sendEvent(\n FEEDBACK,\n {\n rating: overall,\n comment\n });\n\n return CallStats.sendFeedback(this.options.confID, overall, comment);\n};\n\nStatistics.LOCAL_JID = require('../../service/statistics/constants').LOCAL_JID;\n\n/**\n * Reports global error to CallStats.\n *\n * @param {Error} error\n */\nStatistics.reportGlobalError = function(error) {\n if (error instanceof JitsiTrackError && error.gum) {\n Statistics.sendGetUserMediaFailed(error);\n } else {\n Statistics.sendLog(error);\n }\n};\n\n/**\n * Sends event to analytics and logs a message to the logger/console. Console\n * messages might also be logged to callstats automatically.\n *\n * @param {string | Object} event the event name, or an object which\n * represents the entire event.\n * @param {Object} properties properties to attach to the event (if an event\n * name as opposed to an event object is provided).\n */\nStatistics.sendAnalyticsAndLog = function(event, properties = {}) {\n if (!event) {\n logger.warn('No event or event name given.');\n\n return;\n }\n\n let eventToLog;\n\n // Also support an API with a single object as an event.\n if (typeof event === 'object') {\n eventToLog = event;\n } else {\n eventToLog = {\n name: event,\n properties\n };\n }\n\n logger.log(JSON.stringify(eventToLog));\n\n // We do this last, because it may modify the object which is passed.\n this.analytics.sendEvent(event, properties);\n};\n\n/**\n * Sends event to analytics.\n *\n * @param {string | Object} eventName the event name, or an object which\n * represents the entire event.\n * @param {Object} properties properties to attach to the event\n */\nStatistics.sendAnalytics = function(eventName, properties = {}) {\n this.analytics.sendEvent(eventName, properties);\n};\n","import { getLogger } from '@jitsi/logger';\nimport { Strophe } from 'strophe.js';\n\nimport * as JitsiConferenceErrors from './JitsiConferenceErrors';\nimport * as JitsiConferenceEvents from './JitsiConferenceEvents';\nimport { SPEAKERS_AUDIO_LEVELS } from './modules/statistics/constants';\nimport Statistics from './modules/statistics/statistics';\nimport EventEmitterForwarder from './modules/util/EventEmitterForwarder';\nimport * as MediaType from './service/RTC/MediaType';\nimport RTCEvents from './service/RTC/RTCEvents';\nimport VideoType from './service/RTC/VideoType';\nimport AuthenticationEvents\n from './service/authentication/AuthenticationEvents';\nimport {\n ACTION_JINGLE_SA_TIMEOUT,\n createBridgeDownEvent,\n createConnectionStageReachedEvent,\n createFocusLeftEvent,\n createJingleEvent,\n createRemotelyMutedEvent\n} from './service/statistics/AnalyticsEvents';\nimport XMPPEvents from './service/xmpp/XMPPEvents';\n\nconst logger = getLogger(__filename);\n\n/**\n * Setups all event listeners related to conference\n * @param conference {JitsiConference} the conference\n */\nexport default function JitsiConferenceEventManager(conference) {\n this.conference = conference;\n this.xmppListeners = {};\n\n // Listeners related to the conference only\n conference.on(JitsiConferenceEvents.TRACK_MUTE_CHANGED,\n track => {\n if (!track.isLocal() || !conference.statistics) {\n return;\n }\n const session\n = track.isP2P\n ? conference.p2pJingleSession : conference.jvbJingleSession;\n\n // TPC will be null, before the conference starts, but the event\n // still should be queued\n const tpc = (session && session.peerconnection) || null;\n\n conference.statistics.sendMuteEvent(\n tpc,\n track.isMuted(),\n track.getType());\n });\n}\n\n/**\n * Setups event listeners related to conference.chatRoom\n */\nJitsiConferenceEventManager.prototype.setupChatRoomListeners = function() {\n const conference = this.conference;\n const chatRoom = conference.room;\n\n this.chatRoomForwarder = new EventEmitterForwarder(chatRoom,\n this.conference.eventEmitter);\n\n chatRoom.addListener(XMPPEvents.ICE_RESTARTING, jingleSession => {\n if (!jingleSession.isP2P) {\n // If using DataChannel as bridge channel, it must be closed\n // before ICE restart, otherwise Chrome will not trigger \"opened\"\n // event for the channel established with the new bridge.\n // TODO: This may be bypassed when using a WebSocket as bridge\n // channel.\n conference.rtc.closeBridgeChannel();\n }\n\n // else: there are no DataChannels in P2P session (at least for now)\n });\n\n chatRoom.addListener(XMPPEvents.PARTICIPANT_FEATURES_CHANGED, (from, features) => {\n const participant = conference.getParticipantById(Strophe.getResourceFromJid(from));\n\n if (participant) {\n participant.setFeatures(features);\n conference.eventEmitter.emit(JitsiConferenceEvents.PARTCIPANT_FEATURES_CHANGED, participant);\n }\n });\n\n chatRoom.addListener(\n XMPPEvents.ICE_RESTART_SUCCESS,\n (jingleSession, offerIq) => {\n // The JVB data chanel needs to be reopened in case the conference\n // has been moved to a new bridge.\n !jingleSession.isP2P\n && conference._setBridgeChannel(\n offerIq, jingleSession.peerconnection);\n });\n\n\n chatRoom.addListener(XMPPEvents.AUDIO_MUTED_BY_FOCUS,\n actor => {\n // TODO: Add a way to differentiate between commands which caused\n // us to mute and those that did not change our state (i.e. we were\n // already muted).\n Statistics.sendAnalytics(createRemotelyMutedEvent(MediaType.AUDIO));\n\n conference.mutedByFocusActor = actor;\n\n // set isMutedByFocus when setAudioMute Promise ends\n conference.rtc.setAudioMute(true).then(\n () => {\n conference.isMutedByFocus = true;\n conference.mutedByFocusActor = null;\n })\n .catch(\n error => {\n conference.mutedByFocusActor = null;\n logger.warn(\n 'Error while audio muting due to focus request', error);\n });\n }\n );\n\n chatRoom.addListener(XMPPEvents.VIDEO_MUTED_BY_FOCUS,\n actor => {\n // TODO: Add a way to differentiate between commands which caused\n // us to mute and those that did not change our state (i.e. we were\n // already muted).\n Statistics.sendAnalytics(createRemotelyMutedEvent(MediaType.VIDEO));\n\n conference.mutedVideoByFocusActor = actor;\n\n // set isVideoMutedByFocus when setVideoMute Promise ends\n conference.rtc.setVideoMute(true).then(\n () => {\n conference.isVideoMutedByFocus = true;\n conference.mutedVideoByFocusActor = null;\n })\n .catch(\n error => {\n conference.mutedVideoByFocusActor = null;\n logger.warn(\n 'Error while video muting due to focus request', error);\n });\n }\n );\n\n this.chatRoomForwarder.forward(XMPPEvents.SUBJECT_CHANGED,\n JitsiConferenceEvents.SUBJECT_CHANGED);\n\n this.chatRoomForwarder.forward(XMPPEvents.MUC_JOINED,\n JitsiConferenceEvents.CONFERENCE_JOINED);\n\n this.chatRoomForwarder.forward(XMPPEvents.MUC_JOIN_IN_PROGRESS,\n JitsiConferenceEvents.CONFERENCE_JOIN_IN_PROGRESS);\n\n this.chatRoomForwarder.forward(XMPPEvents.MEETING_ID_SET,\n JitsiConferenceEvents.CONFERENCE_UNIQUE_ID_SET);\n\n // send some analytics events\n chatRoom.addListener(XMPPEvents.MUC_JOINED,\n () => {\n this.conference._onMucJoined();\n\n this.conference.isJvbConnectionInterrupted = false;\n\n // TODO: Move all of the 'connectionTimes' logic to its own module.\n Object.keys(chatRoom.connectionTimes).forEach(key => {\n const event\n = createConnectionStageReachedEvent(\n `conference_${key}`,\n { value: chatRoom.connectionTimes[key] });\n\n Statistics.sendAnalytics(event);\n });\n\n // TODO: Move all of the 'connectionTimes' logic to its own module.\n Object.keys(chatRoom.xmpp.connectionTimes).forEach(key => {\n const event\n = createConnectionStageReachedEvent(\n `xmpp_${key}`,\n { value: chatRoom.xmpp.connectionTimes[key] });\n\n Statistics.sendAnalytics(event);\n });\n });\n\n chatRoom.addListener(XMPPEvents.RENEGOTIATION_FAILED, (e, session) => {\n if (!session.isP2P) {\n conference.eventEmitter.emit(JitsiConferenceEvents.CONFERENCE_FAILED,\n JitsiConferenceErrors.OFFER_ANSWER_FAILED, e);\n }\n });\n\n this.chatRoomForwarder.forward(XMPPEvents.ROOM_JOIN_ERROR,\n JitsiConferenceEvents.CONFERENCE_FAILED,\n JitsiConferenceErrors.CONNECTION_ERROR);\n\n this.chatRoomForwarder.forward(XMPPEvents.ROOM_CONNECT_ERROR,\n JitsiConferenceEvents.CONFERENCE_FAILED,\n JitsiConferenceErrors.CONNECTION_ERROR);\n this.chatRoomForwarder.forward(XMPPEvents.ROOM_CONNECT_NOT_ALLOWED_ERROR,\n JitsiConferenceEvents.CONFERENCE_FAILED,\n JitsiConferenceErrors.NOT_ALLOWED_ERROR);\n this.chatRoomForwarder.forward(XMPPEvents.ROOM_CONNECT_MEMBERS_ONLY_ERROR,\n JitsiConferenceEvents.CONFERENCE_FAILED,\n JitsiConferenceErrors.MEMBERS_ONLY_ERROR);\n\n this.chatRoomForwarder.forward(XMPPEvents.ROOM_MAX_USERS_ERROR,\n JitsiConferenceEvents.CONFERENCE_FAILED,\n JitsiConferenceErrors.CONFERENCE_MAX_USERS);\n\n this.chatRoomForwarder.forward(XMPPEvents.PASSWORD_REQUIRED,\n JitsiConferenceEvents.CONFERENCE_FAILED,\n JitsiConferenceErrors.PASSWORD_REQUIRED);\n\n this.chatRoomForwarder.forward(XMPPEvents.AUTHENTICATION_REQUIRED,\n JitsiConferenceEvents.CONFERENCE_FAILED,\n JitsiConferenceErrors.AUTHENTICATION_REQUIRED);\n\n this.chatRoomForwarder.forward(XMPPEvents.BRIDGE_DOWN,\n JitsiConferenceEvents.CONFERENCE_FAILED,\n JitsiConferenceErrors.VIDEOBRIDGE_NOT_AVAILABLE);\n chatRoom.addListener(\n XMPPEvents.BRIDGE_DOWN,\n () => Statistics.sendAnalytics(createBridgeDownEvent()));\n\n chatRoom.addListener(XMPPEvents.CONNECTION_RESTARTED,\n jingleSession => {\n conference._onConferenceRestarted(jingleSession);\n });\n\n this.chatRoomForwarder.forward(XMPPEvents.RESERVATION_ERROR,\n JitsiConferenceEvents.CONFERENCE_FAILED,\n JitsiConferenceErrors.RESERVATION_ERROR);\n chatRoom.addListener(XMPPEvents.RESERVATION_ERROR,\n () => {\n conference.xmpp.connection.emuc.doLeave(conference.room.roomjid);\n });\n\n this.chatRoomForwarder.forward(XMPPEvents.GRACEFUL_SHUTDOWN,\n JitsiConferenceEvents.CONFERENCE_FAILED,\n JitsiConferenceErrors.GRACEFUL_SHUTDOWN);\n\n chatRoom.addListener(XMPPEvents.CONNECTION_ICE_FAILED,\n jingleSession => {\n conference._onIceConnectionFailed(jingleSession);\n });\n\n this.chatRoomForwarder.forward(XMPPEvents.MUC_DESTROYED,\n JitsiConferenceEvents.CONFERENCE_FAILED,\n JitsiConferenceErrors.CONFERENCE_DESTROYED);\n\n this.chatRoomForwarder.forward(XMPPEvents.CHAT_ERROR_RECEIVED,\n JitsiConferenceEvents.CONFERENCE_ERROR,\n JitsiConferenceErrors.CHAT_ERROR);\n\n this.chatRoomForwarder.forward(XMPPEvents.SETTINGS_ERROR_RECEIVED,\n JitsiConferenceEvents.CONFERENCE_ERROR,\n JitsiConferenceErrors.SETTINGS_ERROR);\n\n this.chatRoomForwarder.forward(XMPPEvents.FOCUS_DISCONNECTED,\n JitsiConferenceEvents.CONFERENCE_FAILED,\n JitsiConferenceErrors.FOCUS_DISCONNECTED);\n\n chatRoom.addListener(XMPPEvents.FOCUS_LEFT,\n () => {\n Statistics.sendAnalytics(createFocusLeftEvent());\n conference.eventEmitter.emit(\n JitsiConferenceEvents.CONFERENCE_FAILED,\n JitsiConferenceErrors.FOCUS_LEFT);\n });\n\n chatRoom.addListener(XMPPEvents.SESSION_ACCEPT_TIMEOUT,\n jingleSession => {\n Statistics.sendAnalyticsAndLog(\n createJingleEvent(\n ACTION_JINGLE_SA_TIMEOUT,\n { p2p: jingleSession.isP2P }));\n });\n\n chatRoom.addListener(XMPPEvents.RECORDER_STATE_CHANGED,\n (session, jid) => {\n\n if (jid) {\n const resource = Strophe.getResourceFromJid(jid);\n const participant = conference.getParticipantById(resource) || resource;\n\n if (session.getStatus() === 'off') {\n session.setTerminator(participant);\n } else if (session.getStatus() === 'on') {\n session.setInitiator(participant);\n }\n }\n\n conference.eventEmitter.emit(\n JitsiConferenceEvents.RECORDER_STATE_CHANGED,\n session);\n });\n\n this.chatRoomForwarder.forward(XMPPEvents.TRANSCRIPTION_STATUS_CHANGED,\n JitsiConferenceEvents.TRANSCRIPTION_STATUS_CHANGED);\n\n this.chatRoomForwarder.forward(XMPPEvents.VIDEO_SIP_GW_AVAILABILITY_CHANGED,\n JitsiConferenceEvents.VIDEO_SIP_GW_AVAILABILITY_CHANGED);\n\n this.chatRoomForwarder.forward(\n XMPPEvents.VIDEO_SIP_GW_SESSION_STATE_CHANGED,\n JitsiConferenceEvents.VIDEO_SIP_GW_SESSION_STATE_CHANGED);\n\n this.chatRoomForwarder.forward(XMPPEvents.PHONE_NUMBER_CHANGED,\n JitsiConferenceEvents.PHONE_NUMBER_CHANGED);\n\n chatRoom.setParticipantPropertyListener((node, from) => {\n const participant = conference.getParticipantById(from);\n\n if (!participant) {\n return;\n }\n\n participant.setProperty(\n node.tagName.substring('jitsi_participant_'.length),\n node.value);\n });\n\n chatRoom.addListener(XMPPEvents.KICKED,\n conference.onMemberKicked.bind(conference));\n chatRoom.addListener(XMPPEvents.SUSPEND_DETECTED,\n conference.onSuspendDetected.bind(conference));\n\n this.chatRoomForwarder.forward(XMPPEvents.MUC_LOCK_CHANGED,\n JitsiConferenceEvents.LOCK_STATE_CHANGED);\n\n this.chatRoomForwarder.forward(XMPPEvents.MUC_MEMBERS_ONLY_CHANGED,\n JitsiConferenceEvents.MEMBERS_ONLY_CHANGED);\n\n chatRoom.addListener(XMPPEvents.MUC_MEMBER_JOINED,\n conference.onMemberJoined.bind(conference));\n this.chatRoomForwarder.forward(XMPPEvents.MUC_LOBBY_MEMBER_JOINED,\n JitsiConferenceEvents.LOBBY_USER_JOINED);\n this.chatRoomForwarder.forward(XMPPEvents.MUC_LOBBY_MEMBER_UPDATED,\n JitsiConferenceEvents.LOBBY_USER_UPDATED);\n this.chatRoomForwarder.forward(XMPPEvents.MUC_LOBBY_MEMBER_LEFT,\n JitsiConferenceEvents.LOBBY_USER_LEFT);\n chatRoom.addListener(XMPPEvents.MUC_MEMBER_BOT_TYPE_CHANGED,\n conference._onMemberBotTypeChanged.bind(conference));\n chatRoom.addListener(XMPPEvents.MUC_MEMBER_LEFT,\n conference.onMemberLeft.bind(conference));\n this.chatRoomForwarder.forward(XMPPEvents.MUC_LEFT,\n JitsiConferenceEvents.CONFERENCE_LEFT);\n this.chatRoomForwarder.forward(XMPPEvents.MUC_DENIED_ACCESS,\n JitsiConferenceEvents.CONFERENCE_FAILED,\n JitsiConferenceErrors.CONFERENCE_ACCESS_DENIED);\n\n chatRoom.addListener(XMPPEvents.DISPLAY_NAME_CHANGED,\n conference.onDisplayNameChanged.bind(conference));\n\n chatRoom.addListener(XMPPEvents.LOCAL_ROLE_CHANGED, role => {\n conference.onLocalRoleChanged(role);\n\n // log all events for the recorder operated by the moderator\n if (conference.statistics && conference.isModerator()) {\n conference.on(JitsiConferenceEvents.RECORDER_STATE_CHANGED,\n recorderSession => {\n const logObject = {\n error: recorderSession.getError(),\n id: 'recorder_status',\n status: recorderSession.getStatus()\n };\n\n Statistics.sendLog(JSON.stringify(logObject));\n });\n }\n });\n\n chatRoom.addListener(XMPPEvents.MUC_ROLE_CHANGED,\n conference.onUserRoleChanged.bind(conference));\n\n chatRoom.addListener(AuthenticationEvents.IDENTITY_UPDATED,\n (authEnabled, authIdentity) => {\n conference.authEnabled = authEnabled;\n conference.authIdentity = authIdentity;\n conference.eventEmitter.emit(\n JitsiConferenceEvents.AUTH_STATUS_CHANGED, authEnabled,\n authIdentity);\n });\n\n chatRoom.addListener(\n XMPPEvents.MESSAGE_RECEIVED,\n\n // eslint-disable-next-line max-params\n (jid, txt, myJid, ts) => {\n const id = Strophe.getResourceFromJid(jid);\n\n conference.eventEmitter.emit(\n JitsiConferenceEvents.MESSAGE_RECEIVED,\n id, txt, ts);\n });\n\n chatRoom.addListener(\n XMPPEvents.PRIVATE_MESSAGE_RECEIVED,\n\n // eslint-disable-next-line max-params\n (jid, txt, myJid, ts) => {\n const id = Strophe.getResourceFromJid(jid);\n\n conference.eventEmitter.emit(\n JitsiConferenceEvents.PRIVATE_MESSAGE_RECEIVED,\n id, txt, ts);\n });\n\n chatRoom.addListener(XMPPEvents.PRESENCE_STATUS,\n (jid, status) => {\n const id = Strophe.getResourceFromJid(jid);\n const participant = conference.getParticipantById(id);\n\n if (!participant || participant._status === status) {\n return;\n }\n participant._status = status;\n conference.eventEmitter.emit(\n JitsiConferenceEvents.USER_STATUS_CHANGED, id, status);\n });\n\n chatRoom.addListener(XMPPEvents.JSON_MESSAGE_RECEIVED,\n (from, payload) => {\n const id = Strophe.getResourceFromJid(from);\n const participant = conference.getParticipantById(id);\n\n if (participant) {\n conference.eventEmitter.emit(\n JitsiConferenceEvents.ENDPOINT_MESSAGE_RECEIVED,\n participant, payload);\n } else {\n conference.eventEmitter.emit(\n JitsiConferenceEvents.NON_PARTICIPANT_MESSAGE_RECEIVED,\n id, payload);\n }\n });\n\n chatRoom.addPresenceListener('startmuted', (data, from) => {\n let isModerator = false;\n\n if (conference.myUserId() === from && conference.isModerator()) {\n isModerator = true;\n } else {\n const participant = conference.getParticipantById(from);\n\n if (participant && participant.isModerator()) {\n isModerator = true;\n }\n }\n\n if (!isModerator) {\n return;\n }\n\n const startAudioMuted = data.attributes.audio === 'true';\n const startVideoMuted = data.attributes.video === 'true';\n\n let updated = false;\n\n if (startAudioMuted !== conference.startMutedPolicy.audio) {\n conference.startMutedPolicy.audio = startAudioMuted;\n updated = true;\n }\n\n if (startVideoMuted !== conference.startMutedPolicy.video) {\n conference.startMutedPolicy.video = startVideoMuted;\n updated = true;\n }\n\n if (updated) {\n conference.eventEmitter.emit(\n JitsiConferenceEvents.START_MUTED_POLICY_CHANGED,\n conference.startMutedPolicy\n );\n }\n });\n\n if (conference.statistics) {\n // FIXME ICE related events should end up in RTCEvents eventually\n chatRoom.addListener(XMPPEvents.CONNECTION_ICE_FAILED,\n session => {\n conference.statistics.sendIceConnectionFailedEvent(\n session.peerconnection);\n });\n\n // FIXME XMPPEvents.ADD_ICE_CANDIDATE_FAILED is never emitted\n chatRoom.addListener(XMPPEvents.ADD_ICE_CANDIDATE_FAILED,\n (e, pc) => {\n conference.statistics.sendAddIceCandidateFailed(e, pc);\n });\n }\n\n // Breakout rooms.\n this.chatRoomForwarder.forward(XMPPEvents.BREAKOUT_ROOMS_MOVE_TO_ROOM,\n JitsiConferenceEvents.BREAKOUT_ROOMS_MOVE_TO_ROOM);\n this.chatRoomForwarder.forward(XMPPEvents.BREAKOUT_ROOMS_UPDATED,\n JitsiConferenceEvents.BREAKOUT_ROOMS_UPDATED);\n};\n\n/**\n * Setups event listeners related to conference.rtc\n */\nJitsiConferenceEventManager.prototype.setupRTCListeners = function() {\n const conference = this.conference;\n const rtc = conference.rtc;\n\n rtc.addListener(\n RTCEvents.REMOTE_TRACK_ADDED,\n conference.onRemoteTrackAdded.bind(conference));\n\n rtc.addListener(\n RTCEvents.REMOTE_TRACK_REMOVED,\n conference.onRemoteTrackRemoved.bind(conference));\n\n rtc.addListener(RTCEvents.DOMINANT_SPEAKER_CHANGED,\n (dominant, previous) => {\n if (conference.lastDominantSpeaker !== dominant && conference.room) {\n conference.lastDominantSpeaker = dominant;\n conference.eventEmitter.emit(\n JitsiConferenceEvents.DOMINANT_SPEAKER_CHANGED, dominant, previous);\n\n if (previous && previous.length) {\n const speakerList = previous.slice(0);\n\n // Add the dominant speaker to the top of the list (exclude self).\n if (conference.myUserId !== dominant) {\n speakerList.splice(0, 0, dominant);\n }\n\n // Trim the list to the top 5 speakers only.\n if (speakerList.length > SPEAKERS_AUDIO_LEVELS) {\n speakerList.splice(SPEAKERS_AUDIO_LEVELS, speakerList.length - SPEAKERS_AUDIO_LEVELS);\n }\n conference.statistics && conference.statistics.setSpeakerList(speakerList);\n }\n if (conference.statistics && conference.myUserId() === dominant) {\n // We are the new dominant speaker.\n conference.statistics.sendDominantSpeakerEvent(conference.room.roomjid);\n }\n }\n });\n\n rtc.addListener(RTCEvents.DATA_CHANNEL_OPEN, () => {\n const now = window.performance.now();\n const key = 'data.channel.opened';\n\n // TODO: Move all of the 'connectionTimes' logic to its own module.\n logger.log(`(TIME) ${key}:\\t`, now);\n conference.room.connectionTimes[key] = now;\n Statistics.sendAnalytics(\n createConnectionStageReachedEvent(key, { value: now }));\n\n conference.eventEmitter.emit(JitsiConferenceEvents.DATA_CHANNEL_OPENED);\n });\n\n rtc.addListener(RTCEvents.ENDPOINT_MESSAGE_RECEIVED,\n (from, payload) => {\n const participant = conference.getParticipantById(from);\n\n if (participant) {\n conference.eventEmitter.emit(\n JitsiConferenceEvents.ENDPOINT_MESSAGE_RECEIVED,\n participant, payload);\n } else {\n logger.warn(\n 'Ignored ENDPOINT_MESSAGE_RECEIVED for not existing '\n + `participant: ${from}`,\n payload);\n }\n });\n\n rtc.addListener(RTCEvents.ENDPOINT_STATS_RECEIVED,\n (from, payload) => {\n const participant = conference.getParticipantById(from);\n\n if (participant) {\n conference.eventEmitter.emit(JitsiConferenceEvents.ENDPOINT_STATS_RECEIVED, participant, payload);\n } else {\n logger.warn(`Ignoring ENDPOINT_STATS_RECEIVED for a non-existant participant: ${from}`);\n }\n });\n\n rtc.addListener(RTCEvents.LOCAL_UFRAG_CHANGED,\n (tpc, ufrag) => {\n if (!tpc.isP2P) {\n Statistics.sendLog(\n JSON.stringify({\n id: 'local_ufrag',\n value: ufrag\n }));\n }\n });\n rtc.addListener(RTCEvents.REMOTE_UFRAG_CHANGED,\n (tpc, ufrag) => {\n if (!tpc.isP2P) {\n Statistics.sendLog(\n JSON.stringify({\n id: 'remote_ufrag',\n value: ufrag\n }));\n }\n });\n\n rtc.addListener(RTCEvents.CREATE_ANSWER_FAILED,\n (e, tpc) => {\n conference.statistics.sendCreateAnswerFailed(e, tpc);\n if (!tpc.isP2P) {\n conference.eventEmitter.emit(JitsiConferenceEvents.CONFERENCE_FAILED,\n JitsiConferenceErrors.OFFER_ANSWER_FAILED, e);\n }\n });\n\n rtc.addListener(RTCEvents.CREATE_OFFER_FAILED,\n (e, tpc) => {\n conference.statistics.sendCreateOfferFailed(e, tpc);\n if (!tpc.isP2P) {\n conference.eventEmitter.emit(JitsiConferenceEvents.CONFERENCE_FAILED,\n JitsiConferenceErrors.OFFER_ANSWER_FAILED, e);\n }\n });\n\n rtc.addListener(RTCEvents.SET_LOCAL_DESCRIPTION_FAILED,\n (e, tpc) => {\n conference.statistics.sendSetLocalDescFailed(e, tpc);\n if (!tpc.isP2P) {\n conference.eventEmitter.emit(JitsiConferenceEvents.CONFERENCE_FAILED,\n JitsiConferenceErrors.OFFER_ANSWER_FAILED, e);\n }\n });\n\n rtc.addListener(RTCEvents.SET_REMOTE_DESCRIPTION_FAILED,\n (e, tpc) => {\n conference.statistics.sendSetRemoteDescFailed(e, tpc);\n if (!tpc.isP2P) {\n conference.eventEmitter.emit(JitsiConferenceEvents.CONFERENCE_FAILED,\n JitsiConferenceErrors.OFFER_ANSWER_FAILED, e);\n }\n });\n\n rtc.addListener(RTCEvents.LOCAL_TRACK_SSRC_UPDATED,\n (track, ssrc) => {\n // when starting screen sharing, the track is created and when\n // we do set local description and we process the ssrc we\n // will be notified for it and we will report it with the event\n // for screen sharing\n if (track.isVideoTrack() && track.videoType === VideoType.DESKTOP) {\n conference.statistics.sendScreenSharingEvent(true, ssrc);\n }\n });\n};\n\n/**\n * Removes event listeners related to conference.xmpp\n */\nJitsiConferenceEventManager.prototype.removeXMPPListeners = function() {\n const conference = this.conference;\n\n Object.keys(this.xmppListeners).forEach(eventName => {\n conference.xmpp.removeListener(\n eventName,\n this.xmppListeners[eventName]);\n });\n this.xmppListeners = {};\n};\n\n\n/**\n * Setups event listeners related to conference.xmpp\n */\nJitsiConferenceEventManager.prototype.setupXMPPListeners = function() {\n const conference = this.conference;\n\n this._addConferenceXMPPListener(\n XMPPEvents.CALL_INCOMING,\n conference.onIncomingCall.bind(conference));\n this._addConferenceXMPPListener(\n XMPPEvents.CALL_ACCEPTED,\n conference.onCallAccepted.bind(conference));\n this._addConferenceXMPPListener(\n XMPPEvents.TRANSPORT_INFO,\n conference.onTransportInfo.bind(conference));\n this._addConferenceXMPPListener(\n XMPPEvents.CALL_ENDED,\n conference.onCallEnded.bind(conference));\n\n this._addConferenceXMPPListener(XMPPEvents.START_MUTED_FROM_FOCUS,\n (audioMuted, videoMuted) => {\n if (conference.options.config.ignoreStartMuted) {\n return;\n }\n\n conference.startAudioMuted = audioMuted;\n conference.startVideoMuted = videoMuted;\n\n // mute existing local tracks because this is initial mute from\n // Jicofo\n conference.getLocalTracks().forEach(track => {\n switch (track.getType()) {\n case MediaType.AUDIO:\n conference.startAudioMuted && track.mute();\n break;\n case MediaType.VIDEO:\n conference.startVideoMuted && track.mute();\n break;\n }\n });\n\n conference.eventEmitter.emit(JitsiConferenceEvents.STARTED_MUTED);\n });\n\n this._addConferenceXMPPListener(XMPPEvents.CONFERENCE_TIMESTAMP_RECEIVED,\n createdTimestamp => {\n conference.eventEmitter.emit(JitsiConferenceEvents.CONFERENCE_CREATED_TIMESTAMP, createdTimestamp);\n });\n\n this._addConferenceXMPPListener(XMPPEvents.AV_MODERATION_CHANGED,\n (value, mediaType, actorJid) => {\n const actorParticipant = conference.getParticipants().find(p => p.getJid() === actorJid);\n\n conference.eventEmitter.emit(JitsiConferenceEvents.AV_MODERATION_CHANGED, {\n enabled: value,\n mediaType,\n actor: actorParticipant\n });\n });\n this._addConferenceXMPPListener(XMPPEvents.AV_MODERATION_PARTICIPANT_APPROVED,\n (mediaType, jid) => {\n const participant = conference.getParticipantById(Strophe.getResourceFromJid(jid));\n\n if (participant) {\n conference.eventEmitter.emit(JitsiConferenceEvents.AV_MODERATION_PARTICIPANT_APPROVED, {\n participant,\n mediaType\n });\n }\n });\n this._addConferenceXMPPListener(XMPPEvents.AV_MODERATION_PARTICIPANT_REJECTED,\n (mediaType, jid) => {\n const participant = conference.getParticipantById(Strophe.getResourceFromJid(jid));\n\n if (participant) {\n conference.eventEmitter.emit(JitsiConferenceEvents.AV_MODERATION_PARTICIPANT_REJECTED, {\n participant,\n mediaType\n });\n }\n });\n this._addConferenceXMPPListener(XMPPEvents.AV_MODERATION_APPROVED,\n value => conference.eventEmitter.emit(JitsiConferenceEvents.AV_MODERATION_APPROVED, { mediaType: value }));\n this._addConferenceXMPPListener(XMPPEvents.AV_MODERATION_REJECTED,\n value => {\n conference.eventEmitter.emit(JitsiConferenceEvents.AV_MODERATION_REJECTED, { mediaType: value });\n });\n};\n\n/**\n * Add XMPP listener and save its reference for remove on leave conference.\n */\nJitsiConferenceEventManager.prototype._addConferenceXMPPListener = function(\n eventName, listener) {\n this.xmppListeners[eventName] = listener;\n this.conference.xmpp.addListener(eventName, listener);\n};\n\n/**\n * Setups event listeners related to conference.statistics\n */\nJitsiConferenceEventManager.prototype.setupStatisticsListeners = function() {\n const conference = this.conference;\n\n if (!conference.statistics) {\n return;\n }\n\n /* eslint-disable max-params */\n conference.statistics.addAudioLevelListener((tpc, ssrc, level, isLocal) => {\n conference.rtc.setAudioLevel(tpc, ssrc, level, isLocal);\n });\n\n /* eslint-enable max-params */\n\n // Forward the \"before stats disposed\" event\n conference.statistics.addBeforeDisposedListener(() => {\n conference.eventEmitter.emit(\n JitsiConferenceEvents.BEFORE_STATISTICS_DISPOSED);\n });\n\n // if we are in startSilent mode we will not be sending/receiving so nothing to detect\n if (!conference.options.config.startSilent) {\n conference.statistics.addByteSentStatsListener((tpc, stats) => {\n conference.getLocalTracks(MediaType.AUDIO).forEach(track => {\n const ssrc = tpc.getLocalSSRC(track);\n\n if (!ssrc || !stats.hasOwnProperty(ssrc)) {\n return;\n }\n\n track.onByteSentStatsReceived(tpc, stats[ssrc]);\n });\n });\n }\n};\n","/**\n * The media track was removed to the conference.\n */\nexport const LOCAL_TRACK_STOPPED = 'track.stopped';\n\n/**\n * Audio levels of a this track was changed.\n * The first argument is a number with audio level value in range [0, 1].\n * The second argument is a TraceablePeerConnection which is the peer\n * connection which measured the audio level (one audio track can be added\n * to multiple peer connection at the same time). This argument is optional for\n * local tracks for which we can measure audio level without the peer\n * connection (the value will be undefined).\n *\n * NOTE The second argument should be treated as library internal and can be\n * removed at any time.\n */\nexport const TRACK_AUDIO_LEVEL_CHANGED = 'track.audioLevelsChanged';\n\n/**\n * The audio output of the track was changed.\n */\nexport const TRACK_AUDIO_OUTPUT_CHANGED = 'track.audioOutputChanged';\n\n/**\n * A media track mute status was changed.\n */\nexport const TRACK_MUTE_CHANGED = 'track.trackMuteChanged';\n\n/**\n * The video type(\"camera\" or \"desktop\") of the track was changed.\n */\nexport const TRACK_VIDEOTYPE_CHANGED = 'track.videoTypeChanged';\n\n/**\n * Indicates that the track is not receiving any data even though we expect it\n * to receive data (i.e. the stream is not stopped).\n */\nexport const NO_DATA_FROM_SOURCE = 'track.no_data_from_source';\n\n/**\n * Indicates that the local audio track is not receiving any audio input from\n * the microphone that is currently selected.\n */\nexport const NO_AUDIO_INPUT = 'track.no_audio_input';\n\n/**\n * Event fired whenever video track's streaming changes.\n * First argument is the sourceName of the track and the second is a string indicating if the connection is currently\n * - active - the connection is active.\n * - inactive - the connection is inactive, was intentionally interrupted by the bridge because of low BWE or because\n * of the endpoint falling out of last N.\n * - interrupted - a network problem occurred.\n * - restoring - the connection was inactive and is restoring now.\n *\n * The current status value can be obtained by calling JitsiRemoteTrack.getTrackStreamingStatus().\n */\nexport const TRACK_STREAMING_STATUS_CHANGED = 'track.streaming_status_changed';\n","import { getLogger } from '@jitsi/logger';\n\nimport * as JitsiConferenceEvents from '../../JitsiConferenceEvents';\nimport * as JitsiTrackEvents from '../../JitsiTrackEvents';\nimport * as MediaType from '../../service/RTC/MediaType';\nimport RTCEvents from '../../service/RTC/RTCEvents';\nimport { createParticipantConnectionStatusEvent } from '../../service/statistics/AnalyticsEvents';\nimport browser from '../browser';\nimport Statistics from '../statistics/statistics';\n\nconst logger = getLogger(__filename);\n\n/**\n * Default value of 500 milliseconds for {@link ParticipantConnectionStatus.outOfLastNTimeout}.\n *\n * @type {number}\n */\nconst DEFAULT_NOT_IN_LAST_N_TIMEOUT = 500;\n\n/**\n * Default value of 2500 milliseconds for {@link ParticipantConnectionStatus.p2pRtcMuteTimeout}.\n */\nconst DEFAULT_P2P_RTC_MUTE_TIMEOUT = 2500;\n\n/**\n * Default value of 10000 milliseconds for {@link ParticipantConnectionStatus.rtcMuteTimeout}.\n *\n * @type {number}\n */\nconst DEFAULT_RTC_MUTE_TIMEOUT = 10000;\n\n/**\n * The time to wait a track to be restored. Track which was out of lastN\n * should be inactive and when entering lastN it becomes restoring and when\n * data is received from bridge it will become active, but if no data is\n * received for some time we set status of that participant connection to\n * interrupted.\n * @type {number}\n */\nconst DEFAULT_RESTORING_TIMEOUT = 10000;\n\n/**\n * Participant connection statuses.\n *\n * @type {{\n * ACTIVE: string,\n * INACTIVE: string,\n * INTERRUPTED: string,\n * RESTORING: string\n * }}\n */\nexport const ParticipantConnectionStatus = {\n /**\n * Status indicating that connection is currently active.\n */\n ACTIVE: 'active',\n\n /**\n * Status indicating that connection is currently inactive.\n * Inactive means the connection was stopped on purpose from the bridge,\n * like exiting lastN or adaptivity decided to drop video because of not\n * enough bandwidth.\n */\n INACTIVE: 'inactive',\n\n /**\n * Status indicating that connection is currently interrupted.\n */\n INTERRUPTED: 'interrupted',\n\n /**\n * Status indicating that connection is currently restoring.\n */\n RESTORING: 'restoring'\n};\n\n/**\n * Class is responsible for emitting\n * JitsiConferenceEvents.PARTICIPANT_CONN_STATUS_CHANGED events.\n */\nexport default class ParticipantConnectionStatusHandler {\n /* eslint-disable max-params*/\n /**\n * Calculates the new {@link ParticipantConnectionStatus} based on\n * the values given for some specific remote user. It is assumed that\n * the conference is currently in the JVB mode (in contrary to the P2P mode)\n * @param {boolean} isConnectionActiveByJvb true if the JVB did not get any\n * data from the user for the last 15 seconds.\n * @param {boolean} isInLastN indicates whether the user is in the last N\n * set. When set to false it means that JVB is not sending any video for\n * the user.\n * @param {boolean} isRestoringTimedout if true it means that the user has\n * been outside of last N too long to be considered\n * {@link ParticipantConnectionStatus.RESTORING}.\n * @param {boolean} isVideoMuted true if the user is video muted and we\n * should not expect to receive any video.\n * @param {boolean} isVideoTrackFrozen if the current browser support video\n * frozen detection then it will be set to true when the video track is\n * frozen. If the current browser does not support frozen detection the it's\n * always false.\n * @return {ParticipantConnectionStatus} the new connection status for\n * the user for whom the values above were provided.\n * @private\n */\n static _getNewStateForJvbMode(\n isConnectionActiveByJvb,\n isInLastN,\n isRestoringTimedout,\n isVideoMuted,\n isVideoTrackFrozen) {\n if (!isConnectionActiveByJvb) {\n // when there is a connection problem signaled from jvb\n // it means no media was flowing for at least 15secs, so both audio\n // and video are most likely interrupted\n return ParticipantConnectionStatus.INTERRUPTED;\n } else if (isVideoMuted) {\n // If the connection is active according to JVB and the user is\n // video muted there is no way for the connection to be inactive,\n // because the detection logic below only makes sense for video.\n return ParticipantConnectionStatus.ACTIVE;\n }\n\n // Logic when isVideoTrackFrozen is supported\n if (browser.supportsVideoMuteOnConnInterrupted()) {\n if (!isVideoTrackFrozen) {\n // If the video is playing we're good\n return ParticipantConnectionStatus.ACTIVE;\n } else if (isInLastN) {\n return isRestoringTimedout\n ? ParticipantConnectionStatus.INTERRUPTED\n : ParticipantConnectionStatus.RESTORING;\n }\n\n return ParticipantConnectionStatus.INACTIVE;\n }\n\n // Because this browser is incapable of detecting frozen video we must\n // rely on the lastN value\n return isInLastN\n ? ParticipantConnectionStatus.ACTIVE\n : ParticipantConnectionStatus.INACTIVE;\n }\n\n /* eslint-enable max-params*/\n\n /**\n * In P2P mode we don't care about any values coming from the JVB and\n * the connection status can be only active or interrupted.\n * @param {boolean} isVideoMuted the user if video muted\n * @param {boolean} isVideoTrackFrozen true if the video track for\n * the remote user is currently frozen. If the current browser does not\n * support video frozen detection then it's always false.\n * @return {ParticipantConnectionStatus}\n * @private\n */\n static _getNewStateForP2PMode(isVideoMuted, isVideoTrackFrozen) {\n if (!browser.supportsVideoMuteOnConnInterrupted()) {\n // There's no way to detect problems in P2P when there's no video\n // track frozen detection...\n return ParticipantConnectionStatus.ACTIVE;\n }\n\n return isVideoMuted || !isVideoTrackFrozen\n ? ParticipantConnectionStatus.ACTIVE\n : ParticipantConnectionStatus.INTERRUPTED;\n }\n\n /**\n * Creates new instance of ParticipantConnectionStatus.\n *\n * @constructor\n * @param {RTC} rtc the RTC service instance\n * @param {JitsiConference} conference parent conference instance\n * @param {Object} options\n * @param {number} [options.p2pRtcMuteTimeout=2500] custom value for\n * {@link ParticipantConnectionStatus.p2pRtcMuteTimeout}.\n * @param {number} [options.rtcMuteTimeout=2000] custom value for\n * {@link ParticipantConnectionStatus.rtcMuteTimeout}.\n * @param {number} [options.outOfLastNTimeout=500] custom value for\n * {@link ParticipantConnectionStatus.outOfLastNTimeout}.\n */\n constructor(rtc, conference, options) {\n this.rtc = rtc;\n this.conference = conference;\n\n /**\n * A map of the \"endpoint ID\"(which corresponds to the resource part\n * of MUC JID(nickname)) to the timeout callback IDs scheduled using\n * window.setTimeout.\n * @type {Object.}\n */\n this.trackTimers = {};\n\n /**\n * This map holds the endpoint connection status received from the JVB\n * (as it might be different than the one stored in JitsiParticipant).\n * Required for getting back in sync when remote video track is removed.\n * @type {Object.}\n */\n this.connStatusFromJvb = { };\n\n /**\n * If video track frozen detection through RTC mute event is supported,\n * we wait some time until video track is considered frozen. But because\n * when the user falls out of last N it is expected for the video to\n * freeze this timeout must be significantly reduced in \"out of last N\"\n * case.\n *\n * Basically this value is used instead of {@link rtcMuteTimeout} when\n * user is not in last N.\n * @type {number}\n */\n this.outOfLastNTimeout\n = typeof options.outOfLastNTimeout === 'number'\n ? options.outOfLastNTimeout : DEFAULT_NOT_IN_LAST_N_TIMEOUT;\n\n /**\n * How long we are going to wait for the corresponding signaling mute event after the RTC video track muted\n * event is fired on the Media stream, before the connection interrupted is fired. The default value is\n * {@link DEFAULT_P2P_RTC_MUTE_TIMEOUT}.\n *\n * @type {number} amount of time in milliseconds.\n */\n this.p2pRtcMuteTimeout = typeof options.p2pRtcMuteTimeout === 'number'\n ? options.p2pRtcMuteTimeout : DEFAULT_P2P_RTC_MUTE_TIMEOUT;\n\n /**\n * How long we're going to wait after the RTC video track muted event\n * for the corresponding signalling mute event, before the connection\n * interrupted is fired. The default value is\n * {@link DEFAULT_RTC_MUTE_TIMEOUT}.\n *\n * @type {number} amount of time in milliseconds\n */\n this.rtcMuteTimeout\n = typeof options.rtcMuteTimeout === 'number'\n ? options.rtcMuteTimeout : DEFAULT_RTC_MUTE_TIMEOUT;\n\n /**\n * This map holds a timestamp indicating when participant's video track\n * was RTC muted (it is assumed that each participant can have only 1\n * video track at a time). The purpose of storing the timestamp is to\n * avoid the transition to disconnected status in case of legitimate\n * video mute operation where the signalling video muted event can\n * arrive shortly after RTC muted event.\n *\n * The key is participant's ID which is the same as endpoint id in\n * the Colibri conference allocated on the JVB.\n *\n * The value is a timestamp measured in milliseconds obtained with\n * Date.now().\n *\n * FIXME merge this logic with NO_DATA_FROM_SOURCE event\n * implemented in JitsiLocalTrack by extending the event to\n * the remote track and allowing to set different timeout for\n * local and remote tracks.\n *\n * @type {Object.}\n */\n this.rtcMutedTimestamp = { };\n logger.info(`RtcMuteTimeout set to: ${this.rtcMuteTimeout}`);\n\n /**\n * This map holds the timestamps indicating when participant's video\n * entered lastN set. Participants entering lastN will have connection\n * status restoring and when we start receiving video will become\n * active, but if video is not received for certain time\n * {@link DEFAULT_RESTORING_TIMEOUT} that participant connection status\n * will become interrupted.\n *\n * @type {Map}\n */\n this.enteredLastNTimestamp = new Map();\n\n /**\n * A map of the \"endpoint ID\"(which corresponds to the resource part\n * of MUC JID(nickname)) to the restoring timeout callback IDs\n * scheduled using window.setTimeout.\n *\n * @type {Map}\n */\n this.restoringTimers = new Map();\n\n /**\n * A map that holds the current connection status (along with all the internal events that happen\n * while in that state).\n *\n * The goal is to send this information to the analytics backend for post-mortem analysis.\n */\n this.connectionStatusMap = new Map();\n }\n\n /**\n * Gets the video frozen timeout for given user.\n * @param {string} id endpoint/participant ID\n * @return {number} how long are we going to wait since RTC video muted\n * even, before a video track is considered frozen.\n * @private\n */\n _getVideoFrozenTimeout(id) {\n return this.rtc.isInLastN(id)\n ? this.rtcMuteTimeout\n : this.conference.isP2PActive() ? this.p2pRtcMuteTimeout : this.outOfLastNTimeout;\n }\n\n /**\n * Initializes ParticipantConnectionStatus and bind required event\n * listeners.\n */\n init() {\n\n this._onEndpointConnStatusChanged\n = this.onEndpointConnStatusChanged.bind(this);\n\n this.rtc.addListener(\n RTCEvents.ENDPOINT_CONN_STATUS_CHANGED,\n this._onEndpointConnStatusChanged);\n\n // Handles P2P status changes\n this._onP2PStatus = this.refreshConnectionStatusForAll.bind(this);\n this.conference.on(JitsiConferenceEvents.P2P_STATUS, this._onP2PStatus);\n\n // Used to send analytics events for the participant that left the call.\n this._onUserLeft = this.onUserLeft.bind(this);\n this.conference.on(JitsiConferenceEvents.USER_LEFT, this._onUserLeft);\n\n // On some browsers MediaStreamTrack trigger \"onmute\"/\"onunmute\"\n // events for video type tracks when they stop receiving data which is\n // often a sign that remote user is having connectivity issues\n if (browser.supportsVideoMuteOnConnInterrupted()) {\n\n this._onTrackRtcMuted = this.onTrackRtcMuted.bind(this);\n this.rtc.addListener(\n RTCEvents.REMOTE_TRACK_MUTE, this._onTrackRtcMuted);\n\n this._onTrackRtcUnmuted = this.onTrackRtcUnmuted.bind(this);\n this.rtc.addListener(\n RTCEvents.REMOTE_TRACK_UNMUTE, this._onTrackRtcUnmuted);\n\n // Track added/removed listeners are used to bind \"mute\"/\"unmute\"\n // event handlers\n this._onRemoteTrackAdded = this.onRemoteTrackAdded.bind(this);\n this.conference.on(\n JitsiConferenceEvents.TRACK_ADDED,\n this._onRemoteTrackAdded);\n\n this._onRemoteTrackRemoved = this.onRemoteTrackRemoved.bind(this);\n this.conference.on(\n JitsiConferenceEvents.TRACK_REMOVED,\n this._onRemoteTrackRemoved);\n\n // Listened which will be bound to JitsiRemoteTrack to listen for\n // signalling mute/unmute events.\n this._onSignallingMuteChanged\n = this.onSignallingMuteChanged.bind(this);\n\n // Used to send an analytics event when the video type changes.\n this._onTrackVideoTypeChanged\n = this.onTrackVideoTypeChanged.bind(this);\n }\n\n this._onLastNChanged = this._onLastNChanged.bind(this);\n this.conference.on(\n JitsiConferenceEvents.LAST_N_ENDPOINTS_CHANGED,\n this._onLastNChanged);\n\n this._onLastNValueChanged\n = this.refreshConnectionStatusForAll.bind(this);\n this.rtc.on(\n RTCEvents.LASTN_VALUE_CHANGED, this._onLastNValueChanged);\n }\n\n /**\n * Removes all event listeners and disposes of all resources held by this\n * instance.\n */\n dispose() {\n\n this.rtc.removeListener(\n RTCEvents.ENDPOINT_CONN_STATUS_CHANGED,\n this._onEndpointConnStatusChanged);\n\n if (browser.supportsVideoMuteOnConnInterrupted()) {\n this.rtc.removeListener(\n RTCEvents.REMOTE_TRACK_MUTE,\n this._onTrackRtcMuted);\n this.rtc.removeListener(\n RTCEvents.REMOTE_TRACK_UNMUTE,\n this._onTrackRtcUnmuted);\n\n this.conference.off(\n JitsiConferenceEvents.TRACK_ADDED,\n this._onRemoteTrackAdded);\n this.conference.off(\n JitsiConferenceEvents.TRACK_REMOVED,\n this._onRemoteTrackRemoved);\n }\n\n this.conference.off(\n JitsiConferenceEvents.LAST_N_ENDPOINTS_CHANGED,\n this._onLastNChanged);\n\n this.rtc.removeListener(\n RTCEvents.LASTN_VALUE_CHANGED, this._onLastNValueChanged);\n\n this.conference.off(\n JitsiConferenceEvents.P2P_STATUS, this._onP2PStatus);\n\n this.conference.off(\n JitsiConferenceEvents.USER_LEFT, this._onUserLeft);\n\n const participantIds = Object.keys(this.trackTimers);\n\n for (const participantId of participantIds) {\n this.clearTimeout(participantId);\n this.clearRtcMutedTimestamp(participantId);\n }\n\n for (const id in this.connectionStatusMap) {\n if (this.connectionStatusMap.hasOwnProperty(id)) {\n this.onUserLeft(id);\n }\n }\n\n // Clear RTC connection status cache\n this.connStatusFromJvb = {};\n }\n\n /**\n * Handles RTCEvents.ENDPOINT_CONN_STATUS_CHANGED triggered when we receive\n * notification over the data channel from the bridge about endpoint's\n * connection status update.\n * @param {string} endpointId - The endpoint ID(MUC nickname/resource JID).\n * @param {boolean} isActive - true if the connection is OK or false otherwise.\n */\n onEndpointConnStatusChanged(endpointId, isActive) {\n\n logger.debug(\n `Detector RTCEvents.ENDPOINT_CONN_STATUS_CHANGED(${Date.now()}): ${\n endpointId}: ${isActive}`);\n\n // Filter out events for the local JID for now\n if (endpointId !== this.conference.myUserId()) {\n // Store the status received over the data channels\n this.connStatusFromJvb[endpointId] = isActive;\n this.figureOutConnectionStatus(endpointId);\n }\n }\n\n /**\n * Changes connection status.\n * @param {JitsiParticipant} participant\n * @param newStatus\n */\n _changeConnectionStatus(participant, newStatus) {\n if (participant.getConnectionStatus() !== newStatus) {\n\n const endpointId = participant.getId();\n\n participant._setConnectionStatus(newStatus);\n\n logger.debug(\n `Emit endpoint conn status(${Date.now()}) ${endpointId}: ${\n newStatus}`);\n\n // Log the event on CallStats\n Statistics.sendLog(\n JSON.stringify({\n id: 'peer.conn.status',\n participant: endpointId,\n status: newStatus\n }));\n\n\n this.conference.eventEmitter.emit(\n JitsiConferenceEvents.PARTICIPANT_CONN_STATUS_CHANGED,\n endpointId, newStatus);\n }\n }\n\n /**\n * Reset the postponed \"connection interrupted\" event which was previously\n * scheduled as a timeout on RTC 'onmute' event.\n *\n * @param {string} participantId - The participant for which the \"connection\n * interrupted\" timeout was scheduled.\n */\n clearTimeout(participantId) {\n if (this.trackTimers[participantId]) {\n window.clearTimeout(this.trackTimers[participantId]);\n this.trackTimers[participantId] = null;\n }\n }\n\n /**\n * Clears the timestamp of the RTC muted event for participant's video track\n * @param {string} participantId the id of the conference participant which\n * is the same as the Colibri endpoint ID of the video channel allocated for\n * the user on the videobridge.\n */\n clearRtcMutedTimestamp(participantId) {\n this.rtcMutedTimestamp[participantId] = null;\n }\n\n /**\n * Bind signalling mute event listeners for video {JitsiRemoteTrack} when\n * a new one is added to the conference.\n *\n * @param {JitsiTrack} remoteTrack - The {JitsiTrack} which is being added to\n * the conference.\n */\n onRemoteTrackAdded(remoteTrack) {\n if (!remoteTrack.isLocal()\n && remoteTrack.getType() === MediaType.VIDEO) {\n\n logger.debug(\n `Detector on remote track added for: ${\n remoteTrack.getParticipantId()}`);\n\n remoteTrack.on(\n JitsiTrackEvents.TRACK_MUTE_CHANGED,\n this._onSignallingMuteChanged);\n remoteTrack.on(\n JitsiTrackEvents.TRACK_VIDEOTYPE_CHANGED,\n videoType => this._onTrackVideoTypeChanged(remoteTrack, videoType));\n }\n }\n\n /**\n * Removes all event listeners bound to the remote video track and clears\n * any related timeouts.\n *\n * @param {JitsiRemoteTrack} remoteTrack - The remote track which is being\n * removed from the conference.\n */\n onRemoteTrackRemoved(remoteTrack) {\n if (!remoteTrack.isLocal()\n && remoteTrack.getType() === MediaType.VIDEO) {\n\n const endpointId = remoteTrack.getParticipantId();\n\n logger.debug(`Detector on remote track removed: ${endpointId}`);\n\n remoteTrack.off(\n JitsiTrackEvents.TRACK_MUTE_CHANGED,\n this._onSignallingMuteChanged);\n\n this.clearTimeout(endpointId);\n this.clearRtcMutedTimestamp(endpointId);\n\n this.figureOutConnectionStatus(endpointId);\n }\n }\n\n /**\n * Checks if given participant's video is considered frozen.\n * @param {JitsiParticipant} participant - The participant.\n * @return {boolean} true if the video has frozen for given\n * participant or false when it's either not considered frozen\n * (yet) or if freeze detection is not supported by the current browser.\n *\n * FIXME merge this logic with NO_DATA_FROM_SOURCE event\n * implemented in JitsiLocalTrack by extending the event to\n * the remote track and allowing to set different timeout for\n * local and remote tracks.\n *\n */\n isVideoTrackFrozen(participant) {\n if (!browser.supportsVideoMuteOnConnInterrupted()) {\n return false;\n }\n\n const id = participant.getId();\n const hasAnyVideoRTCMuted = participant.hasAnyVideoTrackWebRTCMuted();\n const rtcMutedTimestamp = this.rtcMutedTimestamp[id];\n const timeout = this._getVideoFrozenTimeout(id);\n\n return hasAnyVideoRTCMuted\n && typeof rtcMutedTimestamp === 'number'\n && (Date.now() - rtcMutedTimestamp) >= timeout;\n }\n\n /**\n * Goes over every participant and updates connectivity status.\n * Should be called when a parameter which affects all of the participants\n * is changed (P2P for example).\n */\n refreshConnectionStatusForAll() {\n const participants = this.conference.getParticipants();\n\n for (const participant of participants) {\n this.figureOutConnectionStatus(participant.getId());\n }\n }\n\n /**\n * Figures out (and updates) the current connectivity status for\n * the participant identified by the given id.\n *\n * @param {string} id - The participant's id (MUC nickname or Colibri endpoint ID).\n */\n figureOutConnectionStatus(id) {\n const participant = this.conference.getParticipantById(id);\n\n if (!participant) {\n // Probably the participant is no longer in the conference\n // (at the time of writing this code, participant is\n // detached from the conference and TRACK_REMOVED events are\n // fired),\n // so we don't care, but let's print a log message for debugging purposes.\n logger.debug(`figure out conn status - no participant for: ${id}`);\n\n return;\n }\n\n const inP2PMode = this.conference.isP2PActive();\n const isRestoringTimedOut = this._isRestoringTimedout(id);\n const audioOnlyMode = this.conference.getLastN() === 0;\n\n // NOTE Overriding videoMuted to true for audioOnlyMode should disable\n // any detection based on video playback or the last N.\n const isVideoMuted = participant.isVideoMuted() || audioOnlyMode;\n const isVideoTrackFrozen = this.isVideoTrackFrozen(participant);\n const isInLastN = this.rtc.isInLastN(id);\n let isConnActiveByJvb = this.connStatusFromJvb[id];\n\n if (typeof isConnActiveByJvb !== 'boolean') {\n // If no status was received from the JVB it means that it's active\n // (the bridge does not send notification unless there is a problem)\n isConnActiveByJvb = true;\n }\n\n const newState\n = inP2PMode\n ? ParticipantConnectionStatusHandler._getNewStateForP2PMode(\n isVideoMuted,\n isVideoTrackFrozen)\n : ParticipantConnectionStatusHandler._getNewStateForJvbMode(\n isConnActiveByJvb,\n isInLastN,\n isRestoringTimedOut,\n isVideoMuted,\n isVideoTrackFrozen);\n\n // if the new state is not restoring clear timers and timestamps\n // that we use to track the restoring state\n if (newState !== ParticipantConnectionStatus.RESTORING) {\n this._clearRestoringTimer(id);\n }\n\n logger.debug(\n `Figure out conn status for ${id}, is video muted: ${\n isVideoMuted} is active(jvb): ${\n isConnActiveByJvb} video track frozen: ${\n isVideoTrackFrozen} p2p mode: ${\n inP2PMode} is in last N: ${\n isInLastN} currentStatus => newStatus: ${\n participant.getConnectionStatus()} => ${newState}`);\n\n const oldConnectionStatus = this.connectionStatusMap[id] || {};\n\n // Send an analytics event (guard on either the p2p flag or the connection status has changed\n // since the last time this code block run).\n if (!('p2p' in oldConnectionStatus)\n || !('connectionStatus' in oldConnectionStatus)\n || oldConnectionStatus.p2p !== inP2PMode\n || oldConnectionStatus.connectionStatus !== newState) {\n\n const nowMs = Date.now();\n\n this.maybeSendParticipantConnectionStatusEvent(id, nowMs);\n\n this.connectionStatusMap[id] = {\n ...oldConnectionStatus,\n connectionStatus: newState,\n p2p: inP2PMode,\n startedMs: nowMs\n };\n\n // sometimes (always?) we're late to hook the TRACK_VIDEOTYPE_CHANGED event and the\n // video type is not in oldConnectionStatus.\n if (!('videoType' in this.connectionStatusMap[id])) {\n const videoTracks = participant.getTracksByMediaType(MediaType.VIDEO);\n\n if (Array.isArray(videoTracks) && videoTracks.length !== 0) {\n this.connectionStatusMap[id].videoType = videoTracks[0].videoType;\n }\n }\n }\n this._changeConnectionStatus(participant, newState);\n }\n\n /**\n * Computes the duration of the current connection status for the participant with the specified id (i.e. 15 seconds\n * in the INTERRUPTED state) and sends a participant connection status event.\n * @param {string} id - The jid of the participant.\n * @param {Number} nowMs - The current time (in millis).\n * @returns {void}\n */\n maybeSendParticipantConnectionStatusEvent(id, nowMs) {\n const participantConnectionStatus = this.connectionStatusMap[id];\n\n if (participantConnectionStatus\n && 'startedMs' in participantConnectionStatus\n && 'videoType' in participantConnectionStatus\n && 'connectionStatus' in participantConnectionStatus\n && 'p2p' in participantConnectionStatus) {\n participantConnectionStatus.value = nowMs - participantConnectionStatus.startedMs;\n Statistics.sendAnalytics(\n createParticipantConnectionStatusEvent(participantConnectionStatus));\n }\n }\n\n /**\n * On change in Last N set check all leaving and entering participants to\n * change their corresponding statuses.\n *\n * @param {Array} leavingLastN - The array of ids leaving lastN.\n * @param {Array} enteringLastN - The array of ids entering lastN.\n * @private\n */\n _onLastNChanged(leavingLastN = [], enteringLastN = []) {\n const now = Date.now();\n\n logger.debug(`LastN endpoints changed leaving=${leavingLastN}, entering=${enteringLastN} at ${now}`);\n\n // If the browser doesn't fire the mute/onmute events when the remote peer stops/starts sending media,\n // calculate the connection status for all the endpoints since it won't get triggered automatically on\n // the endpoint that has started/stopped receiving media.\n if (!browser.supportsVideoMuteOnConnInterrupted()) {\n this.refreshConnectionStatusForAll();\n }\n\n for (const id of leavingLastN) {\n this.enteredLastNTimestamp.delete(id);\n this._clearRestoringTimer(id);\n browser.supportsVideoMuteOnConnInterrupted() && this.figureOutConnectionStatus(id);\n }\n for (const id of enteringLastN) {\n // store the timestamp this id is entering lastN\n this.enteredLastNTimestamp.set(id, now);\n browser.supportsVideoMuteOnConnInterrupted() && this.figureOutConnectionStatus(id);\n }\n }\n\n /**\n * Clears the restoring timer for participant's video track and the\n * timestamp for entering lastN.\n *\n * @param {string} participantId - The id of the conference participant which\n * is the same as the Colibri endpoint ID of the video channel allocated for\n * the user on the videobridge.\n */\n _clearRestoringTimer(participantId) {\n const rTimer = this.restoringTimers.get(participantId);\n\n if (rTimer) {\n clearTimeout(rTimer);\n this.restoringTimers.delete(participantId);\n }\n }\n\n /**\n * Checks whether a track had stayed enough in restoring state, compares\n * current time and the time the track entered in lastN. If it hasn't\n * timedout and there is no timer added, add new timer in order to give it\n * more time to become active or mark it as interrupted on next check.\n *\n * @param {string} participantId - The id of the conference participant which\n * is the same as the Colibri endpoint ID of the video channel allocated for\n * the user on the videobridge.\n * @returns {boolean} true if the track was in restoring state\n * more than the timeout ({@link DEFAULT_RESTORING_TIMEOUT}.) in order to\n * set its status to interrupted.\n * @private\n */\n _isRestoringTimedout(participantId) {\n const enteredLastNTimestamp\n = this.enteredLastNTimestamp.get(participantId);\n\n if (enteredLastNTimestamp\n && (Date.now() - enteredLastNTimestamp)\n >= DEFAULT_RESTORING_TIMEOUT) {\n return true;\n }\n\n // still haven't reached timeout, if there is no timer scheduled,\n // schedule one so we can track the restoring state and change it after\n // reaching the timeout\n const rTimer = this.restoringTimers.get(participantId);\n\n if (!rTimer) {\n this.restoringTimers.set(participantId, setTimeout(\n () => this.figureOutConnectionStatus(participantId),\n DEFAULT_RESTORING_TIMEOUT));\n }\n\n return false;\n }\n\n /**\n * Sends a last/final participant connection status event for the participant that left the conference.\n * @param {string} id - The id of the participant that left the conference.\n * @returns {void}\n */\n onUserLeft(id) {\n this.maybeSendParticipantConnectionStatusEvent(id, Date.now());\n delete this.connectionStatusMap[id];\n }\n\n /**\n * Handles RTC 'onmute' event for the video track.\n *\n * @param {JitsiRemoteTrack} track - The video track for which 'onmute' event\n * will be processed.\n */\n onTrackRtcMuted(track) {\n const participantId = track.getParticipantId();\n const participant = this.conference.getParticipantById(participantId);\n\n logger.debug(`Detector track RTC muted: ${participantId}`, Date.now());\n if (!participant) {\n logger.error(`No participant for id: ${participantId}`);\n\n return;\n }\n this.rtcMutedTimestamp[participantId] = Date.now();\n if (!participant.isVideoMuted()) {\n // If the user is not muted according to the signalling we'll give\n // it some time, before the connection interrupted event is\n // triggered.\n this.clearTimeout(participantId);\n\n // The timeout is reduced when user is not in the last N\n const timeout = this._getVideoFrozenTimeout(participantId);\n\n this.trackTimers[participantId] = window.setTimeout(() => {\n logger.debug(\n `Set RTC mute timeout for: ${participantId}\\\n of ${timeout} ms`);\n this.clearTimeout(participantId);\n this.figureOutConnectionStatus(participantId);\n }, timeout);\n }\n }\n\n /**\n * Handles RTC 'onunmute' event for the video track.\n *\n * @param {JitsiRemoteTrack} track - The video track for which 'onunmute'\n * event will be processed.\n */\n onTrackRtcUnmuted(track) {\n const participantId = track.getParticipantId();\n\n logger.debug(\n `Detector track RTC unmuted: ${participantId}`, Date.now());\n\n this.clearTimeout(participantId);\n this.clearRtcMutedTimestamp(participantId);\n\n this.figureOutConnectionStatus(participantId);\n }\n\n /**\n * Here the signalling \"mute\"/\"unmute\" events are processed.\n *\n * @param {JitsiRemoteTrack} track - The remote video track for which\n * the signalling mute/unmute event will be processed.\n */\n onSignallingMuteChanged(track) {\n const participantId = track.getParticipantId();\n\n logger.debug(\n `Detector on track signalling mute changed: ${participantId}`,\n track.isMuted());\n\n this.figureOutConnectionStatus(participantId);\n }\n\n /**\n * Sends a participant connection status event as a result of the video type\n * changing.\n * @param {JitsiRemoteTrack} track - The track.\n * @param {VideoType} type - The video type.\n * @returns {void}\n */\n onTrackVideoTypeChanged(track, type) {\n const id = track.getParticipantId();\n const nowMs = Date.now();\n\n this.maybeSendParticipantConnectionStatusEvent(id, nowMs);\n\n this.connectionStatusMap[id] = {\n ...this.connectionStatusMap[id] || {},\n videoType: type,\n startedMs: nowMs\n };\n }\n}\n","\nimport { Strophe } from 'strophe.js';\n\n\nimport * as JitsiConferenceEvents from './JitsiConferenceEvents';\nimport { ParticipantConnectionStatus }\n from './modules/connectivity/ParticipantConnectionStatus';\nimport * as MediaType from './service/RTC/MediaType';\n\n/**\n * Represents a participant in (i.e. a member of) a conference.\n */\nexport default class JitsiParticipant {\n\n /* eslint-disable max-params */\n\n /**\n * Initializes a new JitsiParticipant instance.\n *\n * @constructor\n * @param jid the conference XMPP jid\n * @param conference\n * @param displayName\n * @param {Boolean} hidden - True if the new JitsiParticipant instance is to\n * represent a hidden participant; otherwise, false.\n * @param {string} statsID - optional participant statsID\n * @param {string} status - the initial status if any.\n * @param {object} identity - the xmpp identity\n * @param {boolean?} isReplacing - whether this is a participant replacing another into the meeting.\n * @param {boolean?} isReplaced - whether this is a participant to be kicked and replaced into the meeting.\n */\n constructor(jid, conference, displayName, hidden, statsID, status, identity, isReplacing, isReplaced) {\n this._jid = jid;\n this._id = Strophe.getResourceFromJid(jid);\n this._conference = conference;\n this._displayName = displayName;\n this._supportsDTMF = false;\n this._tracks = [];\n this._role = 'none';\n this._status = status;\n this._hidden = hidden;\n this._statsID = statsID;\n this._connectionStatus = ParticipantConnectionStatus.ACTIVE;\n this._properties = {};\n this._identity = identity;\n this._isReplacing = isReplacing;\n this._isReplaced = isReplaced;\n this._features = new Set();\n }\n\n /* eslint-enable max-params */\n\n /**\n * @returns {JitsiConference} The conference that this participant belongs\n * to.\n */\n getConference() {\n return this._conference;\n }\n\n /**\n * Gets the value of a property of this participant.\n */\n getProperty(name) {\n return this._properties[name];\n }\n\n /**\n * Checks whether this JitsiParticipant has any video tracks which\n * are muted according to their underlying WebRTC MediaStreamTrack\n * muted status.\n * @return {boolean} true if this participant contains any\n * video JitsiTracks which are muted as defined in\n * {@link JitsiTrack.isWebRTCTrackMuted}.\n */\n hasAnyVideoTrackWebRTCMuted() {\n return (\n this.getTracks().some(\n jitsiTrack =>\n jitsiTrack.getType() === MediaType.VIDEO\n && jitsiTrack.isWebRTCTrackMuted()));\n }\n\n /**\n * Updates participant's connection status.\n * @param {string} state the current participant connection state.\n * {@link ParticipantConnectionStatus}.\n * @private\n */\n _setConnectionStatus(status) {\n this._connectionStatus = status;\n }\n\n /**\n * Return participant's connectivity status.\n *\n * @returns {string} the connection status\n * ParticipantConnectionStatus of the user.\n * {@link ParticipantConnectionStatus}.\n */\n getConnectionStatus() {\n return this._connectionStatus;\n }\n\n /**\n * Sets the value of a property of this participant, and fires an event if\n * the value has changed.\n * @name the name of the property.\n * @value the value to set.\n */\n setProperty(name, value) {\n const oldValue = this._properties[name];\n\n if (value !== oldValue) {\n this._properties[name] = value;\n this._conference.eventEmitter.emit(\n JitsiConferenceEvents.PARTICIPANT_PROPERTY_CHANGED,\n this,\n name,\n oldValue,\n value);\n }\n }\n\n /**\n * @returns {Array.} The list of media tracks for this\n * participant.\n */\n getTracks() {\n return this._tracks.slice();\n }\n\n /**\n * @param {MediaType} mediaType\n * @returns {Array.} an array of media tracks for this\n * participant, for given media type.\n */\n getTracksByMediaType(mediaType) {\n return this.getTracks().filter(track => track.getType() === mediaType);\n }\n\n /**\n * @returns {String} The ID of this participant.\n */\n getId() {\n return this._id;\n }\n\n /**\n * @returns {String} The JID of this participant.\n */\n getJid() {\n return this._jid;\n }\n\n /**\n * @returns {String} The human-readable display name of this participant.\n */\n getDisplayName() {\n return this._displayName;\n }\n\n /**\n * @returns {String} The stats ID of this participant.\n */\n getStatsID() {\n return this._statsID;\n }\n\n /**\n * @returns {String} The status of the participant.\n */\n getStatus() {\n return this._status;\n }\n\n /**\n * @returns {Boolean} Whether this participant is a moderator or not.\n */\n isModerator() {\n return this._role === 'moderator';\n }\n\n /**\n * @returns {Boolean} Whether this participant is a hidden participant. Some\n * special system participants may want to join hidden (like for example the\n * recorder).\n */\n isHidden() {\n return this._hidden;\n }\n\n /**\n * @returns {Boolean} Whether this participant is a hidden participant. Some\n * special system participants may want to join hidden (like for example the\n * recorder).\n */\n isHiddenFromRecorder() {\n return Boolean(this._identity?.user?.['hidden-from-recorder']);\n }\n\n /**\n * @returns {Boolean} Whether this participant replaces another participant\n * from the meeting.\n */\n isReplacing() {\n return this._isReplacing;\n }\n\n /**\n * @returns {Boolean} Wheter this participants will be replaced by another\n * participant in the meeting.\n */\n isReplaced() {\n return this._isReplaced;\n }\n\n /**\n * @returns {Boolean} Whether this participant has muted their audio.\n */\n isAudioMuted() {\n return this._isMediaTypeMuted(MediaType.AUDIO);\n }\n\n /**\n * Determines whether all JitsiTracks which are of a specific MediaType and\n * which belong to this JitsiParticipant are muted.\n *\n * @param {MediaType} mediaType - The MediaType of the JitsiTracks to be\n * checked.\n * @private\n * @returns {Boolean} True if all JitsiTracks which are of the specified\n * mediaType and which belong to this JitsiParticipant are muted; otherwise,\n * false.\n */\n _isMediaTypeMuted(mediaType) {\n return this.getTracks().reduce(\n (muted, track) =>\n muted && (track.getType() !== mediaType || track.isMuted()),\n true);\n }\n\n /**\n * @returns {Boolean} Whether this participant has muted their video.\n */\n isVideoMuted() {\n return this._isMediaTypeMuted(MediaType.VIDEO);\n }\n\n /**\n * @returns {String} The role of this participant.\n */\n getRole() {\n return this._role;\n }\n\n /**\n * Sets a new participant role.\n * @param {String} newRole - the new role.\n */\n setRole(newRole) {\n this._role = newRole;\n }\n\n /**\n * Sets whether participant is replacing another based on jwt.\n * @param {String} newIsReplacing - whether is replacing.\n */\n setIsReplacing(newIsReplacing) {\n this._isReplacing = newIsReplacing;\n }\n\n /**\n * Sets whether participant is being replaced by another based on jwt.\n * @param {boolean} newIsReplaced - whether is being replaced.\n */\n setIsReplaced(newIsReplaced) {\n this._isReplaced = newIsReplaced;\n }\n\n /**\n *\n */\n supportsDTMF() {\n return this._supportsDTMF;\n }\n\n /**\n * Returns a set with the features for the participant.\n * @returns {Promise, Error>}\n */\n getFeatures() {\n return Promise.resolve(this._features);\n }\n\n /**\n * Checks current set features.\n * @param {String} feature - the feature to check.\n * @return {boolean} true if this participant contains the\n * feature.\n */\n hasFeature(feature) {\n return this._features.has(feature);\n }\n\n /**\n * Set new features.\n * @param {Set|undefined} newFeatures - Sets new features.\n */\n setFeatures(newFeatures) {\n this._features = newFeatures || new Set();\n }\n\n /**\n * Returns the bot type for the participant.\n *\n * @returns {string|undefined} - The bot type of the participant.\n */\n getBotType() {\n return this._botType;\n }\n\n /**\n * Sets the bot type for the participant.\n * @param {String} newBotType - The new bot type to set.\n */\n setBotType(newBotType) {\n this._botType = newBotType;\n }\n}\n","/**\n * The events for the connection.\n */\n\nexport enum JitsiConnectionEvents {\n /**\n * Indicates that the connection has been disconnected. The event provides\n * the following parameters to its listeners:\n *\n * @param msg {string} a message associated with the disconnect such as the\n * last (known) error message\n */\n CONNECTION_DISCONNECTED = 'connection.connectionDisconnected',\n\n /**\n * Indicates that the connection has been established. The event provides\n * the following parameters to its listeners:\n *\n * @param id {string} the ID of the local endpoint/participant/peer (within\n * the context of the established connection)\n */\n CONNECTION_ESTABLISHED = 'connection.connectionEstablished',\n\n /**\n * Indicates that the connection has been failed for some reason. The event\n * provides the following parameters to its listeners:\n *\n * @param errType {JitsiConnectionErrors} the type of error associated with\n * the failure\n * @param errReason {string} the error (message) associated with the failure\n * @param credentials {object} the credentials used to connect (if any)\n * @param errReasonDetails {object} an optional object with details about\n * the error, like shard moving, suspending. Used for analytics purposes.\n */\n CONNECTION_FAILED = 'connection.connectionFailed',\n\n /**\n * Indicates that the performed action cannot be executed because the\n * connection is not in the correct state(connected, disconnected, etc.)\n */\n WRONG_STATE = 'connection.wrongState',\n\n /**\n * Indicates that the display name is required over this connection and need to be supplied when\n * joining the room.\n * There are cases like lobby room where display name is required.\n */\n DISPLAY_NAME_REQUIRED = 'connection.display_name_required'\n};\n\n// exported for backward compatibility\nexport const CONNECTION_DISCONNECTED = JitsiConnectionEvents.CONNECTION_DISCONNECTED;\nexport const CONNECTION_ESTABLISHED = JitsiConnectionEvents.CONNECTION_ESTABLISHED;\nexport const CONNECTION_FAILED = JitsiConnectionEvents.CONNECTION_FAILED;\nexport const WRONG_STATE = JitsiConnectionEvents.WRONG_STATE;\nexport const DISPLAY_NAME_REQUIRED = JitsiConnectionEvents.DISPLAY_NAME_REQUIRED;\n","/**\n * The errors for the connection.\n */\n\nexport enum JitsiConnectionErrors {\n /**\n * Indicates that the connection was dropped with an error which was most likely\n * caused by some networking issues. The dropped term in this context means that\n * the connection was closed unexpectedly (not on user's request).\n *\n * One example is 'item-not-found' error thrown by Prosody when the BOSH session\n * times out after 60 seconds of inactivity. On the other hand 'item-not-found'\n * could also happen when BOSH request is sent to the server with the session-id\n * that is not know to the server. But this should not happen in lib-jitsi-meet\n * case as long as the service is configured correctly (there is no bug).\n */\n CONNECTION_DROPPED_ERROR = 'connection.droppedError',\n\n /**\n * Not specified errors.\n */\n OTHER_ERROR = 'connection.otherError',\n\n /**\n * Indicates that a password is required in order to join the conference.\n */\n PASSWORD_REQUIRED = 'connection.passwordRequired',\n\n /**\n * Indicates that the connection was dropped, because of too many 5xx HTTP\n * errors on BOSH requests.\n */\n SERVER_ERROR = 'connection.serverError'\n};\n\n// exported for backward compatibility\nexport const CONNECTION_DROPPED_ERROR = JitsiConnectionErrors.CONNECTION_DROPPED_ERROR;\nexport const OTHER_ERROR = JitsiConnectionErrors.OTHER_ERROR;\nexport const PASSWORD_REQUIRED = JitsiConnectionErrors.PASSWORD_REQUIRED;\nexport const SERVER_ERROR = JitsiConnectionErrors.SERVER_ERROR;\n","\n/**\n * Promise-like object which can be passed around for resolving it later. It\n * implements the \"thenable\" interface, so it can be used wherever a Promise\n * could be used.\n *\n * In addition a \"reject on timeout\" functionality is provided.\n */\nexport default class Deferred {\n /**\n * Instantiates a Deferred object.\n */\n constructor() {\n this.promise = new Promise((resolve, reject) => {\n this.resolve = (...args) => {\n this.clearRejectTimeout();\n resolve(...args);\n };\n this.reject = (...args) => {\n this.clearRejectTimeout();\n reject(...args);\n };\n });\n this.then = this.promise.then.bind(this.promise);\n this.catch = this.promise.catch.bind(this.promise);\n }\n\n /**\n * Clears the reject timeout.\n */\n clearRejectTimeout() {\n clearTimeout(this._timeout);\n }\n\n /**\n * Rejects the promise after the given timeout.\n */\n setRejectTimeout(ms) {\n this._timeout = setTimeout(() => {\n this.reject(new Error('timeout'));\n }, ms);\n }\n}\n","import EventEmitter from 'events';\n\n/**\n * The class implements basic event operations - add/remove listener.\n * NOTE: The purpose of the class is to be extended in order to add\n * this functionality to other classes.\n */\nexport default class Listenable {\n /**\n * Creates new instance.\n * @param {EventEmitter} eventEmitter\n * @constructor\n */\n constructor(eventEmitter = new EventEmitter()) {\n this.eventEmitter = eventEmitter;\n\n // aliases for addListener/removeListener\n this.addEventListener = this.on = this.addListener;\n this.removeEventListener = this.off = this.removeListener;\n }\n\n /**\n * Adds new listener.\n * @param {String} eventName the name of the event\n * @param {Function} listener the listener.\n * @returns {Function} - The unsubscribe function.\n */\n addListener(eventName, listener) {\n this.eventEmitter.addListener(eventName, listener);\n\n return () => this.removeEventListener(eventName, listener);\n }\n\n /**\n * Removes listener.\n * @param {String} eventName the name of the event that triggers the\n * listener\n * @param {Function} listener the listener.\n */\n removeListener(eventName, listener) {\n this.eventEmitter.removeListener(eventName, listener);\n }\n}\n","/* global RTCRtpScriptTransform */\n\nimport { getLogger } from '@jitsi/logger';\n\nconst logger = getLogger(__filename);\n\n// Flag to set on senders / receivers to avoid setting up the encryption transform\n// more than once.\nconst kJitsiE2EE = Symbol('kJitsiE2EE');\n\n/**\n * Context encapsulating the cryptography bits required for E2EE.\n * This uses the WebRTC Insertable Streams API which is explained in\n * https://github.com/alvestrand/webrtc-media-streams/blob/master/explainer.md\n * that provides access to the encoded frames and allows them to be transformed.\n *\n * The encoded frame format is explained below in the _encodeFunction method.\n * High level design goals were:\n * - do not require changes to existing SFUs and retain (VP8) metadata.\n * - allow the SFU to rewrite SSRCs, timestamp, pictureId.\n * - allow for the key to be rotated frequently.\n */\nexport default class E2EEcontext {\n /**\n * Build a new E2EE context instance, which will be used in a given conference.\n * @param {boolean} [options.sharedKey] - whether there is a uniques key shared amoung all participants.\n */\n constructor({ sharedKey } = {}) {\n // Determine the URL for the worker script. Relative URLs are relative to\n // the entry point, not the script that launches the worker.\n let baseUrl = '';\n const ljm = document.querySelector('script[src*=\"lib-jitsi-meet\"]');\n\n if (ljm) {\n const idx = ljm.src.lastIndexOf('/');\n\n baseUrl = `${ljm.src.substring(0, idx)}/`;\n }\n\n let workerUrl = `${baseUrl}lib-jitsi-meet.e2ee-worker.js`;\n\n // If there is no baseUrl then we create the worker in a normal way\n // as you cant load scripts inside blobs from relative paths.\n // See: https://www.html5rocks.com/en/tutorials/workers/basics/#toc-inlineworkers-loadingscripts\n if (baseUrl && baseUrl !== '/') {\n // Initialize the E2EE worker. In order to avoid CORS issues, start the worker and have it\n // synchronously load the JS.\n const workerBlob\n = new Blob([ `importScripts(\"${workerUrl}\");` ], { type: 'application/javascript' });\n\n workerUrl = window.URL.createObjectURL(workerBlob);\n }\n\n this._worker = new Worker(workerUrl, { name: 'E2EE Worker' });\n\n this._worker.onerror = e => logger.error(e);\n\n this._worker.postMessage({\n operation: 'initialize',\n sharedKey\n });\n }\n\n /**\n * Cleans up all state associated with the given participant. This is needed when a\n * participant leaves the current conference.\n *\n * @param {string} participantId - The participant that just left.\n */\n cleanup(participantId) {\n this._worker.postMessage({\n operation: 'cleanup',\n participantId\n });\n }\n\n /**\n * Cleans up all state associated with all participants in the conference. This is needed when disabling e2ee.\n *\n */\n cleanupAll() {\n this._worker.postMessage({\n operation: 'cleanupAll'\n });\n }\n\n /**\n * Handles the given {@code RTCRtpReceiver} by creating a {@code TransformStream} which will inject\n * a frame decoder.\n *\n * @param {RTCRtpReceiver} receiver - The receiver which will get the decoding function injected.\n * @param {string} kind - The kind of track this receiver belongs to.\n * @param {string} participantId - The participant id that this receiver belongs to.\n */\n handleReceiver(receiver, kind, participantId) {\n if (receiver[kJitsiE2EE]) {\n return;\n }\n receiver[kJitsiE2EE] = true;\n\n if (window.RTCRtpScriptTransform) {\n const options = {\n operation: 'decode',\n participantId\n };\n\n receiver.transform = new RTCRtpScriptTransform(this._worker, options);\n } else {\n const receiverStreams = receiver.createEncodedStreams();\n\n this._worker.postMessage({\n operation: 'decode',\n readableStream: receiverStreams.readable,\n writableStream: receiverStreams.writable,\n participantId\n }, [ receiverStreams.readable, receiverStreams.writable ]);\n }\n }\n\n /**\n * Handles the given {@code RTCRtpSender} by creating a {@code TransformStream} which will inject\n * a frame encoder.\n *\n * @param {RTCRtpSender} sender - The sender which will get the encoding function injected.\n * @param {string} kind - The kind of track this sender belongs to.\n * @param {string} participantId - The participant id that this sender belongs to.\n */\n handleSender(sender, kind, participantId) {\n if (sender[kJitsiE2EE]) {\n return;\n }\n sender[kJitsiE2EE] = true;\n\n if (window.RTCRtpScriptTransform) {\n const options = {\n operation: 'encode',\n participantId\n };\n\n sender.transform = new RTCRtpScriptTransform(this._worker, options);\n } else {\n const senderStreams = sender.createEncodedStreams();\n\n this._worker.postMessage({\n operation: 'encode',\n readableStream: senderStreams.readable,\n writableStream: senderStreams.writable,\n participantId\n }, [ senderStreams.readable, senderStreams.writable ]);\n }\n }\n\n /**\n * Set the E2EE key for the specified participant.\n *\n * @param {string} participantId - the ID of the participant who's key we are setting.\n * @param {Uint8Array | boolean} key - they key for the given participant.\n * @param {Number} keyIndex - the key index.\n */\n setKey(participantId, key, keyIndex) {\n this._worker.postMessage({\n operation: 'setKey',\n key,\n keyIndex,\n participantId\n });\n }\n}\n","import { getLogger } from '@jitsi/logger';\n\nimport * as JitsiConferenceEvents from '../../JitsiConferenceEvents';\nimport RTCEvents from '../../service/RTC/RTCEvents';\nimport browser from '../browser';\nimport Deferred from '../util/Deferred';\nimport Listenable from '../util/Listenable';\n\nimport E2EEContext from './E2EEContext';\n\nconst logger = getLogger(__filename);\n\n/**\n * Abstract class that integrates {@link E2EEContext} with a key management system.\n */\nexport class KeyHandler extends Listenable {\n /**\n * Build a new KeyHandler instance, which will be used in a given conference.\n * @param {JitsiConference} conference - the current conference.\n * @param {object} options - the options passed to {E2EEContext}, see implemention.\n */\n constructor(conference, options = {}) {\n super();\n\n this.conference = conference;\n this.e2eeCtx = new E2EEContext(options);\n\n this.enabled = false;\n this._enabling = undefined;\n\n // Conference media events in order to attach the encryptor / decryptor.\n // FIXME add events to TraceablePeerConnection which will allow to see when there's new receiver or sender\n // added instead of shenanigans around conference track events and track muted.\n //\n\n this.conference.on(\n JitsiConferenceEvents._MEDIA_SESSION_STARTED,\n this._onMediaSessionStarted.bind(this));\n this.conference.on(\n JitsiConferenceEvents.TRACK_ADDED,\n track => track.isLocal() && this._onLocalTrackAdded(track));\n this.conference.rtc.on(\n RTCEvents.REMOTE_TRACK_ADDED,\n (track, tpc) => this._setupReceiverE2EEForTrack(tpc, track));\n this.conference.on(\n JitsiConferenceEvents.TRACK_MUTE_CHANGED,\n this._trackMuteChanged.bind(this));\n }\n\n /**\n * Indicates whether E2EE is currently enabled or not.\n *\n * @returns {boolean}\n */\n isEnabled() {\n return this.enabled;\n }\n\n /**\n * Enables / disables End-To-End encryption.\n *\n * @param {boolean} enabled - whether E2EE should be enabled or not.\n * @returns {void}\n */\n async setEnabled(enabled) {\n if (enabled === this.enabled) {\n return;\n }\n\n this._enabling && await this._enabling;\n\n this._enabling = new Deferred();\n\n this.enabled = enabled;\n\n if (!enabled) {\n this.e2eeCtx.cleanupAll();\n }\n\n this._setEnabled && await this._setEnabled(enabled);\n\n this.conference.setLocalParticipantProperty('e2ee.enabled', enabled);\n\n this.conference._restartMediaSessions();\n\n this._enabling.resolve();\n }\n\n /**\n * Sets the key for End-to-End encryption.\n *\n * @returns {void}\n */\n setEncryptionKey() {\n throw new Error('Not implemented by subclass');\n }\n\n /**\n * Setup E2EE on the new track that has been added to the conference, apply it on all the open peerconnections.\n * @param {JitsiLocalTrack} track - the new track that's being added to the conference.\n * @private\n */\n _onLocalTrackAdded(track) {\n for (const session of this.conference.getMediaSessions()) {\n this._setupSenderE2EEForTrack(session, track);\n }\n }\n\n /**\n * Setups E2E encryption for the new session.\n * @param {JingleSessionPC} session - the new media session.\n * @private\n */\n _onMediaSessionStarted(session) {\n const localTracks = this.conference.getLocalTracks();\n\n for (const track of localTracks) {\n this._setupSenderE2EEForTrack(session, track);\n }\n }\n\n /**\n * Setup E2EE for the receiving side.\n *\n * @private\n */\n _setupReceiverE2EEForTrack(tpc, track) {\n if (!this.enabled) {\n return;\n }\n\n const receiver = tpc.findReceiverForTrack(track.track);\n\n if (receiver) {\n this.e2eeCtx.handleReceiver(receiver, track.getType(), track.getParticipantId());\n } else {\n logger.warn(`Could not handle E2EE for ${track}: receiver not found in: ${tpc}`);\n }\n }\n\n /**\n * Setup E2EE for the sending side.\n *\n * @param {JingleSessionPC} session - the session which sends the media produced by the track.\n * @param {JitsiLocalTrack} track - the local track for which e2e encoder will be configured.\n * @private\n */\n _setupSenderE2EEForTrack(session, track) {\n if (!this.enabled) {\n return;\n }\n\n const pc = session.peerconnection;\n const sender = pc && pc.findSenderForTrack(track.track);\n\n if (sender) {\n this.e2eeCtx.handleSender(sender, track.getType(), track.getParticipantId());\n } else {\n logger.warn(`Could not handle E2EE for ${track}: sender not found in ${pc}`);\n }\n }\n\n /**\n * Setup E2EE on the sender that is created for the unmuted local video track.\n * @param {JitsiLocalTrack} track - the track for which muted status has changed.\n * @private\n */\n _trackMuteChanged(track) {\n if (browser.doesVideoMuteByStreamRemove() && track.isLocal() && track.isVideoTrack() && !track.isMuted()) {\n for (const session of this.conference.getMediaSessions()) {\n this._setupSenderE2EEForTrack(session, track);\n }\n }\n }\n}\n","import { KeyHandler } from './KeyHandler';\n\n/**\n * This module integrates {@link E2EEContext} with {external} in order to set the keys for encryption.\n */\nexport class ExternallyManagedKeyHandler extends KeyHandler {\n /**\n * Build a new ExternallyManagedKeyHandler instance, which will be used in a given conference.\n * @param conference - the current conference.\n */\n constructor(conference) {\n super(conference, { sharedKey: true });\n }\n\n /**\n * Sets the key and index for End-to-End encryption.\n *\n * @param {CryptoKey} [keyInfo.encryptionKey] - encryption key.\n * @param {Number} [keyInfo.index] - the index of the encryption key.\n * @returns {void}\n */\n setKey(keyInfo) {\n this.e2eeCtx.setKey(undefined, { encryptionKey: keyInfo.encryptionKey }, keyInfo.index);\n }\n}\n","import uuid from './dist/index.js';\nexport const v1 = uuid.v1;\nexport const v3 = uuid.v3;\nexport const v4 = uuid.v4;\nexport const v5 = uuid.v5;\n","/* global Olm */\n\nimport { getLogger } from '@jitsi/logger';\nimport base64js from 'base64-js';\nimport isEqual from 'lodash.isequal';\nimport { v4 as uuidv4 } from 'uuid';\n\nimport * as JitsiConferenceEvents from '../../JitsiConferenceEvents';\nimport Deferred from '../util/Deferred';\nimport Listenable from '../util/Listenable';\nimport { FEATURE_E2EE, JITSI_MEET_MUC_TYPE } from '../xmpp/xmpp';\n\nconst logger = getLogger(__filename);\n\nconst REQ_TIMEOUT = 5 * 1000;\nconst OLM_MESSAGE_TYPE = 'olm';\nconst OLM_MESSAGE_TYPES = {\n ERROR: 'error',\n KEY_INFO: 'key-info',\n KEY_INFO_ACK: 'key-info-ack',\n SESSION_ACK: 'session-ack',\n SESSION_INIT: 'session-init'\n};\n\nconst kOlmData = Symbol('OlmData');\n\nconst OlmAdapterEvents = {\n OLM_ID_KEY_READY: 'olm.id_key_ready',\n PARTICIPANT_E2EE_CHANNEL_READY: 'olm.participant_e2ee_channel_ready',\n PARTICIPANT_KEY_UPDATED: 'olm.partitipant_key_updated'\n};\n\n/**\n * This class implements an End-to-End Encrypted communication channel between every two peers\n * in the conference. This channel uses libolm to achieve E2EE.\n *\n * The created channel is then used to exchange the secret key that each participant will use\n * to encrypt the actual media (see {@link E2EEContext}).\n *\n * A simple JSON message based protocol is implemented, which follows a request - response model:\n * - session-init: Initiates an olm session establishment procedure. This message will be sent\n * by the participant who just joined, to everyone else.\n * - session-ack: Completes the olm session etablishment. This messsage may contain ancilliary\n * encrypted data, more specifically the sender's current key.\n * - key-info: Includes the sender's most up to date key information.\n * - key-info-ack: Acknowledges the reception of a key-info request. In addition, it may contain\n * the sender's key information, if available.\n * - error: Indicates a request processing error has occurred.\n *\n * These requessts and responses are transport independent. Currently they are sent using XMPP\n * MUC private messages.\n */\nexport class OlmAdapter extends Listenable {\n /**\n * Creates an adapter instance for the given conference.\n */\n constructor(conference) {\n super();\n\n this._conf = conference;\n this._init = new Deferred();\n this._key = undefined;\n this._keyIndex = -1;\n this._reqs = new Map();\n this._sessionInitialization = undefined;\n\n if (OlmAdapter.isSupported()) {\n this._bootstrapOlm();\n\n this._conf.on(JitsiConferenceEvents.ENDPOINT_MESSAGE_RECEIVED, this._onEndpointMessageReceived.bind(this));\n this._conf.on(JitsiConferenceEvents.CONFERENCE_LEFT, this._onConferenceLeft.bind(this));\n this._conf.on(JitsiConferenceEvents.USER_LEFT, this._onParticipantLeft.bind(this));\n this._conf.on(JitsiConferenceEvents.PARTICIPANT_PROPERTY_CHANGED,\n this._onParticipantPropertyChanged.bind(this));\n } else {\n this._init.reject(new Error('Olm not supported'));\n }\n }\n\n /**\n * Starts new olm sessions with every other participant that has the participantId \"smaller\" the localParticipantId.\n */\n async initSessions() {\n if (this._sessionInitialization) {\n throw new Error('OlmAdapter initSessions called multiple times');\n } else {\n this._sessionInitialization = new Deferred();\n\n await this._init;\n\n const promises = [];\n const localParticipantId = this._conf.myUserId();\n\n for (const participant of this._conf.getParticipants()) {\n if (participant.hasFeature(FEATURE_E2EE) && localParticipantId < participant.getId()) {\n promises.push(this._sendSessionInit(participant));\n }\n }\n\n await Promise.allSettled(promises);\n\n // TODO: retry failed ones.\n\n this._sessionInitialization.resolve();\n this._sessionInitialization = undefined;\n }\n }\n\n /**\n * Indicates if olm is supported on the current platform.\n *\n * @returns {boolean}\n */\n static isSupported() {\n return typeof window.Olm !== 'undefined';\n }\n\n /**\n * Updates the current participant key and distributes it to all participants in the conference\n * by sending a key-info message.\n *\n * @param {Uint8Array|boolean} key - The new key.\n * @retrns {Promise}\n */\n async updateKey(key) {\n // Store it locally for new sessions.\n this._key = key;\n this._keyIndex++;\n\n // Broadcast it.\n const promises = [];\n\n for (const participant of this._conf.getParticipants()) {\n const pId = participant.getId();\n const olmData = this._getParticipantOlmData(participant);\n\n // TODO: skip those who don't support E2EE.\n if (!olmData.session) {\n logger.warn(`Tried to send key to participant ${pId} but we have no session`);\n\n // eslint-disable-next-line no-continue\n continue;\n }\n\n const uuid = uuidv4();\n const data = {\n [JITSI_MEET_MUC_TYPE]: OLM_MESSAGE_TYPE,\n olm: {\n type: OLM_MESSAGE_TYPES.KEY_INFO,\n data: {\n ciphertext: this._encryptKeyInfo(olmData.session),\n uuid\n }\n }\n };\n const d = new Deferred();\n\n d.setRejectTimeout(REQ_TIMEOUT);\n d.catch(() => {\n this._reqs.delete(uuid);\n });\n this._reqs.set(uuid, d);\n promises.push(d);\n\n this._sendMessage(data, pId);\n }\n\n await Promise.allSettled(promises);\n\n // TODO: retry failed ones?\n\n return this._keyIndex;\n }\n\n /**\n * Updates the current participant key.\n * @param {Uint8Array|boolean} key - The new key.\n * @returns {number}\n */\n updateCurrentKey(key) {\n this._key = key;\n\n return this._keyIndex;\n }\n\n /**\n * Frees the olmData session for the given participant.\n *\n */\n clearParticipantSession(participant) {\n const olmData = this._getParticipantOlmData(participant);\n\n if (olmData.session) {\n olmData.session.free();\n olmData.session = undefined;\n }\n }\n\n\n /**\n * Frees the olmData sessions for all participants.\n *\n */\n clearAllParticipantsSessions() {\n for (const participant of this._conf.getParticipants()) {\n this.clearParticipantSession(participant);\n }\n }\n\n /**\n * Internal helper to bootstrap the olm library.\n *\n * @returns {Promise}\n * @private\n */\n async _bootstrapOlm() {\n logger.debug('Initializing Olm...');\n\n try {\n await Olm.init();\n\n this._olmAccount = new Olm.Account();\n this._olmAccount.create();\n\n const idKeys = JSON.parse(this._olmAccount.identity_keys());\n\n this._idKey = idKeys.curve25519;\n\n logger.debug(`Olm ${Olm.get_library_version().join('.')} initialized`);\n this._init.resolve();\n this._onIdKeyReady(this._idKey);\n } catch (e) {\n logger.error('Failed to initialize Olm', e);\n this._init.reject(e);\n }\n\n }\n\n /**\n * Publishes our own Olmn id key in presence.\n * @private\n */\n _onIdKeyReady(idKey) {\n logger.debug(`Olm id key ready: ${idKey}`);\n\n // Publish it in presence.\n this._conf.setLocalParticipantProperty('e2ee.idKey', idKey);\n }\n\n /**\n * Event posted when the E2EE signalling channel has been established with the given participant.\n * @private\n */\n _onParticipantE2EEChannelReady(id) {\n logger.debug(`E2EE channel with participant ${id} is ready`);\n }\n\n /**\n * Internal helper for encrypting the current key information for a given participant.\n *\n * @param {Olm.Session} session - Participant's session.\n * @returns {string} - The encrypted text with the key information.\n * @private\n */\n _encryptKeyInfo(session) {\n const keyInfo = {};\n\n if (this._key !== undefined) {\n keyInfo.key = this._key ? base64js.fromByteArray(this._key) : false;\n keyInfo.keyIndex = this._keyIndex;\n }\n\n return session.encrypt(JSON.stringify(keyInfo));\n }\n\n /**\n * Internal helper for getting the olm related data associated with a participant.\n *\n * @param {JitsiParticipant} participant - Participant whose data wants to be extracted.\n * @returns {Object}\n * @private\n */\n _getParticipantOlmData(participant) {\n participant[kOlmData] = participant[kOlmData] || {};\n\n return participant[kOlmData];\n }\n\n /**\n * Handles leaving the conference, cleaning up olm sessions.\n *\n * @private\n */\n async _onConferenceLeft() {\n logger.debug('Conference left');\n\n await this._init;\n\n for (const participant of this._conf.getParticipants()) {\n this._onParticipantLeft(participant.getId(), participant);\n }\n\n if (this._olmAccount) {\n this._olmAccount.free();\n this._olmAccount = undefined;\n }\n }\n\n /**\n * Main message handler. Handles 1-to-1 messages received from other participants\n * and send the appropriate replies.\n *\n * @private\n */\n async _onEndpointMessageReceived(participant, payload) {\n if (payload[JITSI_MEET_MUC_TYPE] !== OLM_MESSAGE_TYPE) {\n return;\n }\n\n if (!payload.olm) {\n logger.warn('Incorrectly formatted message');\n\n return;\n }\n\n await this._init;\n\n const msg = payload.olm;\n const pId = participant.getId();\n const olmData = this._getParticipantOlmData(participant);\n\n switch (msg.type) {\n case OLM_MESSAGE_TYPES.SESSION_INIT: {\n if (olmData.session) {\n logger.warn(`Participant ${pId} already has a session`);\n\n this._sendError(participant, 'Session already established');\n } else {\n // Create a session for communicating with this participant.\n\n const session = new Olm.Session();\n\n session.create_outbound(this._olmAccount, msg.data.idKey, msg.data.otKey);\n olmData.session = session;\n\n // Send ACK\n const ack = {\n [JITSI_MEET_MUC_TYPE]: OLM_MESSAGE_TYPE,\n olm: {\n type: OLM_MESSAGE_TYPES.SESSION_ACK,\n data: {\n ciphertext: this._encryptKeyInfo(session),\n uuid: msg.data.uuid\n }\n }\n };\n\n this._sendMessage(ack, pId);\n this._onParticipantE2EEChannelReady(pId);\n }\n break;\n }\n case OLM_MESSAGE_TYPES.SESSION_ACK: {\n if (olmData.session) {\n logger.warn(`Participant ${pId} already has a session`);\n\n this._sendError(participant, 'No session found');\n } else if (msg.data.uuid === olmData.pendingSessionUuid) {\n const { ciphertext } = msg.data;\n const d = this._reqs.get(msg.data.uuid);\n const session = new Olm.Session();\n\n session.create_inbound(this._olmAccount, ciphertext.body);\n\n // Remove OT keys that have been used to setup this session.\n this._olmAccount.remove_one_time_keys(session);\n\n // Decrypt first message.\n const data = session.decrypt(ciphertext.type, ciphertext.body);\n\n olmData.session = session;\n olmData.pendingSessionUuid = undefined;\n\n this._onParticipantE2EEChannelReady(pId);\n\n this._reqs.delete(msg.data.uuid);\n d.resolve();\n\n const json = safeJsonParse(data);\n\n if (json.key) {\n const key = base64js.toByteArray(json.key);\n const keyIndex = json.keyIndex;\n\n olmData.lastKey = key;\n this.eventEmitter.emit(OlmAdapterEvents.PARTICIPANT_KEY_UPDATED, pId, key, keyIndex);\n }\n } else {\n logger.warn('Received ACK with the wrong UUID');\n\n this._sendError(participant, 'Invalid UUID');\n }\n break;\n }\n case OLM_MESSAGE_TYPES.ERROR: {\n logger.error(msg.data.error);\n\n break;\n }\n case OLM_MESSAGE_TYPES.KEY_INFO: {\n if (olmData.session) {\n const { ciphertext } = msg.data;\n const data = olmData.session.decrypt(ciphertext.type, ciphertext.body);\n const json = safeJsonParse(data);\n\n if (json.key !== undefined && json.keyIndex !== undefined) {\n const key = json.key ? base64js.toByteArray(json.key) : false;\n const keyIndex = json.keyIndex;\n\n if (!isEqual(olmData.lastKey, key)) {\n olmData.lastKey = key;\n this.eventEmitter.emit(OlmAdapterEvents.PARTICIPANT_KEY_UPDATED, pId, key, keyIndex);\n }\n\n // Send ACK.\n const ack = {\n [JITSI_MEET_MUC_TYPE]: OLM_MESSAGE_TYPE,\n olm: {\n type: OLM_MESSAGE_TYPES.KEY_INFO_ACK,\n data: {\n ciphertext: this._encryptKeyInfo(olmData.session),\n uuid: msg.data.uuid\n }\n }\n };\n\n this._sendMessage(ack, pId);\n }\n } else {\n logger.debug(`Received key info message from ${pId} but we have no session for them!`);\n\n this._sendError(participant, 'No session found while processing key-info');\n }\n break;\n }\n case OLM_MESSAGE_TYPES.KEY_INFO_ACK: {\n if (olmData.session) {\n const { ciphertext } = msg.data;\n const data = olmData.session.decrypt(ciphertext.type, ciphertext.body);\n const json = safeJsonParse(data);\n\n if (json.key !== undefined && json.keyIndex !== undefined) {\n const key = json.key ? base64js.toByteArray(json.key) : false;\n const keyIndex = json.keyIndex;\n\n if (!isEqual(olmData.lastKey, key)) {\n olmData.lastKey = key;\n this.eventEmitter.emit(OlmAdapterEvents.PARTICIPANT_KEY_UPDATED, pId, key, keyIndex);\n }\n }\n\n const d = this._reqs.get(msg.data.uuid);\n\n this._reqs.delete(msg.data.uuid);\n d.resolve();\n } else {\n logger.debug(`Received key info ack message from ${pId} but we have no session for them!`);\n\n this._sendError(participant, 'No session found while processing key-info-ack');\n }\n break;\n }\n }\n }\n\n /**\n * Handles a participant leaving. When a participant leaves their olm session is destroyed.\n *\n * @private\n */\n _onParticipantLeft(id, participant) {\n logger.debug(`Participant ${id} left`);\n\n this.clearParticipantSession(participant);\n }\n\n /**\n * Handles an update in a participant's presence property.\n *\n * @param {JitsiParticipant} participant - The participant.\n * @param {string} name - The name of the property that changed.\n * @param {*} oldValue - The property's previous value.\n * @param {*} newValue - The property's new value.\n * @private\n */\n async _onParticipantPropertyChanged(participant, name, oldValue, newValue) {\n switch (name) {\n case 'e2ee.enabled':\n if (newValue && this._conf.isE2EEEnabled()) {\n const localParticipantId = this._conf.myUserId();\n const participantId = participant.getId();\n const participantFeatures = await participant.getFeatures();\n\n if (participantFeatures.has(FEATURE_E2EE) && localParticipantId < participantId) {\n if (this._sessionInitialization) {\n await this._sessionInitialization;\n }\n await this._sendSessionInit(participant);\n\n const olmData = this._getParticipantOlmData(participant);\n const uuid = uuidv4();\n const data = {\n [JITSI_MEET_MUC_TYPE]: OLM_MESSAGE_TYPE,\n olm: {\n type: OLM_MESSAGE_TYPES.KEY_INFO,\n data: {\n ciphertext: this._encryptKeyInfo(olmData.session),\n uuid\n }\n }\n };\n\n this._sendMessage(data, participantId);\n }\n }\n break;\n }\n }\n\n /**\n * Builds and sends an error message to the target participant.\n *\n * @param {JitsiParticipant} participant - The target participant.\n * @param {string} error - The error message.\n * @returns {void}\n */\n _sendError(participant, error) {\n const pId = participant.getId();\n const err = {\n [JITSI_MEET_MUC_TYPE]: OLM_MESSAGE_TYPE,\n olm: {\n type: OLM_MESSAGE_TYPES.ERROR,\n data: {\n error\n }\n }\n };\n\n this._sendMessage(err, pId);\n }\n\n /**\n * Internal helper to send the given object to the given participant ID.\n * This function merely exists so the transport can be easily swapped.\n * Currently messages are transmitted via XMPP MUC private messages.\n *\n * @param {object} data - The data that will be sent to the target participant.\n * @param {string} participantId - ID of the target participant.\n */\n _sendMessage(data, participantId) {\n this._conf.sendMessage(data, participantId);\n }\n\n /**\n * Builds and sends the session-init request to the target participant.\n *\n * @param {JitsiParticipant} participant - Participant to whom we'll send the request.\n * @returns {Promise} - The promise will be resolved when the session-ack is received.\n * @private\n */\n _sendSessionInit(participant) {\n const pId = participant.getId();\n const olmData = this._getParticipantOlmData(participant);\n\n if (olmData.session) {\n logger.warn(`Tried to send session-init to ${pId} but we already have a session`);\n\n return Promise.reject();\n }\n\n if (olmData.pendingSessionUuid !== undefined) {\n logger.warn(`Tried to send session-init to ${pId} but we already have a pending session`);\n\n return Promise.reject();\n }\n\n // Generate a One Time Key.\n this._olmAccount.generate_one_time_keys(1);\n\n const otKeys = JSON.parse(this._olmAccount.one_time_keys());\n const otKey = Object.values(otKeys.curve25519)[0];\n\n if (!otKey) {\n return Promise.reject(new Error('No one-time-keys generated'));\n }\n\n // Mark the OT keys (one really) as published so they are not reused.\n this._olmAccount.mark_keys_as_published();\n\n const uuid = uuidv4();\n const init = {\n [JITSI_MEET_MUC_TYPE]: OLM_MESSAGE_TYPE,\n olm: {\n type: OLM_MESSAGE_TYPES.SESSION_INIT,\n data: {\n idKey: this._idKey,\n otKey,\n uuid\n }\n }\n };\n\n const d = new Deferred();\n\n d.setRejectTimeout(REQ_TIMEOUT);\n d.catch(() => {\n this._reqs.delete(uuid);\n olmData.pendingSessionUuid = undefined;\n });\n this._reqs.set(uuid, d);\n\n this._sendMessage(init, pId);\n\n // Store the UUID for matching with the ACK.\n olmData.pendingSessionUuid = uuid;\n\n return d;\n }\n}\n\n/**\n * Helper to ensure JSON parsing always returns an object.\n *\n * @param {string} data - The data that needs to be parsed.\n * @returns {object} - Parsed data or empty object in case of failure.\n */\nfunction safeJsonParse(data) {\n try {\n return JSON.parse(data);\n } catch (e) {\n return {};\n }\n}\n\nOlmAdapter.events = OlmAdapterEvents;\n","import { getLogger } from '@jitsi/logger';\nimport debounce from 'lodash.debounce';\n\nimport * as JitsiConferenceEvents from '../../JitsiConferenceEvents';\n\nimport { KeyHandler } from './KeyHandler';\nimport { OlmAdapter } from './OlmAdapter';\nimport { importKey, ratchet } from './crypto-utils';\n\nconst logger = getLogger(__filename);\n\n// Period which we'll wait before updating / rotating our keys when a participant\n// joins or leaves.\nconst DEBOUNCE_PERIOD = 5000;\n\n/**\n * This module integrates {@link E2EEContext} with {@link OlmAdapter} in order to distribute the keys for encryption.\n */\nexport class ManagedKeyHandler extends KeyHandler {\n /**\n * Build a new AutomaticKeyHandler instance, which will be used in a given conference.\n */\n constructor(conference) {\n super(conference);\n\n this._key = undefined;\n this._conferenceJoined = false;\n\n this._olmAdapter = new OlmAdapter(conference);\n\n this._rotateKey = debounce(this._rotateKeyImpl, DEBOUNCE_PERIOD);\n this._ratchetKey = debounce(this._ratchetKeyImpl, DEBOUNCE_PERIOD);\n\n // Olm signalling events.\n this._olmAdapter.on(\n OlmAdapter.events.PARTICIPANT_KEY_UPDATED,\n this._onParticipantKeyUpdated.bind(this));\n\n this.conference.on(\n JitsiConferenceEvents.PARTICIPANT_PROPERTY_CHANGED,\n this._onParticipantPropertyChanged.bind(this));\n this.conference.on(\n JitsiConferenceEvents.USER_JOINED,\n this._onParticipantJoined.bind(this));\n this.conference.on(\n JitsiConferenceEvents.USER_LEFT,\n this._onParticipantLeft.bind(this));\n this.conference.on(\n JitsiConferenceEvents.CONFERENCE_JOINED,\n () => {\n this._conferenceJoined = true;\n });\n }\n\n /**\n * When E2EE is enabled it initializes sessions and sets the key.\n * Cleans up the sessions when disabled.\n *\n * @param {boolean} enabled - whether E2EE should be enabled or not.\n * @returns {void}\n */\n async _setEnabled(enabled) {\n if (enabled) {\n await this._olmAdapter.initSessions();\n } else {\n this._olmAdapter.clearAllParticipantsSessions();\n }\n\n // Generate a random key in case we are enabling.\n this._key = enabled ? this._generateKey() : false;\n\n // Send it to others using the E2EE olm channel.\n const index = await this._olmAdapter.updateKey(this._key);\n\n // Set our key so we begin encrypting.\n this.e2eeCtx.setKey(this.conference.myUserId(), this._key, index);\n }\n\n /**\n * Handles an update in a participant's presence property.\n *\n * @param {JitsiParticipant} participant - The participant.\n * @param {string} name - The name of the property that changed.\n * @param {*} oldValue - The property's previous value.\n * @param {*} newValue - The property's new value.\n * @private\n */\n async _onParticipantPropertyChanged(participant, name, oldValue, newValue) {\n switch (name) {\n case 'e2ee.idKey':\n logger.debug(`Participant ${participant.getId()} updated their id key: ${newValue}`);\n break;\n case 'e2ee.enabled':\n if (!newValue && this.enabled) {\n this._olmAdapter.clearParticipantSession(participant);\n }\n break;\n }\n }\n\n /**\n * Advances (using ratcheting) the current key when a new participant joins the conference.\n * @private\n */\n _onParticipantJoined() {\n if (this._conferenceJoined && this.enabled) {\n this._ratchetKey();\n }\n }\n\n /**\n * Rotates the current key when a participant leaves the conference.\n * @private\n */\n _onParticipantLeft(id) {\n this.e2eeCtx.cleanup(id);\n\n if (this.enabled) {\n this._rotateKey();\n }\n }\n\n /**\n * Rotates the local key. Rotating the key implies creating a new one, then distributing it\n * to all participants and once they all received it, start using it.\n *\n * @private\n */\n async _rotateKeyImpl() {\n logger.debug('Rotating key');\n\n this._key = this._generateKey();\n const index = await this._olmAdapter.updateKey(this._key);\n\n this.e2eeCtx.setKey(this.conference.myUserId(), this._key, index);\n }\n\n /**\n * Advances the current key by using ratcheting.\n *\n * @private\n */\n async _ratchetKeyImpl() {\n logger.debug('Ratchetting key');\n\n const material = await importKey(this._key);\n const newKey = await ratchet(material);\n\n this._key = new Uint8Array(newKey);\n\n const index = this._olmAdapter.updateCurrentKey(this._key);\n\n this.e2eeCtx.setKey(this.conference.myUserId(), this._key, index);\n }\n\n /**\n * Handles an update in a participant's key.\n *\n * @param {string} id - The participant ID.\n * @param {Uint8Array | boolean} key - The new key for the participant.\n * @param {Number} index - The new key's index.\n * @private\n */\n _onParticipantKeyUpdated(id, key, index) {\n logger.debug(`Participant ${id} updated their key`);\n\n this.e2eeCtx.setKey(id, key, index);\n }\n\n /**\n * Generates a new 256 bit random key.\n *\n * @returns {Uint8Array}\n * @private\n */\n _generateKey() {\n return window.crypto.getRandomValues(new Uint8Array(32));\n }\n}\n","/**\n * Derives a set of keys from the master key.\n * @param {CryptoKey} material - master key to derive from\n *\n * See https://tools.ietf.org/html/draft-omara-sframe-00#section-4.3.1\n */\nexport async function deriveKeys(material) {\n const info = new ArrayBuffer();\n const textEncoder = new TextEncoder();\n\n // https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/deriveKey#HKDF\n // https://developer.mozilla.org/en-US/docs/Web/API/HkdfParams\n const encryptionKey = await crypto.subtle.deriveKey({\n name: 'HKDF',\n salt: textEncoder.encode('JFrameEncryptionKey'),\n hash: 'SHA-256',\n info\n }, material, {\n name: 'AES-GCM',\n length: 128\n }, false, [ 'encrypt', 'decrypt' ]);\n\n return {\n material,\n encryptionKey\n };\n}\n\n/**\n * Ratchets a key. See\n * https://tools.ietf.org/html/draft-omara-sframe-00#section-4.3.5.1\n * @param {CryptoKey} material - base key material\n * @returns {ArrayBuffer} - ratcheted key material\n */\nexport async function ratchet(material) {\n const textEncoder = new TextEncoder();\n\n // https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/deriveBits\n return crypto.subtle.deriveBits({\n name: 'HKDF',\n salt: textEncoder.encode('JFrameRatchetKey'),\n hash: 'SHA-256',\n info: new ArrayBuffer()\n }, material, 256);\n}\n\n/**\n * Converts a raw key into a WebCrypto key object with default options\n * suitable for our usage.\n * @param {ArrayBuffer} keyBytes - raw key\n * @param {Array} keyUsages - key usages, see importKey documentation\n * @returns {CryptoKey} - the WebCrypto key.\n */\nexport async function importKey(keyBytes) {\n // https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/importKey\n return crypto.subtle.importKey('raw', keyBytes, 'HKDF', false, [ 'deriveBits', 'deriveKey' ]);\n}\n","import browser from '../browser';\n\nimport { ExternallyManagedKeyHandler } from './ExternallyManagedKeyHandler';\nimport { ManagedKeyHandler } from './ManagedKeyHandler';\nimport { OlmAdapter } from './OlmAdapter';\n\n/**\n * This module integrates {@link KeyHandler} with {@link JitsiConference} in order to enable E2E encryption.\n */\nexport class E2EEncryption {\n /**\n * A constructor.\n * @param {JitsiConference} conference - The conference instance for which E2E encryption is to be enabled.\n */\n constructor(conference) {\n const { e2ee = {} } = conference.options.config;\n\n this._externallyManaged = e2ee.externallyManagedKey;\n\n if (this._externallyManaged) {\n this._keyHandler = new ExternallyManagedKeyHandler(conference);\n } else {\n this._keyHandler = new ManagedKeyHandler(conference);\n }\n }\n\n /**\n * Indicates if E2EE is supported in the current platform.\n *\n * @param {object} config - Global configuration.\n * @returns {boolean}\n */\n static isSupported(config) {\n const { e2ee = {} } = config;\n\n if (!e2ee.externallyManagedKey && !OlmAdapter.isSupported()) {\n return false;\n }\n\n return !(config.testing && config.testing.disableE2EE)\n && (browser.supportsInsertableStreams()\n || (config.enableEncodedTransformSupport && browser.supportsEncodedTransform()));\n }\n\n /**\n * Indicates whether E2EE is currently enabled or not.\n *\n * @returns {boolean}\n */\n isEnabled() {\n return this._keyHandler.isEnabled();\n }\n\n /**\n * Enables / disables End-To-End encryption.\n *\n * @param {boolean} enabled - whether E2EE should be enabled or not.\n * @returns {void}\n */\n async setEnabled(enabled) {\n await this._keyHandler.setEnabled(enabled);\n }\n\n /**\n * Sets the key and index for End-to-End encryption.\n *\n * @param {CryptoKey} [keyInfo.encryptionKey] - encryption key.\n * @param {Number} [keyInfo.index] - the index of the encryption key.\n * @returns {void}\n */\n setEncryptionKey(keyInfo) {\n this._keyHandler.setKey(keyInfo);\n }\n}\n","/* global $ */\n\nimport { b64_sha1, Strophe } from 'strophe.js'; // eslint-disable-line camelcase\n\nimport XMPPEvents from '../../service/xmpp/XMPPEvents';\nimport Listenable from '../util/Listenable';\n\n/**\n * The property\n */\nconst IDENTITY_PROPERTIES = [ 'category', 'type', 'lang', 'name' ];\nconst IDENTITY_PROPERTIES_FOR_COMPARE = [ 'category', 'type', 'lang' ];\nconst HASH = 'sha-1';\n\n/**\n *\n * @param a\n * @param b\n */\nfunction compareIdentities(a, b) {\n let res = 0;\n\n IDENTITY_PROPERTIES_FOR_COMPARE.some(key =>\n (res = ((a[key] > b[key]) && 1) || ((a[key] < b[key]) && -1)) !== 0\n );\n\n return res;\n}\n\n/**\n * Produces a sha-1 from provided identity and features values.\n *\n * @param {Array} identities - The identity objects.\n * @param {Array} features - The features.\n * @returns {string}\n */\nfunction generateSha(identities, features) {\n const sortedIdentities = identities.sort(compareIdentities).reduce(\n (accumulatedValue, identity) => `${\n IDENTITY_PROPERTIES.reduce(\n (tmp, key, idx) =>\n tmp\n + (idx === 0 ? '' : '/')\n + (identity[key] ? identity[key] : ''),\n '')\n }<`, '');\n const sortedFeatures = features.sort().reduce(\n (tmp, feature) => `${tmp + feature}<`, '');\n\n return b64_sha1(sortedIdentities + sortedFeatures);\n}\n\n/**\n * Parses the disco-info node and returns the sets of features and identities.\n * @param {String} node The node with results to parse.\n * @returns {{features: Set, identities: Set}}\n */\nexport function parseDiscoInfo(node) {\n const features = new Set();\n const identities = new Set();\n\n $(node).find('>query>feature')\n .each((_, el) => features.add(el.getAttribute('var')));\n $(node).find('>query>identity')\n .each((_, el) => identities.add({\n type: el.getAttribute('type'),\n name: el.getAttribute('name'),\n category: el.getAttribute('category')\n }));\n\n return {\n features,\n identities\n };\n}\n\n/**\n * Implements xep-0115 ( http://xmpp.org/extensions/xep-0115.html )\n */\nexport default class Caps extends Listenable {\n /**\n * Constructs new Caps instance.\n * @param {Strophe.Connection} connection the strophe connection object\n * @param {String} node the value of the node attribute of the \"c\" xml node\n * that will be sent to the other participants\n */\n constructor(connection = {}, node = 'http://jitsi.org/jitsimeet') {\n super();\n this.node = node;\n this.disco = connection.disco;\n if (!this.disco) {\n throw new Error(\n 'Missing strophe-plugins '\n + '(disco plugin is required)!');\n }\n\n this.version = '';\n this.rooms = new Set();\n\n // We keep track of features added outside the library and we publish them\n // in the presence of the participant for simplicity, avoiding the disco info request-response.\n this.externalFeatures = new Set();\n\n const emuc = connection.emuc;\n\n emuc.addListener(XMPPEvents.EMUC_ROOM_ADDED,\n room => this._addChatRoom(room));\n emuc.addListener(XMPPEvents.EMUC_ROOM_REMOVED,\n room => this._removeChatRoom(room));\n Object.keys(emuc.rooms).forEach(jid => {\n this._addChatRoom(emuc.rooms[jid]);\n });\n\n Strophe.addNamespace('CAPS', 'http://jabber.org/protocol/caps');\n this.disco.addFeature(Strophe.NS.CAPS);\n }\n\n /**\n * Adds new feature to the list of supported features for the local\n * participant\n * @param {String} feature the name of the feature.\n * @param {boolean} submit if true - new presence with updated \"c\" node\n * will be sent.\n * @param {boolean} external whether this feature was added externally to the library.\n * We put features used directly by the clients (is jibri, remote-control enabled etc.) in the presence\n * to avoid additional disco-info queries by those clients.\n */\n addFeature(feature, submit = false, external = false) {\n this.disco.addFeature(feature);\n this._generateVersion();\n\n if (external && !this.externalFeatures.has(feature)) {\n this.externalFeatures.add(feature);\n this.rooms.forEach(room => this._updateRoomWithExternalFeatures(room));\n }\n\n if (submit) {\n this.submit();\n }\n }\n\n /**\n * Removes a feature from the list of supported features for the local\n * participant\n * @param {String} feature the name of the feature.\n * @param {boolean} submit if true - new presence with updated \"c\" node\n * will be sent.\n * @param {boolean} external whether this feature was added externally to the library.\n */\n removeFeature(feature, submit = false, external = false) {\n this.disco.removeFeature(feature);\n this._generateVersion();\n\n if (external && this.externalFeatures.has(feature)) {\n this.externalFeatures.delete(feature);\n this.rooms.forEach(room => this._updateRoomWithExternalFeatures(room));\n }\n\n if (submit) {\n this.submit();\n }\n }\n\n /**\n * Sends new presence stanza for every room from the list of rooms.\n */\n submit() {\n this.rooms.forEach(room => room.sendPresence());\n }\n\n /**\n * Updates the presences in the room based on the current values in externalFeatures.\n * @param {ChatRoom} room the room to update.\n * @private\n */\n _updateRoomWithExternalFeatures(room) {\n if (this.externalFeatures.size === 0) {\n room.removeFromPresence('features');\n } else {\n const children = [];\n\n this.externalFeatures.forEach(f => {\n children.push({\n 'tagName': 'feature',\n attributes: { 'var': f }\n });\n });\n\n room.addOrReplaceInPresence('features', { children });\n }\n }\n\n /**\n * Returns a set with the features for a host.\n * @param {String} jid the jid of the host\n * @param {int} timeout the timeout in ms for reply from the host.\n * @returns {Promise, Error>}\n */\n getFeaturesAndIdentities(jid, node, timeout = 5000) {\n return this._getDiscoInfo(jid, node, timeout);\n }\n\n /**\n * Returns a set with the features and identities for a host.\n * @param {String} jid the jid of the host\n * @param {String|null} node the node to query\n * @param {int} timeout the timeout in ms for reply from the host.\n * @returns {Promise}\n * @private\n */\n _getDiscoInfo(jid, node, timeout) {\n return new Promise((resolve, reject) =>\n this.disco.info(jid, node, response => {\n resolve(parseDiscoInfo(response));\n }, reject, timeout)\n );\n }\n\n /**\n * Adds ChatRoom instance to the list of rooms. Adds listeners to the room\n * and adds \"c\" element to the presences of the room.\n * @param {ChatRoom} room the room.\n */\n _addChatRoom(room) {\n this.rooms.add(room);\n this._fixChatRoomPresenceMap(room);\n\n this._updateRoomWithExternalFeatures(room);\n }\n\n /**\n * Removes ChatRoom instance from the list of rooms. Removes listeners\n * added from the Caps class.\n * @param {ChatRoom} room the room.\n */\n _removeChatRoom(room) {\n this.rooms.delete(room);\n }\n\n /**\n * Creates/updates the \"c\" xml node into the presence of the passed room.\n * @param {ChatRoom} room the room.\n */\n _fixChatRoomPresenceMap(room) {\n room.addOrReplaceInPresence('c', {\n attributes: {\n xmlns: Strophe.NS.CAPS,\n hash: HASH,\n node: this.node,\n ver: this.version\n }\n });\n }\n\n /**\n * Handles this.version changes.\n */\n _notifyVersionChanged() {\n // update the version for all rooms\n this.rooms.forEach(room => this._fixChatRoomPresenceMap(room));\n }\n\n /**\n * Generates the value for the \"ver\" attribute.\n */\n _generateVersion() {\n this.version\n = generateSha(this.disco._identities, this.disco._features);\n\n this._notifyVersionChanged();\n }\n}\n","import { getLogger } from '@jitsi/logger';\n\nimport Listenable from '../util/Listenable';\n\nexport const NETWORK_INFO_EVENT = 'NETWORK_INFO_CHANGED';\n\nconst logger = getLogger(__filename);\n\n/**\n * Module provides information about the current status of the internet\n * connection. Lib-jitsi-meet doesn't have any logic for detecting internet\n * online/offline, but rather it relies on the information supplied by the app\n * that uses it. By default the online state is assumed and the lib acts as if\n * it was connected. See {@link JitsiMeetJS.setNetworkInfo}.\n */\nexport class NetworkInfo extends Listenable {\n /**\n * Creates new {@link NetworkInfo} instance.\n */\n constructor() {\n super();\n this._current = {\n isOnline: true\n };\n }\n\n /**\n * Updates the network info state.\n * @param {boolean} isOnline - {@code true} if internet is online or {@code false} otherwise.\n */\n updateNetworkInfo({ isOnline }) {\n logger.debug('updateNetworkInfo', { isOnline });\n this._current = {\n isOnline: isOnline === true\n };\n this.eventEmitter.emit(NETWORK_INFO_EVENT, this._current);\n }\n\n /**\n * Returns the online/offline internet status. By default the value is {@code true} and changes only if\n * the lib's user wires the state through {@link JitsiMeetJS.setNetworkInfo} like the jitsi-meet does. Because of\n * that any logic should still assume that the internet may be offline and should handle the failure gracefully.\n * It's only a good hint in the other way around: to pause internet operations until it comes back online.\n * @returns {boolean}\n */\n isOnline() {\n return this._current.isOnline === true;\n }\n}\n\nconst networkInfo = new NetworkInfo();\n\nexport default networkInfo;\n","import { getLogger } from '@jitsi/logger';\n\nimport {\n default as NetworkInfo,\n NETWORK_INFO_EVENT\n} from '../connectivity/NetworkInfo';\nimport { getJitterDelay } from '../util/Retry';\n\nconst logger = getLogger(__filename);\n\n/**\n * The class contains the logic for triggering connection resume via XEP-0198 stream management.\n * It does two things, the first one is it tracks the internet online/offline status and it makes sure that\n * the reconnect is attempted only while online. The seconds thing is that it tracks the retry attempts and extends\n * the retry interval using the full jitter pattern.\n */\nexport default class ResumeTask {\n /**\n * Initializes new {@code RetryTask}.\n * @param {Strophe.Connection} stropheConnection - The Strophe connection instance.\n */\n constructor(stropheConnection) {\n this._stropheConn = stropheConnection;\n\n /**\n * The counter increased before each resume retry attempt, used to calculate exponential backoff.\n * @type {number}\n * @private\n */\n this._resumeRetryN = 0;\n\n this._retryDelay = undefined;\n }\n\n /**\n * @returns {number|undefined} - How much the app will wait before trying to resume the XMPP connection. When\n * 'undefined' it means that no resume task was not scheduled.\n */\n get retryDelay() {\n return this._retryDelay;\n }\n\n /**\n * Called by {@link XmppConnection} when the connection drops and it's a signal it wants to schedule a reconnect.\n *\n * @returns {void}\n */\n schedule() {\n this._cancelResume();\n\n this._resumeRetryN += 1;\n\n this._networkOnlineListener\n = NetworkInfo.addEventListener(\n NETWORK_INFO_EVENT,\n ({ isOnline }) => {\n if (isOnline) {\n this._scheduleResume();\n } else {\n this._cancelResume();\n }\n });\n\n NetworkInfo.isOnline() && this._scheduleResume();\n }\n\n /**\n * Schedules a delayed timeout which will execute the resume action.\n * @private\n * @returns {void}\n */\n _scheduleResume() {\n if (this._resumeTimeout) {\n\n // NO-OP\n return;\n }\n\n // The retry delay will be:\n // 1st retry: 1.5s - 3s\n // 2nd retry: 3s - 9s\n // 3rd and next retry: 4.5s - 27s\n this._resumeRetryN = Math.min(3, this._resumeRetryN);\n this._retryDelay = getJitterDelay(\n /* retry */ this._resumeRetryN,\n /* minDelay */ this._resumeRetryN * 1500,\n 3);\n\n logger.info(`Will try to resume the XMPP connection in ${this.retryDelay}ms`);\n\n this._resumeTimeout = setTimeout(() => this._resumeConnection(), this.retryDelay);\n }\n\n /**\n * Cancels the delayed resume task.\n *\n * @private\n * @returns {void}\n */\n _cancelResume() {\n if (this._resumeTimeout) {\n logger.info('Canceling connection resume task');\n clearTimeout(this._resumeTimeout);\n this._resumeTimeout = undefined;\n this._retryDelay = undefined;\n }\n }\n\n /**\n * Resumes the XMPP connection using the stream management plugin.\n *\n * @private\n * @returns {void}\n */\n _resumeConnection() {\n const { streamManagement } = this._stropheConn;\n const resumeToken = streamManagement.getResumeToken();\n\n // Things may have changed since when the task was scheduled\n if (!resumeToken) {\n return;\n }\n\n logger.info('Trying to resume the XMPP connection');\n\n const url = new URL(this._stropheConn.service);\n let { search } = url;\n const pattern = /(previd=)([\\w-]+)/;\n const oldToken = search.match(pattern);\n\n // Replace previd if the previd value has changed.\n if (oldToken && oldToken.indexOf(resumeToken) === -1) {\n search = search.replace(pattern, `$1${resumeToken}`);\n\n // Append previd if it doesn't exist.\n } else if (!oldToken) {\n search += search.indexOf('?') === -1 ? `?previd=${resumeToken}` : `&previd=${resumeToken}`;\n }\n\n url.search = search;\n\n this._stropheConn.service = url.toString();\n\n streamManagement.resume();\n }\n\n /**\n * Cancels the retry task. It's called by {@link XmppConnection} when it's no longer interested in reconnecting for\n * example when the disconnect method is called.\n *\n * @returns {void}\n */\n cancel() {\n this._cancelResume();\n this._resumeRetryN = 0;\n if (this._networkOnlineListener) {\n this._networkOnlineListener();\n this._networkOnlineListener = null;\n }\n }\n}\n","/**\n* Gets next timeout using the full jitter pattern.\n*\n* NOTE that there are no checks for argument correctness, so either do the math or use defaults.\n*\n* @param {number} retry - The retry number.\n* @param {number} minDelay - The minimal delay in milliseconds.\n* @param {number} base - The exponent base.\n* @returns {number} - The amount of waiting before trying another time given in milliseconds.\n* @private\n*/\nexport function getJitterDelay(retry, minDelay = 500, base = 2) {\n return Math.floor((Math.random() * ((Math.pow(base, retry) * 1000) - minDelay)) + minDelay);\n}\n","/**\n * Attaches to the {@link Strophe.Connection.rawInput} which is called whenever any data is received from the server.\n */\nexport default class LastRequestTracker {\n /**\n * Initializes new instance.\n */\n constructor() {\n this._lastSuccess = null;\n this._lastFailedMessage = null;\n }\n\n /**\n * Starts tracking requests on the given connection.\n *\n * @param {XmppConnection} xmppConnection - The XMPP connection which manages the given {@code stropheConnection}.\n * @param {Object} stropheConnection - Strophe connection instance.\n */\n startTracking(xmppConnection, stropheConnection) {\n const originalRawInput = stropheConnection.rawInput;\n\n stropheConnection.rawInput = (...args) => {\n const rawMessage = args[0];\n\n if (rawMessage.includes('failure')) {\n this._lastFailedMessage = rawMessage;\n }\n\n // It's okay to use rawInput callback only once the connection has been established, otherwise it will\n // treat 'item-not-found' or other connection error on websocket reconnect as successful stanza received.\n if (xmppConnection.connected) {\n this._lastSuccess = Date.now();\n }\n originalRawInput.apply(stropheConnection, args);\n };\n }\n\n /**\n * Returns the last raw failed incoming message on the xmpp connection.\n *\n * @returns {string|null}\n */\n getLastFailedMessage() {\n return this._lastFailedMessage;\n }\n\n /**\n * Returns how many milliseconds have passed since the last successful BOSH request.\n *\n * @returns {number|null}\n */\n getTimeSinceLastSuccess() {\n return this._lastSuccess\n ? Date.now() - this._lastSuccess\n : null;\n }\n}\n","import Listenable from '../util/Listenable';\n\n/**\n * Creates ConnectionPlugin class that extends the passed class.\n * @param {Class} base the definition of the class that will be extended by\n * ConnectionPlugin\n */\nfunction getConnectionPluginDefinition(base = class {}) {\n /**\n * Base class for strophe connection plugins.\n */\n return class extends base {\n /**\n *\n */\n constructor(...args) {\n super(...args);\n this.connection = null;\n }\n\n /**\n *\n * @param connection\n */\n init(connection) {\n this.connection = connection;\n }\n };\n}\n\n/**\n * ConnectionPlugin class.\n */\nexport default getConnectionPluginDefinition();\n\n/**\n * ConnectionPlugin class that extends Listenable.\n */\nexport const ConnectionPluginListenable\n = getConnectionPluginDefinition(Listenable);\n","import { getLogger } from '@jitsi/logger';\nimport { $iq, Strophe } from 'strophe.js';\n\nimport GlobalOnErrorHandler from '../util/GlobalOnErrorHandler';\n\nimport ConnectionPlugin from './ConnectionPlugin';\n\n\nconst logger = getLogger(__filename);\n\n/**\n * Default ping every 10 sec\n */\nconst PING_DEFAULT_INTERVAL = 10000;\n\n/**\n * Default ping timeout error after 5 sec of waiting.\n */\nconst PING_DEFAULT_TIMEOUT = 5000;\n\n/**\n * Default value for how many ping failures will be tolerated before the WebSocket connection is killed.\n * The worst case scenario in case of ping timing out without a response is (25 seconds at the time of this writing):\n * PING_THRESHOLD * PING_INTERVAL + PING_TIMEOUT\n */\nconst PING_DEFAULT_THRESHOLD = 2;\n\n/**\n * XEP-0199 ping plugin.\n *\n * Registers \"urn:xmpp:ping\" namespace under Strophe.NS.PING.\n */\nexport default class PingConnectionPlugin extends ConnectionPlugin {\n /**\n * Constructs new object\n * @param {Object} options\n * @param {Function} options.onPingThresholdExceeded - Callback called when ping fails too many times (controlled\n * by the {@link PING_THRESHOLD} constant).\n * @param {Function} options._getTimeSinceLastServerResponse - A function to obtain the last seen\n * response from the server.\n * @param {Object} options.pingOptions - The ping options if any.\n * @constructor\n */\n constructor({ getTimeSinceLastServerResponse, onPingThresholdExceeded, pingOptions = {} }) {\n super();\n this.failedPings = 0;\n this._onPingThresholdExceeded = onPingThresholdExceeded;\n this._getTimeSinceLastServerResponse = getTimeSinceLastServerResponse;\n\n this.pingInterval = typeof pingOptions.interval === 'number' ? pingOptions.interval : PING_DEFAULT_INTERVAL;\n this.pingTimeout = typeof pingOptions.timeout === 'number' ? pingOptions.timeout : PING_DEFAULT_TIMEOUT;\n this.pingThreshold = typeof pingOptions.threshold === 'number'\n ? pingOptions.threshold : PING_DEFAULT_THRESHOLD;\n\n // The number of timestamps of send pings to keep.\n // The current value is 2 minutes.\n this.pingTimestampsToKeep = Math.round(120000 / this.pingInterval);\n this.pingExecIntervals = new Array(this.pingTimestampsToKeep);\n }\n\n /**\n * Initializes the plugin. Method called by Strophe.\n * @param connection Strophe connection instance.\n */\n init(connection) {\n super.init(connection);\n Strophe.addNamespace('PING', 'urn:xmpp:ping');\n }\n\n /* eslint-disable max-params */\n\n /**\n * Sends \"ping\" to given jid\n * @param jid the JID to which ping request will be sent.\n * @param success callback called on success.\n * @param error callback called on error.\n * @param timeout ms how long are we going to wait for the response. On\n * timeout error callback is called with undefined error argument.\n */\n ping(jid, success, error, timeout) {\n this._addPingExecutionTimestamp();\n\n const iq = $iq({\n type: 'get',\n to: jid\n });\n\n iq.c('ping', { xmlns: Strophe.NS.PING });\n this.connection.sendIQ2(iq, { timeout })\n .then(success, error);\n }\n\n /* eslint-enable max-params */\n\n /**\n * Starts to send ping in given interval to specified remote JID.\n * This plugin supports only one such task and stopInterval\n * must be called before starting a new one.\n * @param remoteJid remote JID to which ping requests will be sent to.\n */\n startInterval(remoteJid) {\n clearInterval(this.intervalId);\n this.intervalId = window.setInterval(() => {\n\n // when there were some server responses in the interval since the last time we checked (_lastServerCheck)\n // let's skip the ping\n\n const now = Date.now();\n\n if (this._getTimeSinceLastServerResponse() < now - this._lastServerCheck) {\n // do this just to keep in sync the intervals so we can detect suspended device\n this._addPingExecutionTimestamp();\n\n this._lastServerCheck = now;\n this.failedPings = 0;\n\n return;\n }\n\n this.ping(remoteJid, () => {\n // server response is measured on raw input and ping response time is measured after all the xmpp\n // processing is done in js, so there can be some misalignment when we do the check above.\n // That's why we store the last time we got the response\n this._lastServerCheck = this._getTimeSinceLastServerResponse() + Date.now();\n\n this.failedPings = 0;\n }, error => {\n this.failedPings += 1;\n const errmsg = `Ping ${error ? 'error' : 'timeout'}`;\n\n if (this.failedPings >= this.pingThreshold) {\n GlobalOnErrorHandler.callErrorHandler(new Error(errmsg));\n logger.error(errmsg, error);\n this._onPingThresholdExceeded && this._onPingThresholdExceeded();\n } else {\n logger.warn(errmsg, error);\n }\n }, this.pingTimeout);\n }, this.pingInterval);\n logger.info(`XMPP pings will be sent every ${this.pingInterval} ms`);\n }\n\n /**\n * Stops current \"ping\" interval task.\n */\n stopInterval() {\n if (this.intervalId) {\n window.clearInterval(this.intervalId);\n this.intervalId = null;\n this.failedPings = 0;\n logger.info('Ping interval cleared');\n }\n }\n\n /**\n * Adds the current time to the array of send ping timestamps.\n * @private\n */\n _addPingExecutionTimestamp() {\n this.pingExecIntervals.push(new Date().getTime());\n\n // keep array length to PING_TIMESTAMPS_TO_KEEP\n if (this.pingExecIntervals.length > this.pingTimestampsToKeep) {\n this.pingExecIntervals.shift();\n }\n }\n\n /**\n * Returns the maximum time between the recent sent pings, if there is a\n * big value it means the computer was inactive for some time(suspended).\n * Checks the maximum gap between sending pings, considering and the\n * current time. Trying to detect computer inactivity (sleep).\n *\n * @returns {int} the time ping was suspended, if it was not 0 is returned.\n */\n getPingSuspendTime() {\n const pingIntervals = this.pingExecIntervals.slice();\n\n // we need current time, as if ping was sent now\n // if computer sleeps we will get correct interval after next\n // scheduled ping, bet we sometimes need that interval before waiting\n // for the next ping, on closing the connection on error.\n pingIntervals.push(new Date().getTime());\n\n let maxInterval = 0;\n let previousTS = pingIntervals[0];\n\n pingIntervals.forEach(e => {\n const currentInterval = e - previousTS;\n\n if (currentInterval > maxInterval) {\n maxInterval = currentInterval;\n }\n\n previousTS = e;\n });\n\n // remove the interval between the ping sent\n // this way in normal execution there is no suspend and the return\n // will be 0 or close to 0.\n maxInterval -= this.pingInterval;\n\n // make sure we do not return less than 0\n return Math.max(maxInterval, 0);\n }\n}\n","import { getLogger } from '@jitsi/logger';\nimport { $pres, Strophe } from 'strophe.js';\nimport 'strophejs-plugin-stream-management';\n\nimport Listenable from '../util/Listenable';\n\nimport ResumeTask from './ResumeTask';\nimport LastSuccessTracker from './StropheLastSuccess';\nimport PingConnectionPlugin from './strophe.ping';\n\nconst logger = getLogger(__filename);\n\n/**\n * The lib-jitsi-meet layer for {@link Strophe.Connection}.\n */\nexport default class XmppConnection extends Listenable {\n /**\n * The list of {@link XmppConnection} events.\n *\n * @returns {Object}\n */\n static get Events() {\n return {\n CONN_STATUS_CHANGED: 'CONN_STATUS_CHANGED',\n CONN_SHARD_CHANGED: 'CONN_SHARD_CHANGED'\n };\n }\n\n /**\n * The list of Xmpp connection statuses.\n *\n * @returns {Strophe.Status}\n */\n static get Status() {\n return Strophe.Status;\n }\n\n /**\n * Initializes new connection instance.\n *\n * @param {Object} options\n * @param {String} options.serviceUrl - The BOSH or WebSocket service URL.\n * @param {String} options.shard - The BOSH or WebSocket is connecting to this shard.\n * Useful for detecting when shard changes.\n * @param {String} [options.enableWebsocketResume=true] - True/false to control the stream resumption functionality.\n * It will enable automatically by default if supported by the XMPP server.\n * @param {Number} [options.websocketKeepAlive=60000] - The websocket keep alive interval.\n * It's the interval + a up to a minute of jitter. Pass -1 to disable.\n * The keep alive is HTTP GET request to {@link options.serviceUrl} or to {@link options.websocketKeepAliveUrl}.\n * @param {Number} [options.websocketKeepAliveUrl] - The websocket keep alive url to use if any,\n * if missing the serviceUrl url will be used.\n * @param {Object} [options.xmppPing] - The xmpp ping settings.\n */\n constructor({ enableWebsocketResume, websocketKeepAlive, websocketKeepAliveUrl, serviceUrl, shard, xmppPing }) {\n super();\n this._options = {\n enableWebsocketResume: typeof enableWebsocketResume === 'undefined' ? true : enableWebsocketResume,\n pingOptions: xmppPing,\n shard,\n websocketKeepAlive: typeof websocketKeepAlive === 'undefined' ? 60 * 1000 : Number(websocketKeepAlive),\n websocketKeepAliveUrl\n };\n\n this._stropheConn = new Strophe.Connection(serviceUrl);\n this._usesWebsocket = serviceUrl.startsWith('ws:') || serviceUrl.startsWith('wss:');\n\n // The default maxRetries is 5, which is too long.\n this._stropheConn.maxRetries = 3;\n\n this._rawInputTracker = new LastSuccessTracker();\n this._rawInputTracker.startTracking(this, this._stropheConn);\n\n this._resumeTask = new ResumeTask(this._stropheConn);\n\n /**\n * @typedef DeferredSendIQ Object\n * @property {Element} iq - The IQ to send.\n * @property {function} resolve - The resolve method of the deferred Promise.\n * @property {function} reject - The reject method of the deferred Promise.\n * @property {number} timeout - The ID of the timeout task that needs to be cleared, before sending the IQ.\n */\n /**\n * Deferred IQs to be sent upon reconnect.\n * @type {Array}\n * @private\n */\n this._deferredIQs = [];\n\n // Ping plugin is mandatory for the Websocket mode to work correctly. It's used to detect when the connection\n // is broken (WebSocket/TCP connection not closed gracefully).\n this.addConnectionPlugin(\n 'ping',\n new PingConnectionPlugin({\n getTimeSinceLastServerResponse: () => this.getTimeSinceLastSuccess(),\n onPingThresholdExceeded: () => this._onPingErrorThresholdExceeded(),\n pingOptions: xmppPing\n }));\n\n // tracks whether this is the initial connection or a reconnect\n this._oneSuccessfulConnect = false;\n }\n\n /**\n * A getter for the connected state.\n *\n * @returns {boolean}\n */\n get connected() {\n const websocket = this._stropheConn && this._stropheConn._proto && this._stropheConn._proto.socket;\n\n return (this._status === Strophe.Status.CONNECTED || this._status === Strophe.Status.ATTACHED)\n && (!this.isUsingWebSocket || (websocket && websocket.readyState === WebSocket.OPEN));\n }\n\n /**\n * Retrieves the feature discovery plugin instance.\n *\n * @returns {Strophe.Connection.disco}\n */\n get disco() {\n return this._stropheConn.disco;\n }\n\n /**\n * A getter for the disconnecting state.\n *\n * @returns {boolean}\n */\n get disconnecting() {\n return this._stropheConn.disconnecting === true;\n }\n\n /**\n * A getter for the domain.\n *\n * @returns {string|null}\n */\n get domain() {\n return this._stropheConn.domain;\n }\n\n /**\n * Tells if Websocket is used as the transport for the current XMPP connection. Returns true for Websocket or false\n * for BOSH.\n * @returns {boolean}\n */\n get isUsingWebSocket() {\n return this._usesWebsocket;\n }\n\n /**\n * A getter for the JID.\n *\n * @returns {string|null}\n */\n get jid() {\n return this._stropheConn.jid;\n }\n\n /**\n * Returns headers for the last BOSH response received.\n *\n * @returns {string}\n */\n get lastResponseHeaders() {\n return this._stropheConn._proto && this._stropheConn._proto.lastResponseHeaders;\n }\n\n /**\n * A getter for the logger plugin instance.\n *\n * @returns {*}\n */\n get logger() {\n return this._stropheConn.logger;\n }\n\n /**\n * A getter for the connection options.\n *\n * @returns {*}\n */\n get options() {\n return this._stropheConn.options;\n }\n\n /**\n * A getter for the domain to be used for ping.\n */\n get pingDomain() {\n return this._options.pingOptions?.domain || this.domain;\n }\n\n /**\n * A getter for the service URL.\n *\n * @returns {string}\n */\n get service() {\n return this._stropheConn.service;\n }\n\n /**\n * Sets new value for shard.\n * @param value the new shard value.\n */\n set shard(value) {\n this._options.shard = value;\n\n // shard setting changed so let's schedule a new keep-alive check if connected\n if (this._oneSuccessfulConnect) {\n this._maybeStartWSKeepAlive();\n }\n }\n\n /**\n * Returns the current connection status.\n *\n * @returns {Strophe.Status}\n */\n get status() {\n return this._status;\n }\n\n /**\n * Adds a connection plugin to this instance.\n *\n * @param {string} name - The name of the plugin or rather a key under which it will be stored on this connection\n * instance.\n * @param {ConnectionPluginListenable} plugin - The plugin to add.\n */\n addConnectionPlugin(name, plugin) {\n this[name] = plugin;\n plugin.init(this);\n }\n\n /**\n * See {@link Strophe.Connection.addHandler}\n *\n * @returns {void}\n */\n addHandler(...args) {\n this._stropheConn.addHandler(...args);\n }\n\n /* eslint-disable max-params */\n /**\n * Wraps {@link Strophe.Connection.attach} method in order to intercept the connection status updates.\n * See {@link Strophe.Connection.attach} for the params description.\n *\n * @returns {void}\n */\n attach(jid, sid, rid, callback, ...args) {\n this._stropheConn.attach(jid, sid, rid, this._stropheConnectionCb.bind(this, callback), ...args);\n }\n\n /**\n * Wraps Strophe.Connection.connect method in order to intercept the connection status updates.\n * See {@link Strophe.Connection.connect} for the params description.\n *\n * @returns {void}\n */\n connect(jid, pass, callback, ...args) {\n this._stropheConn.connect(jid, pass, this._stropheConnectionCb.bind(this, callback), ...args);\n }\n\n /* eslint-enable max-params */\n\n /**\n * Handles {@link Strophe.Status} updates for the current connection.\n *\n * @param {function} targetCallback - The callback passed by the {@link XmppConnection} consumer to one of\n * the connect methods.\n * @param {Strophe.Status} status - The new connection status.\n * @param {*} args - The rest of the arguments passed by Strophe.\n * @private\n */\n _stropheConnectionCb(targetCallback, status, ...args) {\n this._status = status;\n\n let blockCallback = false;\n\n if (status === Strophe.Status.CONNECTED || status === Strophe.Status.ATTACHED) {\n this._maybeEnableStreamResume();\n\n // after connecting - immediately check whether shard changed,\n // we need this only when using websockets as bosh checks headers from every response\n if (this._usesWebsocket && this._oneSuccessfulConnect) {\n this._keepAliveAndCheckShard();\n }\n this._oneSuccessfulConnect = true;\n\n this._maybeStartWSKeepAlive();\n this._processDeferredIQs();\n this._resumeTask.cancel();\n this.ping.startInterval(this._options.pingOptions?.domain || this.domain);\n } else if (status === Strophe.Status.DISCONNECTED) {\n this.ping.stopInterval();\n\n // FIXME add RECONNECTING state instead of blocking the DISCONNECTED update\n blockCallback = this._tryResumingConnection();\n if (!blockCallback) {\n clearTimeout(this._wsKeepAlive);\n }\n }\n\n if (!blockCallback) {\n targetCallback(status, ...args);\n this.eventEmitter.emit(XmppConnection.Events.CONN_STATUS_CHANGED, status);\n }\n }\n\n /**\n * Clears the list of IQs and rejects deferred Promises with an error.\n *\n * @private\n */\n _clearDeferredIQs() {\n for (const deferred of this._deferredIQs) {\n deferred.reject(new Error('disconnect'));\n }\n this._deferredIQs = [];\n }\n\n /**\n * The method is meant to be used for testing. It's a shortcut for closing the WebSocket.\n *\n * @returns {void}\n */\n closeWebsocket() {\n if (this._stropheConn && this._stropheConn._proto) {\n this._stropheConn._proto._closeSocket();\n this._stropheConn._proto._onClose(null);\n }\n }\n\n /**\n * See {@link Strophe.Connection.disconnect}.\n *\n * @returns {void}\n */\n disconnect(...args) {\n this._resumeTask.cancel();\n clearTimeout(this._wsKeepAlive);\n this._clearDeferredIQs();\n this._stropheConn.disconnect(...args);\n }\n\n /**\n * See {@link Strophe.Connection.flush}.\n *\n * @returns {void}\n */\n flush(...args) {\n this._stropheConn.flush(...args);\n }\n\n /**\n * See {@link LastRequestTracker.getTimeSinceLastSuccess}.\n *\n * @returns {number|null}\n */\n getTimeSinceLastSuccess() {\n return this._rawInputTracker.getTimeSinceLastSuccess();\n }\n\n /**\n * See {@link LastRequestTracker.getLastFailedMessage}.\n *\n * @returns {string|null}\n */\n getLastFailedMessage() {\n return this._rawInputTracker.getLastFailedMessage();\n }\n\n /**\n * Requests a resume token from the server if enabled and all requirements are met.\n *\n * @private\n */\n _maybeEnableStreamResume() {\n if (!this._options.enableWebsocketResume) {\n\n return;\n }\n\n const { streamManagement } = this._stropheConn;\n\n if (!this.isUsingWebSocket) {\n logger.warn('Stream resume enabled, but WebSockets are not enabled');\n } else if (!streamManagement) {\n logger.warn('Stream resume enabled, but Strophe streamManagement plugin is not installed');\n } else if (!streamManagement.isSupported()) {\n logger.warn('Stream resume enabled, but XEP-0198 is not supported by the server');\n } else if (!streamManagement.getResumeToken()) {\n logger.info('Enabling XEP-0198 stream management');\n streamManagement.enable(/* resume */ true);\n }\n }\n\n /**\n * Starts the Websocket keep alive if enabled.\n *\n * @private\n * @returns {void}\n */\n _maybeStartWSKeepAlive() {\n const { websocketKeepAlive } = this._options;\n\n if (this._usesWebsocket && websocketKeepAlive > 0) {\n this._wsKeepAlive || logger.info(`WebSocket keep alive interval: ${websocketKeepAlive}ms`);\n clearTimeout(this._wsKeepAlive);\n\n const intervalWithJitter = /* base */ websocketKeepAlive + /* jitter */ (Math.random() * 60 * 1000);\n\n logger.debug(`Scheduling next WebSocket keep-alive in ${intervalWithJitter}ms`);\n\n this._wsKeepAlive = setTimeout(\n () => this._keepAliveAndCheckShard()\n .then(() => this._maybeStartWSKeepAlive()),\n intervalWithJitter);\n }\n }\n\n /**\n * Do a http GET to the shard and if shard change will throw an event.\n *\n * @private\n * @returns {Promise}\n */\n _keepAliveAndCheckShard() {\n const { shard, websocketKeepAliveUrl } = this._options;\n const url = websocketKeepAliveUrl ? websocketKeepAliveUrl\n : this.service.replace('wss://', 'https://').replace('ws://', 'http://');\n\n return fetch(url)\n .then(response => {\n\n // skips header checking if there is no info in options\n if (!shard) {\n return;\n }\n\n const responseShard = response.headers.get('x-jitsi-shard');\n\n if (responseShard !== shard) {\n logger.error(\n `Detected that shard changed from ${shard} to ${responseShard}`);\n this.eventEmitter.emit(XmppConnection.Events.CONN_SHARD_CHANGED);\n }\n })\n .catch(error => {\n logger.error(`Websocket Keep alive failed for url: ${url}`, { error });\n });\n }\n\n /**\n * Goes over the list of {@link DeferredSendIQ} tasks and sends them.\n *\n * @private\n * @returns {void}\n */\n _processDeferredIQs() {\n for (const deferred of this._deferredIQs) {\n if (deferred.iq) {\n clearTimeout(deferred.timeout);\n\n const timeLeft = Date.now() - deferred.start;\n\n this.sendIQ(\n deferred.iq,\n result => deferred.resolve(result),\n error => deferred.reject(error),\n timeLeft);\n }\n }\n\n this._deferredIQs = [];\n }\n\n /**\n * Send a stanza. This function is called to push data onto the send queue to go out over the wire.\n *\n * @param {Element|Strophe.Builder} stanza - The stanza to send.\n * @returns {void}\n */\n send(stanza) {\n if (!this.connected) {\n throw new Error('Not connected');\n }\n this._stropheConn.send(stanza);\n }\n\n /**\n * Helper function to send IQ stanzas.\n *\n * @param {Element} elem - The stanza to send.\n * @param {Function} callback - The callback function for a successful request.\n * @param {Function} errback - The callback function for a failed or timed out request. On timeout, the stanza will\n * be null.\n * @param {number} timeout - The time specified in milliseconds for a timeout to occur.\n * @returns {number} - The id used to send the IQ.\n */\n sendIQ(elem, callback, errback, timeout) {\n if (!this.connected) {\n errback('Not connected');\n\n return;\n }\n\n return this._stropheConn.sendIQ(elem, callback, errback, timeout);\n }\n\n /**\n * Sends an IQ immediately if connected or puts it on the send queue otherwise(in contrary to other send methods\n * which would fail immediately if disconnected).\n *\n * @param {Element} iq - The IQ to send.\n * @param {number} timeout - How long to wait for the response. The time when the connection is reconnecting is\n * included, which means that the IQ may never be sent and still fail with a timeout.\n */\n sendIQ2(iq, { timeout }) {\n return new Promise((resolve, reject) => {\n if (this.connected) {\n this.sendIQ(\n iq,\n result => resolve(result),\n error => reject(error),\n timeout);\n } else {\n const deferred = {\n iq,\n resolve,\n reject,\n start: Date.now(),\n timeout: setTimeout(() => {\n // clears the IQ on timeout and invalidates the deferred task\n deferred.iq = undefined;\n\n // Strophe calls with undefined on timeout\n reject(undefined);\n }, timeout)\n };\n\n this._deferredIQs.push(deferred);\n }\n });\n }\n\n /**\n * Called by the ping plugin when ping fails too many times.\n *\n * @returns {void}\n */\n _onPingErrorThresholdExceeded() {\n if (this.isUsingWebSocket) {\n logger.warn('Ping error threshold exceeded - killing the WebSocket');\n this.closeWebsocket();\n }\n }\n\n /**\n * Helper function to send presence stanzas. The main benefit is for sending presence stanzas for which you expect\n * a responding presence stanza with the same id (for example when leaving a chat room).\n *\n * @param {Element} elem - The stanza to send.\n * @param {Function} callback - The callback function for a successful request.\n * @param {Function} errback - The callback function for a failed or timed out request. On timeout, the stanza will\n * be null.\n * @param {number} timeout - The time specified in milliseconds for a timeout to occur.\n * @returns {number} - The id used to send the presence.\n */\n sendPresence(elem, callback, errback, timeout) {\n if (!this.connected) {\n errback('Not connected');\n\n return;\n }\n this._stropheConn.sendPresence(elem, callback, errback, timeout);\n }\n\n /**\n * The method gracefully closes the BOSH connection by using 'navigator.sendBeacon'.\n *\n * @returns {boolean} - true if the beacon was sent.\n */\n sendUnavailableBeacon() {\n if (!navigator.sendBeacon || this._stropheConn.disconnecting || !this._stropheConn.connected) {\n return false;\n }\n\n this._stropheConn._changeConnectStatus(Strophe.Status.DISCONNECTING);\n this._stropheConn.disconnecting = true;\n\n const body = this._stropheConn._proto._buildBody()\n .attrs({\n type: 'terminate'\n });\n const pres = $pres({\n xmlns: Strophe.NS.CLIENT,\n type: 'unavailable'\n });\n\n body.cnode(pres.tree());\n\n const res = navigator.sendBeacon(\n this.service.indexOf('https://') === -1 ? `https:${this.service}` : this.service,\n Strophe.serialize(body.tree()));\n\n logger.info(`Successfully send unavailable beacon ${res}`);\n\n this._stropheConn._proto._abortAllRequests();\n this._stropheConn._doDisconnect();\n\n return true;\n }\n\n /**\n * Tries to use stream management plugin to resume dropped XMPP connection. The streamManagement plugin clears\n * the resume token if any connection error occurs which would put it in unrecoverable state, so as long as\n * the token is present it means the connection can be resumed.\n *\n * @private\n * @returns {boolean}\n */\n _tryResumingConnection() {\n const { streamManagement } = this._stropheConn;\n const resumeToken = streamManagement && streamManagement.getResumeToken();\n\n if (resumeToken) {\n this._resumeTask.schedule();\n\n return true;\n }\n\n return false;\n }\n}\n","export enum JitsiTranscriptionStatus {\n /**\n * The transcription is on.\n */\n ON = 'on',\n\n /**\n * The transcription is off.\n */\n OFF = 'off'\n}\n\n// exported for backward compatibility\nexport const ON = JitsiTranscriptionStatus.ON;\nexport const OFF = JitsiTranscriptionStatus.OFF;\n","import { getLogger } from '@jitsi/logger';\nimport { $msg } from 'strophe.js';\n\nimport * as MediaType from '../../service/RTC/MediaType';\nimport XMPPEvents from '../../service/xmpp/XMPPEvents';\n\nconst logger = getLogger(__filename);\n\n/**\n * The AVModeration logic.\n */\nexport default class AVModeration {\n\n /**\n * Constructs AV moderation room.\n *\n * @param {ChatRoom} room the main room.\n */\n constructor(room) {\n this._xmpp = room.xmpp;\n\n this._mainRoom = room;\n\n this._moderationEnabledByType = {\n [MediaType.AUDIO]: false,\n [MediaType.VIDEO]: false\n };\n\n this._whitelistAudio = [];\n this._whitelistVideo = [];\n\n this._onMessage = this._onMessage.bind(this);\n this._xmpp.addListener(XMPPEvents.AV_MODERATION_RECEIVED, this._onMessage);\n }\n\n /**\n * Stops listening for events.\n */\n dispose() {\n this._xmpp.removeListener(XMPPEvents.AV_MODERATION_RECEIVED, this._onMessage);\n }\n\n /**\n * Whether AV moderation is supported on backend.\n *\n * @returns {boolean} whether AV moderation is supported on backend.\n */\n isSupported() {\n return Boolean(this._xmpp.avModerationComponentAddress);\n }\n\n /**\n * Enables or disables AV Moderation by sending a msg with command to the component.\n */\n enable(state, mediaType) {\n if (!this.isSupported() || !this._mainRoom.isModerator()) {\n logger.error(`Cannot enable:${state} AV moderation supported:${this.isSupported()}, \n moderator:${this._mainRoom.isModerator()}`);\n\n return;\n }\n\n if (state === this._moderationEnabledByType[mediaType]) {\n logger.warn(`Moderation already in state:${state} for mediaType:${mediaType}`);\n\n return;\n }\n\n // send the enable/disable message\n const msg = $msg({ to: this._xmpp.avModerationComponentAddress });\n\n msg.c('av_moderation', {\n enable: state,\n mediaType\n }).up();\n\n this._xmpp.connection.send(msg);\n }\n\n /**\n * Approves that a participant can unmute by sending a msg with its jid to the component.\n */\n approve(mediaType, jid) {\n if (!this.isSupported() || !this._mainRoom.isModerator()) {\n logger.error(`Cannot approve in AV moderation supported:${this.isSupported()}, \n moderator:${this._mainRoom.isModerator()}`);\n\n return;\n }\n\n // send a message to whitelist the jid and approve it to unmute\n const msg = $msg({ to: this._xmpp.avModerationComponentAddress });\n\n msg.c('av_moderation', {\n mediaType,\n jidToWhitelist: jid }).up();\n\n this._xmpp.connection.send(msg);\n }\n\n /**\n * Rejects that a participant can unmute by sending a msg with its jid to the component.\n */\n reject(mediaType, jid) {\n if (!this.isSupported() || !this._mainRoom.isModerator()) {\n logger.error(`Cannot reject in AV moderation supported:${this.isSupported()},\n moderator:${this._mainRoom.isModerator()}`);\n\n return;\n }\n\n // send a message to remove from whitelist the jid and reject it to unmute\n const msg = $msg({ to: this._xmpp.avModerationComponentAddress });\n\n msg.c('av_moderation', {\n mediaType,\n jidToBlacklist: jid\n }).up();\n\n this._xmpp.connection.send(msg);\n }\n\n /**\n * Receives av_moderation parsed messages as json.\n * @param obj the parsed json content of the message to process.\n * @private\n */\n _onMessage(obj) {\n const { removed, mediaType: media, enabled, approved, actor, whitelists: newWhitelists } = obj;\n\n if (newWhitelists) {\n const oldList = media === MediaType.AUDIO\n ? this._whitelistAudio\n : this._whitelistVideo;\n const newList = Array.isArray(newWhitelists[media]) ? newWhitelists[media] : [];\n\n if (removed) {\n oldList.filter(x => !newList.includes(x))\n .forEach(jid => this._xmpp.eventEmitter\n .emit(XMPPEvents.AV_MODERATION_PARTICIPANT_REJECTED, media, jid));\n } else {\n newList.filter(x => !oldList.includes(x))\n .forEach(jid => this._xmpp.eventEmitter\n .emit(XMPPEvents.AV_MODERATION_PARTICIPANT_APPROVED, media, jid));\n }\n\n if (media === MediaType.AUDIO) {\n this._whitelistAudio = newList;\n } else {\n this._whitelistVideo = newList;\n }\n } else if (enabled !== undefined && this._moderationEnabledByType[media] !== enabled) {\n this._moderationEnabledByType[media] = enabled;\n\n this._xmpp.eventEmitter.emit(XMPPEvents.AV_MODERATION_CHANGED, enabled, media, actor);\n } else if (removed) {\n this._xmpp.eventEmitter.emit(XMPPEvents.AV_MODERATION_REJECTED, media);\n } else if (approved) {\n this._xmpp.eventEmitter.emit(XMPPEvents.AV_MODERATION_APPROVED, media);\n }\n }\n}\n","import { getLogger } from '@jitsi/logger';\nimport { $msg } from 'strophe.js';\n\nimport XMPPEvents from '../../service/xmpp/XMPPEvents';\n\nconst FEATURE_KEY = 'features/breakout-rooms';\nconst BREAKOUT_ROOM_ACTIONS = {\n ADD: `${FEATURE_KEY}/add`,\n REMOVE: `${FEATURE_KEY}/remove`,\n MOVE_TO_ROOM: `${FEATURE_KEY}/move-to-room`\n};\nconst BREAKOUT_ROOM_EVENTS = {\n MOVE_TO_ROOM: `${FEATURE_KEY}/move-to-room`,\n UPDATE: `${FEATURE_KEY}/update`\n};\n\nconst logger = getLogger(__filename);\n\n/**\n * Helper class for handling breakout rooms.\n */\nexport default class BreakoutRooms {\n\n /**\n * Constructs lobby room.\n *\n * @param {ChatRoom} room the room we are in.\n */\n constructor(room) {\n this.room = room;\n\n this._handleMessages = this._handleMessages.bind(this);\n this.room.xmpp.addListener(XMPPEvents.BREAKOUT_ROOMS_EVENT, this._handleMessages);\n\n this._rooms = {};\n }\n\n /**\n * Stops listening for events.\n */\n dispose() {\n this.room.xmpp.removeListener(XMPPEvents.BREAKOUT_ROOMS_EVENT, this._handleMessages);\n }\n\n /**\n * Creates a breakout room with the given subject.\n *\n * @param {string} subject - A subject for the breakout room.\n */\n createBreakoutRoom(subject) {\n if (!this.isSupported() || !this.room.isModerator()) {\n logger.error(`Cannot create breakout room - supported:${this.isSupported()}, \n moderator:${this.room.isModerator()}`);\n\n return;\n }\n\n const message = {\n type: BREAKOUT_ROOM_ACTIONS.ADD,\n subject\n };\n\n this._sendMessage(message);\n }\n\n /**\n * Removes a breakout room.\n *\n * @param {string} breakoutRoomJid - JID of the room to be removed.\n */\n removeBreakoutRoom(breakoutRoomJid) {\n if (!this.isSupported() || !this.room.isModerator()) {\n logger.error(`Cannot remove breakout room - supported:${this.isSupported()}, \n moderator:${this.room.isModerator()}`);\n\n return;\n }\n\n const message = {\n type: BREAKOUT_ROOM_ACTIONS.REMOVE,\n breakoutRoomJid\n };\n\n this._sendMessage(message);\n }\n\n /**\n * Sends the given participant to the given room.\n *\n * @param {string} participantJid - JID of the participant to be sent to a room.\n * @param {string} roomJid - JID of the target room.\n */\n sendParticipantToRoom(participantJid, roomJid) {\n if (!this.isSupported() || !this.room.isModerator()) {\n logger.error(`Cannot send participant to room - supported:${this.isSupported()}, \n moderator:${this.room.isModerator()}`);\n\n return;\n }\n\n const message = {\n type: BREAKOUT_ROOM_ACTIONS.MOVE_TO_ROOM,\n participantJid,\n roomJid\n };\n\n this._sendMessage(message);\n }\n\n /**\n * Whether Breakout Rooms support is enabled in the backend or not.\n */\n isSupported() {\n return Boolean(this.getComponentAddress());\n }\n\n /**\n * Gets the address of the Breakout Rooms XMPP component.\n *\n * @returns The address of the component.\n */\n getComponentAddress() {\n return this.room.xmpp.breakoutRoomsComponentAddress;\n }\n\n /**\n * Stores if the current room is a breakout room.\n *\n * @param {boolean} isBreakoutRoom - Whether this room is a breakout room.\n */\n _setIsBreakoutRoom(isBreakoutRoom) {\n this._isBreakoutRoom = isBreakoutRoom;\n }\n\n /**\n * Checks whether this room is a breakout room.\n *\n * @returns True if the room is a breakout room, false otherwise.\n */\n isBreakoutRoom() {\n return this._isBreakoutRoom;\n }\n\n /**\n * Sets the main room JID associated with this breakout room. Only applies when\n * in a breakout room.\n *\n * @param {string} jid - The main room JID.\n */\n _setMainRoomJid(jid) {\n this._mainRoomJid = jid;\n }\n\n /**\n * Gets the main room's JID associated with this breakout room.\n *\n * @returns The main room JID.\n */\n getMainRoomJid() {\n return this._mainRoomJid;\n }\n\n /**\n * Handles a message for managing breakout rooms.\n *\n * @param {object} payload - Arbitrary data.\n */\n _handleMessages(payload) {\n switch (payload.event) {\n case BREAKOUT_ROOM_EVENTS.MOVE_TO_ROOM:\n this.room.eventEmitter.emit(XMPPEvents.BREAKOUT_ROOMS_MOVE_TO_ROOM, payload.roomJid);\n break;\n case BREAKOUT_ROOM_EVENTS.UPDATE: {\n this._rooms = payload.rooms;\n this.room.eventEmitter.emit(XMPPEvents.BREAKOUT_ROOMS_UPDATED, payload);\n break;\n }\n }\n }\n\n /**\n * Helper to send a breakout rooms message to the component.\n *\n * @param {Object} message - Command that needs to be sent.\n */\n _sendMessage(message) {\n const msg = $msg({ to: this.getComponentAddress() });\n\n msg.c('breakout_rooms', message).up();\n\n this.room.xmpp.connection.send(msg);\n }\n}\n","import { getLogger } from '@jitsi/logger';\nimport { $msg, Strophe } from 'strophe.js';\n\nimport XMPPEvents from '../../service/xmpp/XMPPEvents';\n\nconst logger = getLogger(__filename);\n\n/**\n * The command type for updating a lobby participant's e-mail address.\n *\n * @type {string}\n */\nconst EMAIL_COMMAND = 'email';\n\n/**\n * The Lobby room implementation. Setting a room to members only, joining the lobby room\n * approving or denying access to participants from the lobby room.\n */\nexport default class Lobby {\n\n /**\n * Constructs lobby room.\n *\n * @param {ChatRoom} room the main room.\n */\n constructor(room) {\n this.xmpp = room.xmpp;\n this.mainRoom = room;\n\n const maybeJoinLobbyRoom = this._maybeJoinLobbyRoom.bind(this);\n\n this.mainRoom.addEventListener(\n XMPPEvents.LOCAL_ROLE_CHANGED,\n maybeJoinLobbyRoom);\n\n this.mainRoom.addEventListener(\n XMPPEvents.MUC_MEMBERS_ONLY_CHANGED,\n maybeJoinLobbyRoom);\n\n this.mainRoom.addEventListener(\n XMPPEvents.ROOM_CONNECT_MEMBERS_ONLY_ERROR,\n jid => {\n this.lobbyRoomJid = jid;\n });\n }\n\n /**\n * Whether lobby is supported on backend.\n *\n * @returns {boolean} whether lobby is supported on backend.\n */\n isSupported() {\n return this.xmpp.lobbySupported;\n }\n\n /**\n * Enables lobby by setting the main room to be members only and joins the lobby chat room.\n *\n * @returns {Promise}\n */\n enable() {\n if (!this.isSupported()) {\n return Promise.reject(new Error('Lobby not supported!'));\n }\n\n return new Promise((resolve, reject) => {\n this.mainRoom.setMembersOnly(true, resolve, reject);\n });\n }\n\n /**\n * Disable lobby by setting the main room to be non members only and levaes the lobby chat room if joined.\n *\n * @returns {void}\n */\n disable() {\n if (!this.isSupported() || !this.mainRoom.isModerator()\n || !this.lobbyRoom || !this.mainRoom.membersOnlyEnabled) {\n return;\n }\n\n this.mainRoom.setMembersOnly(false);\n }\n\n /**\n * Broadcast a message to all participants in the lobby room\n * @param {Object} message The message to send\n *\n * @returns {void}\n */\n sendMessage(message) {\n if (this.lobbyRoom) {\n this.lobbyRoom.sendMessage(JSON.stringify(message), 'json-message');\n }\n }\n\n /**\n * Sends a private message to a participant in a lobby room.\n * @param {string} id The message to send\n * @param {Object} message The message to send\n *\n * @returns {void}\n */\n sendPrivateMessage(id, message) {\n if (this.lobbyRoom) {\n this.lobbyRoom.sendPrivateMessage(id, JSON.stringify(message), 'json-message');\n }\n }\n\n /**\n * Gets the local id for a participant in a lobby room.\n * This is used for lobby room private chat messages.\n *\n * @returns {string}\n */\n getLocalId() {\n if (this.lobbyRoom) {\n return Strophe.getResourceFromJid(this.lobbyRoom.myroomjid);\n }\n }\n\n /**\n * Adds a message listener to the lobby room.\n * @param {Function} listener The listener function,\n * called when a new message is received in the lobby room.\n *\n * @returns {Function} Handler returned to be able to remove it later.\n */\n addMessageListener(listener) {\n if (this.lobbyRoom) {\n const handler = (participantId, message) => {\n listener(message, Strophe.getResourceFromJid(participantId));\n };\n\n this.lobbyRoom.on(XMPPEvents.JSON_MESSAGE_RECEIVED, handler);\n\n return handler;\n }\n }\n\n /**\n * Remove a message handler from the lobby room.\n * @param {Function} handler The handler function to remove.\n *\n * @returns {void}\n */\n removeMessageHandler(handler) {\n if (this.lobbyRoom) {\n this.lobbyRoom.off(XMPPEvents.JSON_MESSAGE_RECEIVED, handler);\n }\n }\n\n /**\n * Leaves the lobby room.\n *\n * @returns {Promise}\n */\n leave() {\n if (this.lobbyRoom) {\n return this.lobbyRoom.leave()\n .then(() => {\n this.lobbyRoom = undefined;\n logger.info('Lobby room left!');\n })\n .catch(() => {}); // eslint-disable-line no-empty-function\n }\n\n return Promise.reject(\n new Error('The lobby has already been left'));\n }\n\n /**\n * We had received a jid for the lobby room.\n *\n * @param jid the lobby room jid to join.\n */\n setLobbyRoomJid(jid) {\n this.lobbyRoomJid = jid;\n }\n\n /**\n * Checks the state of mainRoom, lobbyRoom and current user role to decide whether to join lobby room.\n * @private\n */\n _maybeJoinLobbyRoom() {\n if (!this.isSupported()) {\n return;\n }\n\n const isModerator = this.mainRoom.joined && this.mainRoom.isModerator();\n\n if (isModerator && this.mainRoom.membersOnlyEnabled && !this.lobbyRoom) {\n // join the lobby\n this.join()\n .then(() => logger.info('Joined lobby room'))\n .catch(e => logger.error('Failed joining lobby', e));\n }\n }\n\n /**\n * Joins a lobby room setting display name and eventually avatar(using the email provided).\n *\n * @param {string} username is required.\n * @param {string} email is optional.\n * @returns {Promise} resolves once we join the room.\n */\n join(displayName, email) {\n const isModerator = this.mainRoom.joined && this.mainRoom.isModerator();\n\n if (!this.lobbyRoomJid) {\n return Promise.reject(new Error('Missing lobbyRoomJid, cannot join lobby room.'));\n }\n\n const roomName = Strophe.getNodeFromJid(this.lobbyRoomJid);\n const customDomain = Strophe.getDomainFromJid(this.lobbyRoomJid);\n\n this.lobbyRoom = this.xmpp.createRoom(\n roomName, {\n customDomain,\n disableDiscoInfo: true,\n disableFocus: true,\n enableLobby: false\n }\n );\n\n if (displayName) {\n // remove previously set nickname\n this.lobbyRoom.addOrReplaceInPresence('nick', {\n attributes: { xmlns: 'http://jabber.org/protocol/nick' },\n value: displayName\n });\n }\n\n if (isModerator) {\n this.lobbyRoom.addPresenceListener(EMAIL_COMMAND, (node, from) => {\n this.mainRoom.eventEmitter.emit(XMPPEvents.MUC_LOBBY_MEMBER_UPDATED, from, { email: node.value });\n });\n this.lobbyRoom.addEventListener(\n XMPPEvents.MUC_MEMBER_JOINED,\n // eslint-disable-next-line max-params\n (from, nick, role, isHiddenDomain, statsID, status, identity, botType, jid) => {\n // we need to ignore joins on lobby for participants that are already in the main room\n if (Object.values(this.mainRoom.members).find(m => m.jid === jid)) {\n return;\n }\n\n // Check if the user is a member if any breakout room.\n for (const room of Object.values(this.mainRoom.getBreakoutRooms()._rooms)) {\n if (Object.values(room.participants).find(p => p.jid === jid)) {\n return;\n }\n }\n\n // we emit the new event on the main room so we can propagate\n // events to the conference\n this.mainRoom.eventEmitter.emit(\n XMPPEvents.MUC_LOBBY_MEMBER_JOINED,\n Strophe.getResourceFromJid(from),\n nick,\n identity ? identity.avatar : undefined\n );\n });\n this.lobbyRoom.addEventListener(\n XMPPEvents.MUC_MEMBER_LEFT, from => {\n // we emit the new event on the main room so we can propagate\n // events to the conference\n this.mainRoom.eventEmitter.emit(\n XMPPEvents.MUC_LOBBY_MEMBER_LEFT,\n Strophe.getResourceFromJid(from)\n );\n });\n this.lobbyRoom.addEventListener(\n XMPPEvents.MUC_DESTROYED,\n () => {\n // let's make sure we emit that all lobby users had left\n Object.keys(this.lobbyRoom.members)\n .forEach(j => this.mainRoom.eventEmitter.emit(\n XMPPEvents.MUC_LOBBY_MEMBER_LEFT, Strophe.getResourceFromJid(j)));\n\n this.lobbyRoom.clean();\n\n this.lobbyRoom = undefined;\n logger.info('Lobby room left(destroyed)!');\n });\n } else {\n // this should only be handled by those waiting in lobby\n this.lobbyRoom.addEventListener(XMPPEvents.KICKED, isSelfPresence => {\n if (isSelfPresence) {\n this.mainRoom.eventEmitter.emit(XMPPEvents.MUC_DENIED_ACCESS);\n\n this.lobbyRoom.clean();\n\n return;\n }\n });\n\n // As there is still reference of the main room\n // the invite will be detected and addressed to its eventEmitter, even though we are not in it\n // the invite message should be received directly to the xmpp conn in general\n this.mainRoom.addEventListener(\n XMPPEvents.INVITE_MESSAGE_RECEIVED,\n (roomJid, from, txt, invitePassword) => {\n logger.debug(`Received approval to join ${roomJid} ${from} ${txt}`);\n if (roomJid === this.mainRoom.roomjid) {\n // we are now allowed, so let's join\n this.mainRoom.join(invitePassword);\n }\n });\n this.lobbyRoom.addEventListener(\n XMPPEvents.MUC_DESTROYED,\n (reason, jid) => {\n // we are receiving the jid of the main room\n // means we are invited to join, maybe lobby was disabled\n if (jid) {\n this.mainRoom.join();\n\n return;\n }\n\n this.lobbyRoom.clean();\n\n this.mainRoom.eventEmitter.emit(XMPPEvents.MUC_DESTROYED, reason);\n });\n\n // If participant retries joining shared password while waiting in the lobby\n // and succeeds make sure we leave lobby\n this.mainRoom.addEventListener(\n XMPPEvents.MUC_JOINED,\n () => {\n this.leave();\n });\n }\n\n return new Promise((resolve, reject) => {\n this.lobbyRoom.addEventListener(XMPPEvents.MUC_JOINED, () => {\n resolve();\n\n // send our email, as we do not handle this on initial presence we need a second one\n if (email && !isModerator) {\n this.lobbyRoom.addOrReplaceInPresence(EMAIL_COMMAND, { value: email })\n && this.lobbyRoom.sendPresence();\n }\n });\n this.lobbyRoom.addEventListener(XMPPEvents.ROOM_JOIN_ERROR, reject);\n this.lobbyRoom.addEventListener(XMPPEvents.ROOM_CONNECT_NOT_ALLOWED_ERROR, reject);\n this.lobbyRoom.addEventListener(XMPPEvents.ROOM_CONNECT_ERROR, reject);\n\n this.lobbyRoom.join();\n });\n\n }\n\n /**\n * Should be possible only for moderators.\n * @param id\n */\n denyAccess(id) {\n if (!this.isSupported() || !this.mainRoom.isModerator()) {\n return;\n }\n\n const jid = Object.keys(this.lobbyRoom.members)\n .find(j => Strophe.getResourceFromJid(j) === id);\n\n if (jid) {\n this.lobbyRoom.kick(jid);\n } else {\n logger.error(`Not found member for ${id} in lobby room.`);\n }\n }\n\n /**\n * Should be possible only for moderators.\n * @param id\n */\n approveAccess(id) {\n if (!this.isSupported() || !this.mainRoom.isModerator()) {\n return;\n }\n\n // Get the main room JID. If we are in a breakout room we'll use the main\n // room's lobby.\n let mainRoomJid = this.mainRoom.roomjid;\n\n if (this.mainRoom.getBreakoutRooms().isBreakoutRoom()) {\n mainRoomJid = this.mainRoom.getBreakoutRooms().getMainRoomJid();\n }\n\n const memberRoomJid = Object.keys(this.lobbyRoom.members)\n .find(j => Strophe.getResourceFromJid(j) === id);\n\n if (memberRoomJid) {\n const jid = this.lobbyRoom.members[memberRoomJid].jid;\n const msgToSend\n = $msg({ to: mainRoomJid })\n .c('x', { xmlns: 'http://jabber.org/protocol/muc#user' })\n .c('invite', { to: jid });\n\n this.xmpp.connection.sendIQ(msgToSend,\n () => { }, // eslint-disable-line no-empty-function\n e => {\n logger.error(`Error sending invite for ${jid}`, e);\n });\n } else {\n logger.error(`Not found member for ${memberRoomJid} in lobby room.`);\n }\n }\n}\n","import { jitsiLocalStorage } from '@jitsi/js-utils';\nimport { getLogger } from '@jitsi/logger';\n\nconst logger = getLogger(__filename);\n\nimport UsernameGenerator from '../util/UsernameGenerator';\n\nlet _callStatsUserName;\n\nlet _machineId;\n\n/**\n *\n */\nexport default {\n\n /**\n * The storage used to store the settings.\n */\n _storage: jitsiLocalStorage,\n\n /**\n * Initializes the Settings class.\n *\n * @param {Storage|undefined} externalStorage - Object that implements the Storage interface. This object will be\n * used for storing data instead of jitsiLocalStorage if specified.\n */\n init(externalStorage) {\n this._storage = externalStorage || jitsiLocalStorage;\n },\n\n /**\n * Returns fake username for callstats\n * @returns {string} fake username for callstats\n */\n get callStatsUserName() {\n if (!_callStatsUserName) {\n _callStatsUserName = this._storage.getItem('callStatsUserName');\n if (!_callStatsUserName) {\n _callStatsUserName = generateCallStatsUserName();\n this._storage.setItem('callStatsUserName', _callStatsUserName);\n }\n }\n\n return _callStatsUserName;\n },\n\n /**\n * Returns current machine id.\n * @returns {string} machine id\n */\n get machineId() {\n if (!_machineId) {\n const amDid = this._storage.getItem('billingId');\n\n _machineId = amDid || this._storage.getItem('jitsiMeetId');\n\n if (amDid) {\n this._storage.setItem('jitsiMeetId', amDid);\n } else if (!_machineId) {\n _machineId = generateJitsiMeetId();\n this._storage.setItem('jitsiMeetId', _machineId);\n }\n }\n\n return _machineId;\n },\n\n /**\n * Returns current session id.\n * @returns {string} current session id\n */\n get sessionId() {\n // We may update sessionId in localStorage from another JitsiConference\n // instance and that's why we should always re-read it.\n return this._storage.getItem('sessionId');\n },\n\n /**\n * Save current session id.\n * @param {string} sessionId session id\n */\n set sessionId(sessionId) {\n if (sessionId) {\n this._storage.setItem('sessionId', sessionId);\n } else {\n this._storage.removeItem('sessionId');\n }\n }\n};\n\n/**\n * Generate fake username for callstats.\n * @returns {string} fake random username\n */\nfunction generateCallStatsUserName() {\n const username = UsernameGenerator.generateUsername();\n\n logger.log('generated callstats uid', username);\n\n return username;\n}\n\n/**\n * Generate unique id.\n * @returns {string} random unique id\n */\nfunction generateJitsiMeetId() {\n const jitsiMeetId = generateUniqueId();\n\n logger.log('generated id', jitsiMeetId);\n\n return jitsiMeetId;\n}\n\n/**\n *\n */\nfunction generateUniqueId() {\n return _p8() + _p8() + _p8() + _p8();\n}\n\n/**\n *\n */\nfunction _p8() {\n return `${Math.random().toString(16)}000000000`.substr(2, 8);\n}\n","/* global $ */\n\nimport { getLogger } from '@jitsi/logger';\nimport { $iq, Strophe } from 'strophe.js';\n\nimport Settings from '../settings/Settings';\n\nconst AuthenticationEvents\n = require('../../service/authentication/AuthenticationEvents');\nconst XMPPEvents = require('../../service/xmpp/XMPPEvents');\nconst GlobalOnErrorHandler = require('../util/GlobalOnErrorHandler');\n\nconst logger = getLogger(__filename);\n\n/**\n *\n * @param step\n */\nfunction createExpBackoffTimer(step) {\n let count = 1;\n\n return function(reset) {\n // Reset call\n if (reset) {\n count = 1;\n\n return;\n }\n\n // Calculate next timeout\n const timeout = Math.pow(2, count - 1);\n\n count += 1;\n\n return timeout * step;\n };\n}\n\n/* eslint-disable max-params */\n\n/**\n *\n * @param roomName\n * @param xmpp\n * @param emitter\n * @param options\n */\nexport default function Moderator(roomName, xmpp, emitter, options) {\n this.roomName = roomName;\n this.xmppService = xmpp;\n this.getNextTimeout = createExpBackoffTimer(1000);\n this.getNextErrorTimeout = createExpBackoffTimer(1000);\n\n // External authentication stuff\n this.externalAuthEnabled = false;\n this.options = options;\n\n // Whether SIP gateway (jigasi) support is enabled. This is set\n // based on conference properties received in presence.\n this.sipGatewayEnabled = false;\n\n this.eventEmitter = emitter;\n\n this.connection = this.xmppService.connection;\n\n // FIXME: Message listener that talks to POPUP window\n /**\n *\n * @param event\n */\n function listener(event) {\n if (event.data && event.data.sessionId) {\n if (event.origin !== window.location.origin) {\n logger.warn(\n `Ignoring sessionId from different origin: ${\n event.origin}`);\n\n return;\n }\n Settings.sessionId = event.data.sessionId;\n\n // After popup is closed we will authenticate\n }\n }\n\n // Register\n if (window.addEventListener) {\n window.addEventListener('message', listener, false);\n } else {\n window.attachEvent('onmessage', listener);\n }\n}\n\n/* eslint-enable max-params */\n\nModerator.prototype.isExternalAuthEnabled = function() {\n return this.externalAuthEnabled;\n};\n\nModerator.prototype.isSipGatewayEnabled = function() {\n return this.sipGatewayEnabled;\n};\n\nModerator.prototype.onMucMemberLeft = function(jid) {\n const resource = Strophe.getResourceFromJid(jid);\n\n if (resource === 'focus') {\n logger.info(\n 'Focus has left the room - leaving conference');\n this.eventEmitter.emit(XMPPEvents.FOCUS_LEFT);\n }\n};\n\nModerator.prototype.setFocusUserJid = function(focusJid) {\n if (!this.focusUserJid) {\n this.focusUserJid = focusJid;\n logger.info(`Focus jid set to: ${this.focusUserJid}`);\n }\n};\n\nModerator.prototype.getFocusUserJid = function() {\n return this.focusUserJid;\n};\n\nModerator.prototype.getFocusComponent = function() {\n // Get focus component address\n let focusComponent = this.options.connection.hosts.focus;\n\n // If not specified use default: 'focus.domain'\n\n if (!focusComponent) {\n focusComponent = `focus.${this.options.connection.hosts.domain}`;\n }\n\n return focusComponent;\n};\n\nModerator.prototype.createConferenceIq = function() {\n // Generate create conference IQ\n const elem = $iq({ to: this.getFocusComponent(),\n type: 'set' });\n\n // Session Id used for authentication\n const { sessionId } = Settings;\n const machineUID = Settings.machineId;\n const config = this.options.conference;\n\n logger.info(`Session ID: ${sessionId} machine UID: ${machineUID}`);\n\n elem.c('conference', {\n xmlns: 'http://jitsi.org/protocol/focus',\n room: this.roomName,\n 'machine-uid': machineUID\n });\n\n if (sessionId) {\n elem.attrs({ 'session-id': sessionId });\n }\n\n elem.c(\n 'property', {\n name: 'disableRtx',\n value: Boolean(config.disableRtx)\n }).up();\n\n if (config.audioPacketDelay !== undefined) {\n elem.c(\n 'property', {\n name: 'audioPacketDelay',\n value: config.audioPacketDelay\n }).up();\n }\n if (config.startBitrate) {\n elem.c(\n 'property', {\n name: 'startBitrate',\n value: config.startBitrate\n }).up();\n }\n if (config.minBitrate) {\n elem.c(\n 'property', {\n name: 'minBitrate',\n value: config.minBitrate\n }).up();\n }\n\n if (this.options.conference.startAudioMuted !== undefined) {\n elem.c(\n 'property', {\n name: 'startAudioMuted',\n value: this.options.conference.startAudioMuted\n }).up();\n }\n if (this.options.conference.startVideoMuted !== undefined) {\n elem.c(\n 'property', {\n name: 'startVideoMuted',\n value: this.options.conference.startVideoMuted\n }).up();\n }\n\n // this flag determines whether the bridge will include this call in its\n // rtcstats reporting or not. If the site admin hasn't set the flag in\n // config.js, then the client defaults to false (see\n // react/features/rtcstats/functions.js in jitsi-meet). The server-side\n // components default to true to match the pre-existing behavior so we only\n // signal if false.\n const rtcstatsEnabled = this.options.conference?.analytics?.rtcstatsEnabled ?? false;\n\n if (!rtcstatsEnabled) {\n elem.c(\n 'property', {\n name: 'rtcstatsEnabled',\n value: rtcstatsEnabled\n }).up();\n }\n\n const { callStatsID, callStatsSecret, disableThirdPartyRequests, enableCallStats } = this.options.conference;\n const callstatsDisabled = !callStatsID || !callStatsSecret || !enableCallStats\n\n // Even though AppID and AppSecret may be specified, the integration\n // of callstats.io may be disabled because of globally-disallowed\n // requests to any third parties.\n || disableThirdPartyRequests === true;\n\n // since the default is true across all the server-side components, only signal if false.\n if (callstatsDisabled) {\n elem.c(\n 'property', {\n name: 'callstatsEnabled',\n value: !callstatsDisabled\n }).up();\n }\n elem.up();\n\n return elem;\n};\n\n\nModerator.prototype.parseSessionId = function(resultIq) {\n // eslint-disable-next-line newline-per-chained-call\n const sessionId = $(resultIq).find('conference').attr('session-id');\n\n if (sessionId) {\n logger.info(`Received sessionId: ${sessionId}`);\n Settings.sessionId = sessionId;\n }\n};\n\nModerator.prototype.parseConfigOptions = function(resultIq) {\n // eslint-disable-next-line newline-per-chained-call\n this.setFocusUserJid($(resultIq).find('conference').attr('focusjid'));\n\n const authenticationEnabled\n = $(resultIq).find(\n '>conference>property'\n + '[name=\\'authentication\\'][value=\\'true\\']').length > 0;\n\n logger.info(`Authentication enabled: ${authenticationEnabled}`);\n\n this.externalAuthEnabled = $(resultIq).find(\n '>conference>property'\n + '[name=\\'externalAuth\\'][value=\\'true\\']').length > 0;\n\n logger.info(\n `External authentication enabled: ${this.externalAuthEnabled}`);\n\n if (!this.externalAuthEnabled) {\n // We expect to receive sessionId in 'internal' authentication mode\n this.parseSessionId(resultIq);\n }\n\n // eslint-disable-next-line newline-per-chained-call\n const authIdentity = $(resultIq).find('>conference').attr('identity');\n\n this.eventEmitter.emit(AuthenticationEvents.IDENTITY_UPDATED,\n authenticationEnabled, authIdentity);\n\n // Check if jicofo has jigasi support enabled.\n if ($(resultIq).find(\n '>conference>property'\n + '[name=\\'sipGatewayEnabled\\'][value=\\'true\\']').length) {\n this.sipGatewayEnabled = true;\n }\n\n logger.info(`Sip gateway enabled: ${this.sipGatewayEnabled}`);\n};\n\n// FIXME We need to show the fact that we're waiting for the focus to the user\n// (or that the focus is not available)\n/**\n * Allocates the conference focus.\n *\n * @param {Function} callback - the function to be called back upon the\n * successful allocation of the conference focus\n * @returns {Promise} - Resolved when Jicofo allows to join the room. It's never\n * rejected and it'll keep on pinging Jicofo forever.\n */\nModerator.prototype.allocateConferenceFocus = function() {\n return new Promise(resolve => {\n // Try to use focus user JID from the config\n this.setFocusUserJid(this.options.connection.focusUserJid);\n\n // Send create conference IQ\n this.connection.sendIQ(\n this.createConferenceIq(),\n result => this._allocateConferenceFocusSuccess(result, resolve),\n error => this._allocateConferenceFocusError(error, resolve));\n\n // XXX We're pressed for time here because we're beginning a complex\n // and/or lengthy conference-establishment process which supposedly\n // involves multiple RTTs. We don't have the time to wait for Strophe to\n // decide to send our IQ.\n this.connection.flush();\n });\n};\n\n/**\n * Invoked by {@link #allocateConferenceFocus} upon its request receiving an\n * error result.\n *\n * @param error - the error result of the request that\n * {@link #allocateConferenceFocus} sent\n * @param {Function} callback - the function to be called back upon the\n * successful allocation of the conference focus\n */\nModerator.prototype._allocateConferenceFocusError = function(error, callback) {\n // If the session is invalid, remove and try again without session ID to get\n // a new one\n const invalidSession\n = $(error).find('>error>session-invalid').length\n || $(error).find('>error>not-acceptable').length;\n\n if (invalidSession) {\n logger.info('Session expired! - removing');\n Settings.sessionId = undefined;\n }\n if ($(error).find('>error>graceful-shutdown').length) {\n this.eventEmitter.emit(XMPPEvents.GRACEFUL_SHUTDOWN);\n\n return;\n }\n\n // Check for error returned by the reservation system\n const reservationErr = $(error).find('>error>reservation-error');\n\n if (reservationErr.length) {\n // Trigger error event\n const errorCode = reservationErr.attr('error-code');\n const errorTextNode = $(error).find('>error>text');\n let errorMsg;\n\n if (errorTextNode) {\n errorMsg = errorTextNode.text();\n }\n this.eventEmitter.emit(\n XMPPEvents.RESERVATION_ERROR,\n errorCode,\n errorMsg);\n\n return;\n }\n\n // Not authorized to create new room\n if ($(error).find('>error>not-authorized').length) {\n logger.warn('Unauthorized to start the conference', error);\n const toDomain = Strophe.getDomainFromJid(error.getAttribute('to'));\n\n if (toDomain !== this.options.connection.hosts.anonymousdomain) {\n // FIXME \"is external\" should come either from the focus or\n // config.js\n this.externalAuthEnabled = true;\n }\n this.eventEmitter.emit(XMPPEvents.AUTHENTICATION_REQUIRED);\n\n return;\n }\n const waitMs = this.getNextErrorTimeout();\n const errmsg = `Focus error, retry after ${waitMs}`;\n\n GlobalOnErrorHandler.callErrorHandler(new Error(errmsg));\n logger.error(errmsg, error);\n\n // Show message\n const focusComponent = this.getFocusComponent();\n const retrySec = waitMs / 1000;\n\n // FIXME: message is duplicated ? Do not show in case of session invalid\n // which means just a retry\n\n if (!invalidSession) {\n this.eventEmitter.emit(\n XMPPEvents.FOCUS_DISCONNECTED,\n focusComponent,\n retrySec);\n }\n\n // Reset response timeout\n this.getNextTimeout(true);\n window.setTimeout(\n () => this.allocateConferenceFocus().then(callback),\n waitMs);\n};\n\n/**\n * Invoked by {@link #allocateConferenceFocus} upon its request receiving a\n * success (i.e. non-error) result.\n *\n * @param result - the success (i.e. non-error) result of the request that\n * {@link #allocateConferenceFocus} sent\n * @param {Function} callback - the function to be called back upon the\n * successful allocation of the conference focus\n */\nModerator.prototype._allocateConferenceFocusSuccess = function(\n result,\n callback) {\n // Setup config options\n this.parseConfigOptions(result);\n\n // Reset the error timeout (because we haven't failed here).\n this.getNextErrorTimeout(true);\n\n // eslint-disable-next-line newline-per-chained-call\n if ($(result).find('conference').attr('ready') === 'true') {\n // Reset the non-error timeout (because we've succeeded here).\n this.getNextTimeout(true);\n\n // Exec callback\n callback();\n } else {\n const waitMs = this.getNextTimeout();\n\n logger.info(`Waiting for the focus... ${waitMs}`);\n window.setTimeout(\n () => this.allocateConferenceFocus().then(callback),\n waitMs);\n }\n};\n\nModerator.prototype.authenticate = function() {\n return new Promise((resolve, reject) => {\n this.connection.sendIQ(\n this.createConferenceIq(),\n result => {\n this.parseSessionId(result);\n resolve();\n },\n errorIq => reject({\n error: $(errorIq).find('iq>error :first')\n .prop('tagName'),\n message: $(errorIq).find('iq>error>text')\n .text()\n })\n );\n });\n};\n\nModerator.prototype.getLoginUrl = function(urlCallback, failureCallback) {\n this._getLoginUrl(/* popup */ false, urlCallback, failureCallback);\n};\n\n/**\n *\n * @param {boolean} popup false for {@link Moderator#getLoginUrl} or true for\n * {@link Moderator#getPopupLoginUrl}\n * @param urlCb\n * @param failureCb\n */\nModerator.prototype._getLoginUrl = function(popup, urlCb, failureCb) {\n const iq = $iq({ to: this.getFocusComponent(),\n type: 'get' });\n const attrs = {\n xmlns: 'http://jitsi.org/protocol/focus',\n room: this.roomName,\n 'machine-uid': Settings.machineId\n };\n let str = 'auth url'; // for logger\n\n if (popup) {\n attrs.popup = true;\n str = `POPUP ${str}`;\n }\n iq.c('login-url', attrs);\n\n /**\n * Implements a failure callback which reports an error message and an error\n * through (1) GlobalOnErrorHandler, (2) logger, and (3) failureCb.\n *\n * @param {string} errmsg the error messsage to report\n * @param {*} error the error to report (in addition to errmsg)\n */\n function reportError(errmsg, err) {\n GlobalOnErrorHandler.callErrorHandler(new Error(errmsg));\n logger.error(errmsg, err);\n failureCb(err);\n }\n this.connection.sendIQ(\n iq,\n result => {\n // eslint-disable-next-line newline-per-chained-call\n let url = $(result).find('login-url').attr('url');\n\n url = decodeURIComponent(url);\n if (url) {\n logger.info(`Got ${str}: ${url}`);\n urlCb(url);\n } else {\n reportError(`Failed to get ${str} from the focus`, result);\n }\n },\n reportError.bind(undefined, `Get ${str} error`)\n );\n};\n\nModerator.prototype.getPopupLoginUrl = function(urlCallback, failureCallback) {\n this._getLoginUrl(/* popup */ true, urlCallback, failureCallback);\n};\n\nModerator.prototype.logout = function(callback) {\n const iq = $iq({ to: this.getFocusComponent(),\n type: 'set' });\n const { sessionId } = Settings;\n\n if (!sessionId) {\n callback();\n\n return;\n }\n iq.c('logout', {\n xmlns: 'http://jitsi.org/protocol/focus',\n 'session-id': sessionId\n });\n this.connection.sendIQ(\n iq,\n result => {\n // eslint-disable-next-line newline-per-chained-call\n let logoutUrl = $(result).find('logout').attr('logout-url');\n\n if (logoutUrl) {\n logoutUrl = decodeURIComponent(logoutUrl);\n }\n logger.info(`Log out OK, url: ${logoutUrl}`, result);\n Settings.sessionId = undefined;\n callback(logoutUrl);\n },\n error => {\n const errmsg = 'Logout error';\n\n GlobalOnErrorHandler.callErrorHandler(new Error(errmsg));\n logger.error(errmsg, error);\n }\n );\n};\n","/* global $ */\n\nimport { getLogger } from '@jitsi/logger';\nimport isEqual from 'lodash.isequal';\nimport { $iq, $msg, $pres, Strophe } from 'strophe.js';\n\nimport * as JitsiTranscriptionStatus from '../../JitsiTranscriptionStatus';\nimport * as MediaType from '../../service/RTC/MediaType';\nimport VideoType from '../../service/RTC/VideoType';\nimport XMPPEvents from '../../service/xmpp/XMPPEvents';\nimport GlobalOnErrorHandler from '../util/GlobalOnErrorHandler';\nimport Listenable from '../util/Listenable';\n\nimport AVModeration from './AVModeration';\nimport BreakoutRooms from './BreakoutRooms';\nimport Lobby from './Lobby';\nimport XmppConnection from './XmppConnection';\nimport Moderator from './moderator';\n\nconst logger = getLogger(__filename);\n\nexport const parser = {\n packet2JSON(xmlElement, nodes) {\n for (const child of Array.from(xmlElement.children)) {\n const node = {\n attributes: {},\n children: [],\n tagName: child.tagName\n };\n\n for (const attr of Array.from(child.attributes)) {\n node.attributes[attr.name] = attr.value;\n }\n const text = Strophe.getText(child);\n\n if (text) {\n // Using Strophe.getText will do work for traversing all direct\n // child text nodes but returns an escaped value, which is not\n // desirable at this point.\n node.value = Strophe.xmlunescape(text);\n }\n nodes.push(node);\n this.packet2JSON(child, node.children);\n }\n },\n json2packet(nodes, packet) {\n for (let i = 0; i < nodes.length; i++) {\n const node = nodes[i];\n\n if (node) {\n packet.c(node.tagName, node.attributes);\n if (node.value) {\n packet.t(node.value);\n }\n if (node.children) {\n this.json2packet(node.children, packet);\n }\n packet.up();\n }\n }\n\n // packet.up();\n }\n};\n\n/**\n * Returns array of JS objects from the presence JSON associated with the passed\n / nodeName\n * @param pres the presence JSON\n * @param nodeName the name of the node (videomuted, audiomuted, etc)\n */\nexport function filterNodeFromPresenceJSON(pres, nodeName) {\n const res = [];\n\n for (let i = 0; i < pres.length; i++) {\n if (pres[i].tagName === nodeName) {\n res.push(pres[i]);\n }\n }\n\n return res;\n}\n\n// XXX As ChatRoom constructs XMPP stanzas and Strophe is build around the idea\n// of chaining function calls, allow long function call chains.\n/* eslint-disable newline-per-chained-call */\n\n/**\n * Array of affiliations that are allowed in members only room.\n * @type {string[]}\n */\nconst MEMBERS_AFFILIATIONS = [ 'owner', 'admin', 'member' ];\n\n/**\n *\n */\nexport default class ChatRoom extends Listenable {\n\n /* eslint-disable max-params */\n\n /**\n *\n * @param {XmppConnection} connection - The XMPP connection instance.\n * @param jid\n * @param password\n * @param XMPP\n * @param options\n * @param {boolean} options.disableFocus - when set to {@code false} will\n * not invite Jicofo into the room.\n * @param {boolean} options.disableDiscoInfo - when set to {@code false} will skip disco info.\n * This is intended to be used only for lobby rooms.\n * @param {boolean} options.enableLobby - when set to {@code false} will skip creating lobby room.\n * @param {boolean} options.hiddenFromRecorderFeatureEnabled - when set to {@code true} we will check identity tag\n * for node presence.\n */\n constructor(connection, jid, password, XMPP, options) {\n super();\n this.xmpp = XMPP;\n this.connection = connection;\n this.roomjid = Strophe.getBareJidFromJid(jid);\n this.myroomjid = jid;\n this.password = password;\n this.replaceParticipant = false;\n logger.info(`Joined MUC as ${this.myroomjid}`);\n this.members = {};\n this.presMap = {};\n this.presHandlers = {};\n this._removeConnListeners = [];\n this.joined = false;\n this.inProgressEmitted = false;\n this.role = null;\n this.focusMucJid = null;\n this.noBridgeAvailable = false;\n this.options = options || {};\n this.moderator\n = new Moderator(this.roomjid, this.xmpp, this.eventEmitter, {\n connection: this.xmpp.options,\n conference: this.options\n });\n if (typeof this.options.enableLobby === 'undefined' || this.options.enableLobby) {\n this.lobby = new Lobby(this);\n }\n this.avModeration = new AVModeration(this);\n this.breakoutRooms = new BreakoutRooms(this);\n this.initPresenceMap(options);\n this.lastPresences = {};\n this.phoneNumber = null;\n this.phonePin = null;\n this.connectionTimes = {};\n this.participantPropertyListener = null;\n\n this.locked = false;\n this.transcriptionStatus = JitsiTranscriptionStatus.OFF;\n }\n\n /* eslint-enable max-params */\n\n /**\n *\n */\n initPresenceMap(options = {}) {\n this.presMap.to = this.myroomjid;\n this.presMap.xns = 'http://jabber.org/protocol/muc';\n this.presMap.nodes = [];\n\n if (options.statsId) {\n this.presMap.nodes.push({\n 'tagName': 'stats-id',\n 'value': options.statsId\n });\n }\n\n this.presenceUpdateTime = Date.now();\n }\n\n /**\n * Joins the chat room.\n * @param {string} password - Password to unlock room on joining.\n * @returns {Promise} - resolved when join completes. At the time of this\n * writing it's never rejected.\n */\n join(password, replaceParticipant) {\n this.password = password;\n this.replaceParticipant = replaceParticipant;\n\n return new Promise(resolve => {\n this.options.disableFocus\n && logger.info(`Conference focus disabled for ${this.roomjid}`);\n\n const preJoin\n = this.options.disableFocus\n ? Promise.resolve()\n : this.moderator.allocateConferenceFocus();\n\n preJoin.then(() => {\n this.sendPresence(true);\n this._removeConnListeners.push(\n this.connection.addEventListener(\n XmppConnection.Events.CONN_STATUS_CHANGED,\n this.onConnStatusChanged.bind(this))\n );\n resolve();\n });\n });\n }\n\n /**\n *\n * @param fromJoin - Whether this is initial presence to join the room.\n */\n sendPresence(fromJoin) {\n const to = this.presMap.to;\n\n if (!this.connection || !this.connection.connected || !to || (!this.joined && !fromJoin)) {\n // Too early to send presence - not initialized\n return;\n }\n\n const pres = $pres({ to });\n\n // xep-0045 defines: \"including in the initial presence stanza an empty\n // element qualified by the 'http://jabber.org/protocol/muc'\n // namespace\" and subsequent presences should not include that or it can\n // be considered as joining, and server can send us the message history\n // for the room on every presence\n if (fromJoin) {\n if (this.replaceParticipant) {\n pres.c('flip_device').up();\n }\n\n pres.c('x', { xmlns: this.presMap.xns });\n\n if (this.password) {\n pres.c('password').t(this.password).up();\n }\n if (this.options.billingId) {\n pres.c('billingid').t(this.options.billingId).up();\n }\n\n pres.up();\n }\n\n parser.json2packet(this.presMap.nodes, pres);\n\n // we store time we last synced presence state\n this.presenceSyncTime = Date.now();\n\n this.connection.send(pres);\n if (fromJoin) {\n // XXX We're pressed for time here because we're beginning a complex\n // and/or lengthy conference-establishment process which supposedly\n // involves multiple RTTs. We don't have the time to wait for\n // Strophe to decide to send our IQ.\n this.connection.flush();\n }\n }\n\n /**\n * Sends the presence unavailable, signaling the server\n * we want to leave the room.\n */\n doLeave() {\n logger.log('do leave', this.myroomjid);\n const pres = $pres({ to: this.myroomjid,\n type: 'unavailable' });\n\n this.presMap.length = 0;\n\n // XXX Strophe is asynchronously sending by default. Unfortunately, that\n // means that there may not be enough time to send the unavailable\n // presence. Switching Strophe to synchronous sending is not much of an\n // option because it may lead to a noticeable delay in navigating away\n // from the current location. As a compromise, we will try to increase\n // the chances of sending the unavailable presence within the short time\n // span that we have upon unloading by invoking flush() on the\n // connection. We flush() once before sending/queuing the unavailable\n // presence in order to attemtp to have the unavailable presence at the\n // top of the send queue. We flush() once more after sending/queuing the\n // unavailable presence in order to attempt to have it sent as soon as\n // possible.\n // FIXME do not use Strophe.Connection in the ChatRoom directly\n !this.connection.isUsingWebSocket && this.connection.flush();\n this.connection.send(pres);\n this.connection.flush();\n }\n\n /**\n *\n */\n discoRoomInfo() {\n // https://xmpp.org/extensions/xep-0045.html#disco-roominfo\n\n const getInfo\n = $iq({\n type: 'get',\n to: this.roomjid\n })\n .c('query', { xmlns: Strophe.NS.DISCO_INFO });\n\n this.connection.sendIQ(getInfo, result => {\n const locked\n = $(result).find('>query>feature[var=\"muc_passwordprotected\"]')\n .length\n === 1;\n\n if (locked !== this.locked) {\n this.eventEmitter.emit(XMPPEvents.MUC_LOCK_CHANGED, locked);\n this.locked = locked;\n }\n\n const meetingIdValEl\n = $(result).find('>query>x[type=\"result\"]>field[var=\"muc#roominfo_meetingId\"]>value');\n\n if (meetingIdValEl.length) {\n this.setMeetingId(meetingIdValEl.text());\n } else {\n logger.warn('No meeting ID from backend');\n }\n\n const membersOnly = $(result).find('>query>feature[var=\"muc_membersonly\"]').length === 1;\n\n const lobbyRoomField\n = $(result).find('>query>x[type=\"result\"]>field[var=\"muc#roominfo_lobbyroom\"]>value');\n\n if (this.lobby) {\n this.lobby.setLobbyRoomJid(lobbyRoomField && lobbyRoomField.length ? lobbyRoomField.text() : undefined);\n }\n\n const isBreakoutField\n = $(result).find('>query>x[type=\"result\"]>field[var=\"muc#roominfo_isbreakout\"]>value');\n const isBreakoutRoom = Boolean(isBreakoutField?.text());\n\n this.breakoutRooms._setIsBreakoutRoom(isBreakoutRoom);\n\n const breakoutMainRoomField\n = $(result).find('>query>x[type=\"result\"]>field[var=\"muc#roominfo_breakout_main_room\"]>value');\n\n if (breakoutMainRoomField?.length) {\n this.breakoutRooms._setMainRoomJid(breakoutMainRoomField.text());\n }\n\n if (membersOnly !== this.membersOnlyEnabled) {\n this.membersOnlyEnabled = membersOnly;\n this.eventEmitter.emit(XMPPEvents.MUC_MEMBERS_ONLY_CHANGED, membersOnly);\n }\n\n }, error => {\n GlobalOnErrorHandler.callErrorHandler(error);\n logger.error('Error getting room info: ', error);\n });\n }\n\n /**\n * Sets the meeting unique Id (received from the backend).\n *\n * @param {string} meetingId - The new meetings id.\n * @returns {void}\n */\n setMeetingId(meetingId) {\n if (this.meetingId !== meetingId) {\n if (this.meetingId) {\n logger.warn(`Meeting Id changed from:${this.meetingId} to:${meetingId}`);\n }\n this.meetingId = meetingId;\n this.eventEmitter.emit(XMPPEvents.MEETING_ID_SET, meetingId);\n }\n }\n\n /**\n *\n */\n createNonAnonymousRoom() {\n // http://xmpp.org/extensions/xep-0045.html#createroom-reserved\n\n if (this.options.disableDiscoInfo) {\n return;\n }\n\n const getForm = $iq({ type: 'get',\n to: this.roomjid })\n .c('query', { xmlns: 'http://jabber.org/protocol/muc#owner' })\n .c('x', { xmlns: 'jabber:x:data',\n type: 'submit' });\n\n this.connection.sendIQ(getForm, form => {\n if (!$(form).find(\n '>query>x[xmlns=\"jabber:x:data\"]'\n + '>field[var=\"muc#roomconfig_whois\"]').length) {\n const errmsg = 'non-anonymous rooms not supported';\n\n GlobalOnErrorHandler.callErrorHandler(new Error(errmsg));\n logger.error(errmsg);\n\n return;\n }\n\n const formSubmit = $iq({ to: this.roomjid,\n type: 'set' })\n .c('query', { xmlns: 'http://jabber.org/protocol/muc#owner' });\n\n formSubmit.c('x', { xmlns: 'jabber:x:data',\n type: 'submit' });\n\n formSubmit.c('field', { 'var': 'FORM_TYPE' })\n .c('value')\n .t('http://jabber.org/protocol/muc#roomconfig').up().up();\n\n formSubmit.c('field', { 'var': 'muc#roomconfig_whois' })\n .c('value').t('anyone').up().up();\n\n this.connection.sendIQ(formSubmit);\n\n }, error => {\n GlobalOnErrorHandler.callErrorHandler(error);\n logger.error('Error getting room configuration form: ', error);\n });\n }\n\n /**\n * Handles Xmpp Connection status updates.\n *\n * @param {Strophe.Status} status - The Strophe connection status.\n */\n onConnStatusChanged(status) {\n // Send cached presence when the XMPP connection is re-established.\n if (status === XmppConnection.Status.CONNECTED) {\n this.sendPresence();\n }\n }\n\n /**\n *\n * @param pres\n */\n onPresence(pres) {\n const from = pres.getAttribute('from');\n const member = {};\n const statusEl = pres.getElementsByTagName('status')[0];\n\n if (statusEl) {\n member.status = statusEl.textContent || '';\n }\n let hasStatusUpdate = false;\n let hasVersionUpdate = false;\n const xElement\n = pres.getElementsByTagNameNS(\n 'http://jabber.org/protocol/muc#user', 'x')[0];\n const mucUserItem\n = xElement && xElement.getElementsByTagName('item')[0];\n\n member.isReplaceParticipant\n = pres.getElementsByTagName('flip_device').length;\n\n member.affiliation\n = mucUserItem && mucUserItem.getAttribute('affiliation');\n member.role = mucUserItem && mucUserItem.getAttribute('role');\n\n // Focus recognition\n const jid = mucUserItem && mucUserItem.getAttribute('jid');\n\n member.jid = jid;\n member.isFocus\n = jid && jid.indexOf(`${this.moderator.getFocusUserJid()}/`) === 0;\n member.isHiddenDomain\n = jid && jid.indexOf('@') > 0\n && this.options.hiddenDomain\n === jid.substring(jid.indexOf('@') + 1, jid.indexOf('/'));\n\n this.eventEmitter.emit(XMPPEvents.PRESENCE_RECEIVED, {\n fromHiddenDomain: member.isHiddenDomain,\n presence: pres\n });\n\n const xEl = pres.querySelector('x');\n\n if (xEl) {\n xEl.remove();\n }\n\n const nodes = [];\n\n parser.packet2JSON(pres, nodes);\n this.lastPresences[from] = nodes;\n\n // process nodes to extract data needed for MUC_JOINED and\n // MUC_MEMBER_JOINED events\n const extractIdentityInformation = node => {\n const identity = {};\n const userInfo = node.children.find(c => c.tagName === 'user');\n\n if (userInfo) {\n identity.user = {};\n const tags = [ 'id', 'name', 'avatar' ];\n\n if (this.options.hiddenFromRecorderFeatureEnabled) {\n tags.push('hidden-from-recorder');\n }\n\n for (const tag of tags) {\n const child\n = userInfo.children.find(c => c.tagName === tag);\n\n if (child) {\n identity.user[tag] = child.value;\n }\n }\n }\n const groupInfo = node.children.find(c => c.tagName === 'group');\n\n if (groupInfo) {\n identity.group = groupInfo.value;\n }\n\n return identity;\n };\n\n for (let i = 0; i < nodes.length; i++) {\n const node = nodes[i];\n\n switch (node.tagName) {\n case 'bot': {\n const { attributes } = node;\n\n if (!attributes) {\n break;\n }\n const { type } = attributes;\n\n member.botType = type;\n break;\n }\n case 'nick':\n member.nick = node.value;\n break;\n case 'userId':\n member.id = node.value;\n break;\n case 'stats-id':\n member.statsID = node.value;\n break;\n case 'identity':\n member.identity = extractIdentityInformation(node);\n break;\n case 'features': {\n member.features = this._extractFeatures(node);\n break;\n }\n case 'stat': {\n const { attributes } = node;\n\n if (!attributes) {\n break;\n }\n const { name } = attributes;\n\n if (name === 'version') {\n member.version = attributes.value;\n }\n break;\n }\n }\n }\n\n if (!this.joined && !this.inProgressEmitted) {\n const now = this.connectionTimes['muc.join.started'] = window.performance.now();\n\n logger.log('(TIME) MUC join started:\\t', now);\n\n this.eventEmitter.emit(XMPPEvents.MUC_JOIN_IN_PROGRESS);\n this.inProgressEmitted = true;\n }\n\n if (from === this.myroomjid) {\n const newRole\n = member.affiliation === 'owner' ? member.role : 'none';\n\n if (this.role !== newRole) {\n this.role = newRole;\n this.eventEmitter.emit(\n XMPPEvents.LOCAL_ROLE_CHANGED,\n this.role);\n }\n if (!this.joined) {\n this.joined = true;\n const now = this.connectionTimes['muc.joined']\n = window.performance.now();\n\n logger.log('(TIME) MUC joined:\\t', now);\n\n // set correct initial state of locked\n if (this.password) {\n this.locked = true;\n }\n\n // Re-send presence in case any presence updates were added,\n // but blocked from sending, during the join process.\n // send the presence only if there was a modification after we had synced it\n if (this.presenceUpdateTime >= this.presenceSyncTime) {\n this.sendPresence();\n }\n\n this.eventEmitter.emit(XMPPEvents.MUC_JOINED);\n\n // Now let's check the disco-info to retrieve the\n // meeting Id if any\n !this.options.disableDiscoInfo && this.discoRoomInfo();\n }\n } else if (jid === undefined) {\n logger.info('Ignoring member with undefined JID');\n } else if (this.members[from] === undefined) {\n // new participant\n this.members[from] = member;\n logger.log('entered', from, member);\n hasStatusUpdate = member.status !== undefined;\n hasVersionUpdate = member.version !== undefined;\n if (member.isFocus) {\n this._initFocus(from, member.features);\n } else {\n // identity is being added to member joined, so external\n // services can be notified for that (currently identity is\n // not used inside library)\n this.eventEmitter.emit(\n XMPPEvents.MUC_MEMBER_JOINED,\n from,\n member.nick,\n member.role,\n member.isHiddenDomain,\n member.statsID,\n member.status,\n member.identity,\n member.botType,\n member.jid,\n member.features,\n member.isReplaceParticipant);\n\n // we are reporting the status with the join\n // so we do not want a second event about status update\n hasStatusUpdate = false;\n }\n } else {\n // Presence update for existing participant\n // Watch role change:\n const memberOfThis = this.members[from];\n\n if (memberOfThis.role !== member.role) {\n memberOfThis.role = member.role;\n this.eventEmitter.emit(\n XMPPEvents.MUC_ROLE_CHANGED, from, member.role);\n }\n\n // affiliation changed\n if (memberOfThis.affiliation !== member.affiliation) {\n memberOfThis.affiliation = member.affiliation;\n }\n\n // fire event that botType had changed\n if (memberOfThis.botType !== member.botType) {\n memberOfThis.botType = member.botType;\n this.eventEmitter.emit(\n XMPPEvents.MUC_MEMBER_BOT_TYPE_CHANGED,\n from,\n member.botType);\n }\n\n if (member.isFocus) {\n // From time to time first few presences of the focus are not\n // containing it's jid. That way we can mark later the focus\n // member instead of not marking it at all and not starting the\n // conference.\n // FIXME: Maybe there is a better way to handle this issue. It\n // seems there is some period of time in prosody that the\n // configuration form is received but not applied. And if any\n // participant joins during that period of time the first\n // presence from the focus won't contain\n // .\n // By default we are disabling the waiting for form submission in order to use the room\n // and we had enabled by default that jids are public in the room ,\n // so this case should not happen, if public jid is turned off we will receive the jid\n // when we become moderator in the room\n memberOfThis.isFocus = true;\n this._initFocus(from, member.features);\n }\n\n // store the new display name\n if (member.displayName) {\n memberOfThis.displayName = member.displayName;\n }\n\n // update stored status message to be able to detect changes\n if (memberOfThis.status !== member.status) {\n hasStatusUpdate = true;\n memberOfThis.status = member.status;\n }\n\n if (memberOfThis.version !== member.version) {\n hasVersionUpdate = true;\n memberOfThis.version = member.version;\n }\n\n if (!isEqual(memberOfThis.features, member.features)) {\n memberOfThis.features = member.features;\n this.eventEmitter.emit(XMPPEvents.PARTICIPANT_FEATURES_CHANGED, from, member.features);\n }\n }\n\n // after we had fired member or room joined events, lets fire events\n // for the rest info we got in presence\n for (let i = 0; i < nodes.length; i++) {\n const node = nodes[i];\n\n switch (node.tagName) {\n case 'nick':\n if (!member.isFocus) {\n const displayName\n = this.xmpp.options.displayJids\n ? Strophe.getResourceFromJid(from)\n : member.nick;\n\n this.eventEmitter.emit(\n XMPPEvents.DISPLAY_NAME_CHANGED,\n from,\n displayName);\n }\n break;\n case 'bridgeNotAvailable':\n if (member.isFocus && !this.noBridgeAvailable) {\n this.noBridgeAvailable = true;\n this.eventEmitter.emit(XMPPEvents.BRIDGE_DOWN);\n }\n break;\n case 'conference-properties':\n if (member.isFocus) {\n const properties = {};\n\n for (let j = 0; j < node.children.length; j++) {\n const { attributes } = node.children[j];\n\n if (attributes && attributes.key) {\n properties[attributes.key] = attributes.value;\n }\n }\n\n this.eventEmitter.emit(XMPPEvents.CONFERENCE_PROPERTIES_CHANGED, properties);\n\n // Log if Jicofo supports restart by terminate only once. This conference property does not change\n // during the call.\n if (typeof this.restartByTerminateSupported === 'undefined') {\n this.restartByTerminateSupported = properties['support-terminate-restart'] === 'true';\n logger.info(`Jicofo supports restart by terminate: ${this.supportsRestartByTerminate()}`);\n }\n }\n break;\n case 'transcription-status': {\n const { attributes } = node;\n\n if (!attributes) {\n break;\n }\n\n const { status } = attributes;\n\n if (status && status !== this.transcriptionStatus) {\n this.transcriptionStatus = status;\n this.eventEmitter.emit(\n XMPPEvents.TRANSCRIPTION_STATUS_CHANGED,\n status\n );\n }\n\n\n break;\n }\n case 'call-control': {\n const att = node.attributes;\n\n if (!att) {\n break;\n }\n this.phoneNumber = att.phone || null;\n this.phonePin = att.pin || null;\n this.eventEmitter.emit(XMPPEvents.PHONE_NUMBER_CHANGED);\n break;\n }\n default:\n this.processNode(node, from);\n }\n }\n\n // Trigger status message update if necessary\n if (hasStatusUpdate) {\n this.eventEmitter.emit(\n XMPPEvents.PRESENCE_STATUS,\n from,\n member.status);\n }\n\n if (hasVersionUpdate) {\n logger.info(`Received version for ${jid}: ${member.version}`);\n }\n }\n\n /**\n * Extracts the features from the presence.\n * @param node the node to process.\n * @return features the Set of features where extracted data is added.\n * @private\n */\n _extractFeatures(node) {\n const features = new Set();\n\n for (let j = 0; j < node.children.length; j++) {\n const { attributes } = node.children[j];\n\n if (attributes && attributes.var) {\n features.add(attributes.var);\n }\n }\n\n return features;\n }\n\n /**\n * Initialize some properties when the focus participant is verified.\n * @param from jid of the focus\n * @param features the features reported in jicofo presence\n */\n _initFocus(from, features) {\n this.focusMucJid = from;\n this.focusFeatures = features;\n }\n\n /**\n * Sets the special listener to be used for \"command\"s whose name starts\n * with \"jitsi_participant_\".\n */\n setParticipantPropertyListener(listener) {\n this.participantPropertyListener = listener;\n }\n\n /**\n * Checks if Jicofo supports restarting Jingle session after 'session-terminate'.\n * @returns {boolean}\n */\n supportsRestartByTerminate() {\n return this.restartByTerminateSupported;\n }\n\n /**\n *\n * @param node\n * @param from\n */\n processNode(node, from) {\n // make sure we catch all errors coming from any handler\n // otherwise we can remove the presence handler from strophe\n try {\n let tagHandlers = this.presHandlers[node.tagName];\n\n if (node.tagName.startsWith('jitsi_participant_')) {\n tagHandlers = [ this.participantPropertyListener ];\n }\n\n if (tagHandlers) {\n tagHandlers.forEach(handler => {\n handler(node, Strophe.getResourceFromJid(from), from);\n });\n }\n } catch (e) {\n GlobalOnErrorHandler.callErrorHandler(e);\n logger.error(`Error processing:${node.tagName} node.`, e);\n }\n }\n\n /**\n * Send text message to the other participants in the conference\n * @param message\n * @param elementName\n */\n sendMessage(message, elementName) {\n const msg = $msg({ to: this.roomjid,\n type: 'groupchat' });\n\n // We are adding the message in a packet extension. If this element\n // is different from 'body', we add a custom namespace.\n // e.g. for 'json-message' extension of message stanza.\n if (elementName === 'body') {\n msg.c(elementName, {}, message);\n } else {\n msg.c(elementName, { xmlns: 'http://jitsi.org/jitmeet' }, message);\n }\n\n this.connection.send(msg);\n this.eventEmitter.emit(XMPPEvents.SENDING_CHAT_MESSAGE, message);\n }\n\n /* eslint-disable max-params */\n /**\n * Send private text message to another participant of the conference\n * @param id id/muc resource of the receiver\n * @param message\n * @param elementName\n */\n sendPrivateMessage(id, message, elementName) {\n const msg = $msg({ to: `${this.roomjid}/${id}`,\n type: 'chat' });\n\n // We are adding the message in packet. If this element is different\n // from 'body', we add our custom namespace for the same.\n // e.g. for 'json-message' message extension.\n if (elementName === 'body') {\n msg.c(elementName, message).up();\n } else {\n msg.c(elementName, { xmlns: 'http://jitsi.org/jitmeet' }, message)\n .up();\n }\n\n this.connection.send(msg);\n this.eventEmitter.emit(\n XMPPEvents.SENDING_PRIVATE_CHAT_MESSAGE, message);\n }\n /* eslint-enable max-params */\n\n /**\n *\n * @param subject\n */\n setSubject(subject) {\n const msg = $msg({ to: this.roomjid,\n type: 'groupchat' });\n\n msg.c('subject', subject);\n this.connection.send(msg);\n }\n\n /**\n * Called when participant leaves.\n * @param jid the jid of the participant that leaves\n * @param skipEvents optional params to skip any events, including check\n * whether this is the focus that left\n */\n onParticipantLeft(jid, skipEvents) {\n delete this.lastPresences[jid];\n\n if (skipEvents) {\n return;\n }\n\n this.eventEmitter.emit(XMPPEvents.MUC_MEMBER_LEFT, jid);\n\n this.moderator.onMucMemberLeft(jid);\n }\n\n /**\n *\n * @param pres\n * @param from\n */\n onPresenceUnavailable(pres, from) {\n // ignore presence\n if ($(pres).find('>ignore[xmlns=\"http://jitsi.org/jitmeet/\"]').length) {\n return true;\n }\n\n // room destroyed ?\n const destroySelect = $(pres).find('>x[xmlns=\"http://jabber.org/protocol/muc#user\"]>destroy');\n\n if (destroySelect.length) {\n let reason;\n const reasonSelect\n = $(pres).find(\n '>x[xmlns=\"http://jabber.org/protocol/muc#user\"]'\n + '>destroy>reason');\n\n if (reasonSelect.length) {\n reason = reasonSelect.text();\n }\n\n this.eventEmitter.emit(XMPPEvents.MUC_DESTROYED, reason, destroySelect.attr('jid'));\n this.connection.emuc.doLeave(this.roomjid);\n\n return true;\n }\n\n // Status code 110 indicates that this notification is \"self-presence\".\n const isSelfPresence\n = $(pres)\n .find(\n '>x[xmlns=\"http://jabber.org/protocol/muc#user\"]>'\n + 'status[code=\"110\"]')\n .length;\n const isKick\n = $(pres)\n .find(\n '>x[xmlns=\"http://jabber.org/protocol/muc#user\"]'\n + '>status[code=\"307\"]')\n .length;\n const membersKeys = Object.keys(this.members);\n const isReplaceParticipant = $(pres).find('flip_device').length;\n\n if (isKick) {\n const actorSelect\n = $(pres)\n .find('>x[xmlns=\"http://jabber.org/protocol/muc#user\"]>item>actor');\n let actorNick;\n\n if (actorSelect.length) {\n actorNick = actorSelect.attr('nick');\n }\n\n let reason;\n const reasonSelect\n = $(pres).find(\n '>x[xmlns=\"http://jabber.org/protocol/muc#user\"]'\n + '>item>reason');\n\n if (reasonSelect.length) {\n reason = reasonSelect.text();\n }\n\n // we first fire the kicked so we can show the participant\n // who kicked, before notifying that participant left\n // we fire kicked for us and for any participant kicked\n this.eventEmitter.emit(\n XMPPEvents.KICKED,\n isSelfPresence,\n actorNick,\n Strophe.getResourceFromJid(from),\n reason,\n isReplaceParticipant);\n }\n\n if (isSelfPresence) {\n // If the status code is 110 this means we're leaving and we would\n // like to remove everyone else from our view, so we trigger the\n // event.\n membersKeys.forEach(jid => {\n const member = this.members[jid];\n\n delete this.members[jid];\n this.onParticipantLeft(jid, member.isFocus);\n });\n this.connection.emuc.doLeave(this.roomjid);\n\n // we fire muc_left only if this is not a kick,\n // kick has both statuses 110 and 307.\n if (!isKick) {\n this.eventEmitter.emit(XMPPEvents.MUC_LEFT);\n }\n } else {\n delete this.members[from];\n this.onParticipantLeft(from, false);\n }\n }\n\n /**\n *\n * @param msg\n * @param from\n */\n onMessage(msg, from) {\n const type = msg.getAttribute('type');\n\n if (type === 'error') {\n const settingsErrorMsg = $(msg).find('>settings-error>text').text();\n\n if (settingsErrorMsg.length) {\n this.eventEmitter.emit(XMPPEvents.SETTINGS_ERROR_RECEIVED, settingsErrorMsg);\n\n return true;\n }\n const errorMsg = $(msg).find('>error>text').text();\n\n this.eventEmitter.emit(XMPPEvents.CHAT_ERROR_RECEIVED, errorMsg);\n\n return true;\n }\n\n const txt = $(msg).find('>body').text();\n const subject = $(msg).find('>subject');\n\n if (subject.length) {\n const subjectText = subject.text();\n\n if (subjectText || subjectText === '') {\n this.eventEmitter.emit(XMPPEvents.SUBJECT_CHANGED, subjectText);\n logger.log(`Subject is changed to ${subjectText}`);\n }\n }\n\n // xep-0203 delay\n let stamp = $(msg).find('>delay').attr('stamp');\n\n if (!stamp) {\n // or xep-0091 delay, UTC timestamp\n stamp = $(msg).find('>[xmlns=\"jabber:x:delay\"]').attr('stamp');\n\n if (stamp) {\n // the format is CCYYMMDDThh:mm:ss\n const dateParts\n = stamp.match(/(\\d{4})(\\d{2})(\\d{2}T\\d{2}:\\d{2}:\\d{2})/);\n\n stamp = `${dateParts[1]}-${dateParts[2]}-${dateParts[3]}Z`;\n }\n }\n\n if (from === this.roomjid) {\n let invite;\n\n if ($(msg).find('>x[xmlns=\"http://jabber.org/protocol/muc#user\"]>status[code=\"104\"]').length) {\n this.discoRoomInfo();\n } else if ((invite = $(msg).find('>x[xmlns=\"http://jabber.org/protocol/muc#user\"]>invite'))\n && invite.length) {\n const passwordSelect = $(msg).find('>x[xmlns=\"http://jabber.org/protocol/muc#user\"]>password');\n let password;\n\n if (passwordSelect && passwordSelect.length) {\n password = passwordSelect.text();\n }\n\n this.eventEmitter.emit(XMPPEvents.INVITE_MESSAGE_RECEIVED,\n from, invite.attr('from'), txt, password);\n }\n }\n\n const jsonMessage = $(msg).find('>json-message').text();\n\n if (jsonMessage) {\n const parsedJson = this.xmpp.tryParseJSONAndVerify(jsonMessage);\n\n // We emit this event if the message is a valid json, and is not\n // delivered after a delay, i.e. stamp is undefined.\n // e.g. - subtitles should not be displayed if delayed.\n if (parsedJson && stamp === undefined) {\n this.eventEmitter.emit(XMPPEvents.JSON_MESSAGE_RECEIVED,\n from, parsedJson);\n\n return;\n }\n }\n\n if (txt) {\n if (type === 'chat') {\n this.eventEmitter.emit(XMPPEvents.PRIVATE_MESSAGE_RECEIVED,\n from, txt, this.myroomjid, stamp);\n } else if (type === 'groupchat') {\n this.eventEmitter.emit(XMPPEvents.MESSAGE_RECEIVED,\n from, txt, this.myroomjid, stamp);\n }\n }\n }\n\n /**\n *\n * @param pres\n * @param from\n */\n onPresenceError(pres, from) {\n if ($(pres)\n .find(\n '>error[type=\"auth\"]'\n + '>not-authorized['\n + 'xmlns=\"urn:ietf:params:xml:ns:xmpp-stanzas\"]')\n .length) {\n logger.log('on password required', from);\n this.eventEmitter.emit(XMPPEvents.PASSWORD_REQUIRED);\n } else if ($(pres)\n .find(\n '>error[type=\"cancel\"]'\n + '>not-allowed['\n + 'xmlns=\"urn:ietf:params:xml:ns:xmpp-stanzas\"]')\n .length) {\n const toDomain = Strophe.getDomainFromJid(pres.getAttribute('to'));\n\n if (toDomain === this.xmpp.options.hosts.anonymousdomain) {\n // enter the room by replying with 'not-authorized'. This would\n // result in reconnection from authorized domain.\n // We're either missing Jicofo/Prosody config for anonymous\n // domains or something is wrong.\n this.eventEmitter.emit(XMPPEvents.ROOM_JOIN_ERROR);\n\n } else {\n logger.warn('onPresError ', pres);\n this.eventEmitter.emit(\n XMPPEvents.ROOM_CONNECT_NOT_ALLOWED_ERROR);\n }\n } else if ($(pres).find('>error>service-unavailable').length) {\n logger.warn('Maximum users limit for the room has been reached',\n pres);\n this.eventEmitter.emit(XMPPEvents.ROOM_MAX_USERS_ERROR);\n this.connection.emuc.doLeave(this.roomjid);\n } else if ($(pres)\n .find(\n '>error[type=\"auth\"]'\n + '>registration-required['\n + 'xmlns=\"urn:ietf:params:xml:ns:xmpp-stanzas\"]').length) {\n\n // let's extract the lobby jid from the custom field\n const lobbyRoomNode = $(pres).find('>error[type=\"auth\"]>lobbyroom');\n let lobbyRoomJid;\n\n if (lobbyRoomNode.length) {\n lobbyRoomJid = lobbyRoomNode.text();\n } else {\n // let's fallback to old location of lobbyroom node, TODO: to be removed in the future once\n // everything is updated\n const lobbyRoomOldNode = $(pres).find('>lobbyroom');\n\n if (lobbyRoomOldNode.length) {\n lobbyRoomJid = lobbyRoomOldNode.text();\n }\n }\n\n this.eventEmitter.emit(XMPPEvents.ROOM_CONNECT_MEMBERS_ONLY_ERROR, lobbyRoomJid);\n } else {\n logger.warn('onPresError ', pres);\n this.eventEmitter.emit(XMPPEvents.ROOM_CONNECT_ERROR);\n }\n }\n\n /**\n *\n * @param jid\n * @param affiliation\n */\n setAffiliation(jid, affiliation) {\n const grantIQ = $iq({\n to: this.roomjid,\n type: 'set'\n })\n .c('query', { xmlns: 'http://jabber.org/protocol/muc#admin' })\n .c('item', {\n affiliation,\n nick: Strophe.getResourceFromJid(jid)\n })\n .c('reason').t(`Your affiliation has been changed to '${affiliation}'.`)\n .up().up().up();\n\n this.connection.sendIQ(\n grantIQ,\n result => logger.log('Set affiliation of participant with jid: ', jid, 'to', affiliation, result),\n error => logger.log('Set affiliation of participant error: ', error));\n }\n\n /**\n *\n * @param jid\n * @param reason\n */\n kick(jid, reason = 'You have been kicked.') {\n const kickIQ = $iq({ to: this.roomjid,\n type: 'set' })\n .c('query', { xmlns: 'http://jabber.org/protocol/muc#admin' })\n .c('item', { nick: Strophe.getResourceFromJid(jid),\n role: 'none' })\n .c('reason').t(reason).up().up().up();\n\n this.connection.sendIQ(\n kickIQ,\n result => logger.log('Kick participant with jid: ', jid, result),\n error => logger.log('Kick participant error: ', error));\n }\n\n /* eslint-disable max-params */\n\n /**\n *\n * @param key\n * @param onSuccess\n * @param onError\n * @param onNotSupported\n */\n lockRoom(key, onSuccess, onError, onNotSupported) {\n // http://xmpp.org/extensions/xep-0045.html#roomconfig\n this.connection.sendIQ(\n $iq({\n to: this.roomjid,\n type: 'get'\n })\n .c('query', { xmlns: 'http://jabber.org/protocol/muc#owner' }),\n res => {\n if ($(res)\n .find(\n '>query>x[xmlns=\"jabber:x:data\"]'\n + '>field[var=\"muc#roomconfig_roomsecret\"]')\n .length) {\n const formsubmit\n = $iq({\n to: this.roomjid,\n type: 'set'\n })\n .c('query', {\n xmlns: 'http://jabber.org/protocol/muc#owner'\n });\n\n formsubmit.c('x', {\n xmlns: 'jabber:x:data',\n type: 'submit'\n });\n formsubmit\n .c('field', { 'var': 'FORM_TYPE' })\n .c('value')\n .t('http://jabber.org/protocol/muc#roomconfig')\n .up()\n .up();\n formsubmit\n .c('field', { 'var': 'muc#roomconfig_roomsecret' })\n .c('value')\n .t(key)\n .up()\n .up();\n formsubmit\n .c('field',\n { 'var': 'muc#roomconfig_passwordprotectedroom' })\n .c('value')\n .t(key === null || key.length === 0 ? '0' : '1')\n .up()\n .up();\n\n // if members only enabled\n if (this.membersOnlyEnabled) {\n formsubmit\n .c('field', { 'var': 'muc#roomconfig_membersonly' })\n .c('value')\n .t('true')\n .up()\n .up();\n }\n\n // Fixes a bug in prosody 0.9.+\n // https://prosody.im/issues/issue/373\n formsubmit\n .c('field', { 'var': 'muc#roomconfig_whois' })\n .c('value')\n .t('anyone')\n .up()\n .up();\n\n this.connection.sendIQ(\n formsubmit,\n () => {\n\n // we set the password in chat room so we can use it\n // later when dialing out\n this.password = key;\n onSuccess();\n },\n onError);\n } else {\n onNotSupported();\n }\n },\n onError);\n }\n\n /* eslint-enable max-params */\n\n /**\n * Turns off or on the members only config for the main room.\n *\n * @param {boolean} enabled - Whether to turn it on or off.\n * @param onSuccess - optional callback.\n * @param onError - optional callback.\n */\n setMembersOnly(enabled, onSuccess, onError) {\n if (enabled && Object.values(this.members).filter(m => !m.isFocus).length) {\n // first grant membership to all that are in the room\n // currently there is a bug in prosody where it handles only the first item\n // that's why we will send iq per member\n Object.values(this.members).forEach(m => {\n if (m.jid && !MEMBERS_AFFILIATIONS.includes(m.affiliation)) {\n this.xmpp.connection.sendIQ(\n $iq({\n to: this.roomjid,\n type: 'set' })\n .c('query', {\n xmlns: 'http://jabber.org/protocol/muc#admin' })\n .c('item', {\n 'affiliation': 'member',\n 'jid': m.jid\n }).up().up());\n }\n });\n }\n\n const errorCallback = onError ? onError : () => {}; // eslint-disable-line no-empty-function\n\n this.xmpp.connection.sendIQ(\n $iq({\n to: this.roomjid,\n type: 'get'\n }).c('query', { xmlns: 'http://jabber.org/protocol/muc#owner' }),\n res => {\n if ($(res).find('>query>x[xmlns=\"jabber:x:data\"]>field[var=\"muc#roomconfig_membersonly\"]').length) {\n const formToSubmit\n = $iq({\n to: this.roomjid,\n type: 'set'\n }).c('query', { xmlns: 'http://jabber.org/protocol/muc#owner' });\n\n formToSubmit.c('x', {\n xmlns: 'jabber:x:data',\n type: 'submit'\n });\n formToSubmit\n .c('field', { 'var': 'FORM_TYPE' })\n .c('value')\n .t('http://jabber.org/protocol/muc#roomconfig')\n .up()\n .up();\n formToSubmit\n .c('field', { 'var': 'muc#roomconfig_membersonly' })\n .c('value')\n .t(enabled ? 'true' : 'false')\n .up()\n .up();\n\n // if room is locked from other participant or we are locking it\n if (this.locked) {\n formToSubmit\n .c('field',\n { 'var': 'muc#roomconfig_passwordprotectedroom' })\n .c('value')\n .t('1')\n .up()\n .up();\n }\n\n this.xmpp.connection.sendIQ(formToSubmit, onSuccess, errorCallback);\n } else {\n errorCallback(new Error('Setting members only room not supported!'));\n }\n },\n errorCallback);\n }\n\n /**\n * Adds the key to the presence map, overriding any previous value.\n * This method is used by jibri.\n *\n * @param key The key to add or replace.\n * @param values The new values.\n * @returns {boolean|null} true if the operation succeeded or false when no add or replce was\n * performed as the value was already there.\n * @deprecated Use 'addOrReplaceInPresence' instead. TODO: remove it from here and jibri.\n */\n addToPresence(key, values) {\n return this.addOrReplaceInPresence(key, values);\n }\n\n /**\n * Adds the key to the presence map, overriding any previous value.\n * @param key The key to add or replace.\n * @param values The new values.\n * @returns {boolean|null} true if the operation succeeded or false when no add or replace was\n * performed as the value was already there.\n */\n addOrReplaceInPresence(key, values) {\n values.tagName = key;\n\n const matchingNodes = this.presMap.nodes.filter(node => key === node.tagName);\n\n // if we have found just one, let's check is it the same\n if (matchingNodes.length === 1 && isEqual(matchingNodes[0], values)) {\n return false;\n }\n\n this.removeFromPresence(key);\n this.presMap.nodes.push(values);\n this.presenceUpdateTime = Date.now();\n\n return true;\n }\n\n /**\n * Retrieves a value from the presence map.\n *\n * @param {string} key - The key to find the value for.\n * @returns {Object?}\n */\n getFromPresence(key) {\n return this.presMap.nodes.find(node => key === node.tagName);\n }\n\n /**\n * Removes a key from the presence map.\n * @param key\n */\n removeFromPresence(key) {\n const nodes = this.presMap.nodes.filter(node => key !== node.tagName);\n\n this.presMap.nodes = nodes;\n this.presenceUpdateTime = Date.now();\n }\n\n /**\n *\n * @param name\n * @param handler\n */\n addPresenceListener(name, handler) {\n if (typeof handler !== 'function') {\n throw new Error('\"handler\" is not a function');\n }\n let tagHandlers = this.presHandlers[name];\n\n if (!tagHandlers) {\n this.presHandlers[name] = tagHandlers = [];\n }\n if (tagHandlers.indexOf(handler) === -1) {\n tagHandlers.push(handler);\n } else {\n logger.warn(\n `Trying to add the same handler more than once for: ${name}`);\n }\n }\n\n /**\n *\n * @param name\n * @param handler\n */\n removePresenceListener(name, handler) {\n const tagHandlers = this.presHandlers[name];\n const handlerIdx = tagHandlers ? tagHandlers.indexOf(handler) : -1;\n\n // eslint-disable-next-line no-negated-condition\n if (handlerIdx !== -1) {\n tagHandlers.splice(handlerIdx, 1);\n } else {\n logger.warn(`Handler for: ${name} was not registered`);\n }\n }\n\n /**\n * Checks if the user identified by given mucJid is the conference\n * focus.\n * @param mucJid the full MUC address of the user to be checked.\n * @returns {boolean|null} true if MUC user is the conference focus\n * or false if is not. When given mucJid does not exist in\n * the MUC then null is returned.\n */\n isFocus(mucJid) {\n const member = this.members[mucJid];\n\n if (member) {\n return member.isFocus;\n }\n\n return null;\n }\n\n /**\n *\n */\n isModerator() {\n return this.role === 'moderator';\n }\n\n /**\n *\n * @param peerJid\n */\n getMemberRole(peerJid) {\n if (this.members[peerJid]) {\n return this.members[peerJid].role;\n }\n\n return null;\n }\n\n /**\n *\n * @param mute\n */\n addAudioInfoToPresence(mute) {\n const audioMutedTagName = 'audiomuted';\n\n // we skip adding it as muted is default value\n if (mute && !this.getFromPresence(audioMutedTagName)) {\n return false;\n }\n\n return this.addOrReplaceInPresence(\n audioMutedTagName,\n {\n value: mute.toString()\n });\n }\n\n /**\n *\n * @param mute\n */\n addVideoInfoToPresence(mute) {\n const videoMutedTagName = 'videomuted';\n\n // we skip adding it as muted is default value\n if (mute && !this.getFromPresence(videoMutedTagName)) {\n return false;\n }\n\n return this.addOrReplaceInPresence(\n videoMutedTagName,\n {\n value: mute.toString()\n });\n }\n\n /**\n * Obtains the info about given media advertised in the MUC presence of\n * the participant identified by the given endpoint JID.\n * @param {string} endpointId the endpoint ID mapped to the participant\n * which corresponds to MUC nickname.\n * @param {MediaType} mediaType the type of the media for which presence\n * info will be obtained.\n * @return {PeerMediaInfo} presenceInfo an object with media presence\n * info or null either if there is no presence available or if\n * the media type given is invalid.\n */\n getMediaPresenceInfo(endpointId, mediaType) {\n // Will figure out current muted status by looking up owner's presence\n const pres = this.lastPresences[`${this.roomjid}/${endpointId}`];\n\n if (!pres) {\n // No presence available\n return null;\n }\n const data = {\n muted: true, // muted by default\n videoType: VideoType.CAMERA // 'camera' by default\n };\n let mutedNode = null;\n\n if (mediaType === MediaType.AUDIO) {\n mutedNode = filterNodeFromPresenceJSON(pres, 'audiomuted');\n } else if (mediaType === MediaType.VIDEO) {\n mutedNode = filterNodeFromPresenceJSON(pres, 'videomuted');\n const codecTypeNode = filterNodeFromPresenceJSON(pres, 'jitsi_participant_codecType');\n const videoTypeNode = filterNodeFromPresenceJSON(pres, 'videoType');\n\n if (videoTypeNode.length > 0) {\n data.videoType = videoTypeNode[0].value;\n }\n if (codecTypeNode.length > 0) {\n data.codecType = codecTypeNode[0].value;\n }\n } else {\n logger.error(`Unsupported media type: ${mediaType}`);\n\n return null;\n }\n\n if (mutedNode.length > 0) {\n data.muted = mutedNode[0].value === 'true';\n }\n\n return data;\n }\n\n /**\n * Returns the last presence advertised by a MUC member.\n * @param {string} mucNick\n * @returns {*}\n */\n getLastPresence(mucNick) {\n return this.lastPresences[`${this.roomjid}/${mucNick}`];\n }\n\n /**\n * Returns true if the SIP calls are supported and false otherwise\n */\n isSIPCallingSupported() {\n if (this.moderator) {\n return this.moderator.isSipGatewayEnabled();\n }\n\n return false;\n }\n\n /**\n * Dials a number.\n * @param number the number\n */\n dial(number) {\n return this.connection.rayo.dial(number, 'fromnumber',\n Strophe.getBareJidFromJid(this.myroomjid), this.password,\n this.focusMucJid);\n }\n\n /**\n * Hangup an existing call\n */\n hangup() {\n return this.connection.rayo.hangup();\n }\n\n /**\n *\n * @returns {Lobby}\n */\n getLobby() {\n return this.lobby;\n }\n\n /**\n * @returns {AVModeration}\n */\n getAVModeration() {\n return this.avModeration;\n }\n\n /**\n * @returns {BreakoutRooms}\n */\n getBreakoutRooms() {\n return this.breakoutRooms;\n }\n\n /**\n * Returns the phone number for joining the conference.\n */\n getPhoneNumber() {\n return this.phoneNumber;\n }\n\n /**\n * Returns the pin for joining the conference with phone.\n */\n getPhonePin() {\n return this.phonePin;\n }\n\n /**\n * Returns the meeting unique ID if any came from backend.\n *\n * @returns {string} - The meeting ID.\n */\n getMeetingId() {\n return this.meetingId;\n }\n\n /**\n * Mutes remote participant.\n * @param jid of the participant\n * @param mute\n * @param mediaType\n */\n muteParticipant(jid, mute, mediaType) {\n logger.info('set mute', mute, jid);\n const iqToFocus = $iq(\n { to: this.focusMucJid,\n type: 'set' })\n .c('mute', {\n xmlns: `http://jitsi.org/jitmeet/${mediaType}`,\n jid\n })\n .t(mute.toString())\n .up();\n\n this.connection.sendIQ(\n iqToFocus,\n result => logger.log('set mute', result),\n error => logger.log('set mute error', error));\n }\n\n /**\n * TODO: Document\n * @param iq\n */\n onMute(iq) {\n const from = iq.getAttribute('from');\n\n if (from !== this.focusMucJid) {\n logger.warn('Ignored mute from non focus peer');\n\n return;\n }\n const mute = $(iq).find('mute');\n\n if (mute.length && mute.text() === 'true') {\n this.eventEmitter.emit(XMPPEvents.AUDIO_MUTED_BY_FOCUS, mute.attr('actor'));\n } else {\n // XXX Why do we support anything but muting? Why do we encode the\n // value in the text of the element? Why do we use a separate XML\n // namespace?\n logger.warn('Ignoring a mute request which does not explicitly '\n + 'specify a positive mute command.');\n }\n }\n\n /**\n * TODO: Document\n * @param iq\n */\n onMuteVideo(iq) {\n const from = iq.getAttribute('from');\n\n if (from !== this.focusMucJid) {\n logger.warn('Ignored mute from non focus peer');\n\n return;\n }\n const mute = $(iq).find('mute');\n\n if (mute.length && mute.text() === 'true') {\n this.eventEmitter.emit(XMPPEvents.VIDEO_MUTED_BY_FOCUS, mute.attr('actor'));\n } else {\n // XXX Why do we support anything but muting? Why do we encode the\n // value in the text of the element? Why do we use a separate XML\n // namespace?\n logger.warn('Ignoring a mute request which does not explicitly '\n + 'specify a positive mute command.');\n }\n }\n\n /**\n * Clean any listeners or resources, executed on leaving.\n */\n clean() {\n this._removeConnListeners.forEach(remove => remove());\n this._removeConnListeners = [];\n\n this.joined = false;\n this.inProgressEmitted = false;\n }\n\n /**\n * Leaves the room. Closes the jingle session.\n * @returns {Promise} which is resolved if XMPPEvents.MUC_LEFT is received\n * less than 5s after sending presence unavailable. Otherwise the promise is\n * rejected.\n */\n leave() {\n this.avModeration.dispose();\n this.breakoutRooms.dispose();\n\n const promises = [];\n\n this.lobby?.lobbyRoom && promises.push(this.lobby.leave());\n\n promises.push(new Promise((resolve, reject) => {\n let timeout = -1;\n\n const onMucLeft = (doReject = false) => {\n this.eventEmitter.removeListener(XMPPEvents.MUC_LEFT, onMucLeft);\n clearTimeout(timeout);\n if (doReject) {\n // The timeout expired. Make sure we clean the EMUC state.\n this.connection.emuc.doLeave(this.roomjid);\n reject(new Error('The timeout for the confirmation about leaving the room expired.'));\n } else {\n resolve();\n }\n };\n\n timeout = setTimeout(() => onMucLeft(true), 5000);\n\n this.clean();\n this.eventEmitter.on(XMPPEvents.MUC_LEFT, onMucLeft);\n this.doLeave();\n }));\n\n return Promise.allSettled(promises);\n }\n}\n\n/* eslint-enable newline-per-chained-call */\n","/* global $ */\n\nimport { getLogger } from '@jitsi/logger';\nimport { Strophe } from 'strophe.js';\n\nimport XMPPEvents from '../../service/xmpp/XMPPEvents';\n\nimport ChatRoom from './ChatRoom';\nimport { ConnectionPluginListenable } from './ConnectionPlugin';\n\nconst logger = getLogger(__filename);\n\n/**\n * MUC connection plugin.\n */\nexport default class MucConnectionPlugin extends ConnectionPluginListenable {\n /**\n *\n * @param xmpp\n */\n constructor(xmpp) {\n super();\n this.xmpp = xmpp;\n this.rooms = {};\n }\n\n /**\n *\n * @param connection\n */\n init(connection) {\n super.init(connection);\n\n // add handlers (just once)\n this.connection.addHandler(this.onPresence.bind(this), null,\n 'presence', null, null, null, null);\n this.connection.addHandler(this.onPresenceUnavailable.bind(this),\n null, 'presence', 'unavailable', null);\n this.connection.addHandler(this.onPresenceError.bind(this), null,\n 'presence', 'error', null);\n this.connection.addHandler(this.onMessage.bind(this), null,\n 'message', null, null);\n this.connection.addHandler(this.onMute.bind(this),\n 'http://jitsi.org/jitmeet/audio', 'iq', 'set', null, null);\n this.connection.addHandler(this.onMuteVideo.bind(this),\n 'http://jitsi.org/jitmeet/video', 'iq', 'set', null, null);\n }\n\n /**\n *\n * @param jid\n * @param password\n * @param options\n */\n createRoom(jid, password, options) {\n const roomJid = Strophe.getBareJidFromJid(jid);\n\n if (this.isRoomCreated(roomJid)) {\n const errmsg = 'You are already in the room!';\n\n logger.error(errmsg);\n throw new Error(errmsg);\n }\n this.rooms[roomJid] = new ChatRoom(this.connection, jid,\n password, this.xmpp, options);\n this.eventEmitter.emit(\n XMPPEvents.EMUC_ROOM_ADDED, this.rooms[roomJid]);\n\n return this.rooms[roomJid];\n }\n\n /**\n * Check if a room with the passed JID is already created.\n *\n * @param {string} roomJid - The JID of the room.\n * @returns {boolean}\n */\n isRoomCreated(roomJid) {\n return roomJid in this.rooms;\n }\n\n /**\n *\n * @param jid\n */\n doLeave(jid) {\n this.eventEmitter.emit(\n XMPPEvents.EMUC_ROOM_REMOVED, this.rooms[jid]);\n delete this.rooms[jid];\n }\n\n /**\n *\n * @param pres\n */\n onPresence(pres) {\n const from = pres.getAttribute('from');\n\n // What is this for? A workaround for something?\n if (pres.getAttribute('type')) {\n return true;\n }\n\n const room = this.rooms[Strophe.getBareJidFromJid(from)];\n\n if (!room) {\n return true;\n }\n\n // Parse status.\n if ($(pres).find('>x[xmlns=\"http://jabber.org/protocol/muc#user\"]'\n + '>status[code=\"201\"]').length) {\n room.createNonAnonymousRoom();\n }\n\n room.onPresence(pres);\n\n return true;\n }\n\n /**\n *\n * @param pres\n */\n onPresenceUnavailable(pres) {\n const from = pres.getAttribute('from');\n const room = this.rooms[Strophe.getBareJidFromJid(from)];\n\n if (!room) {\n return true;\n }\n\n room.onPresenceUnavailable(pres, from);\n\n return true;\n }\n\n /**\n *\n * @param pres\n */\n onPresenceError(pres) {\n const from = pres.getAttribute('from');\n const room = this.rooms[Strophe.getBareJidFromJid(from)];\n\n if (!room) {\n return true;\n }\n\n room.onPresenceError(pres, from);\n\n return true;\n }\n\n /**\n *\n * @param msg\n */\n onMessage(msg) {\n // FIXME: this is a hack. but jingle on muc makes nickchanges hard\n const from = msg.getAttribute('from');\n const room = this.rooms[Strophe.getBareJidFromJid(from)];\n\n if (!room) {\n return true;\n }\n\n room.onMessage(msg, from);\n\n return true;\n }\n\n /**\n * TODO: Document\n * @param iq\n */\n onMute(iq) {\n const from = iq.getAttribute('from');\n const room = this.rooms[Strophe.getBareJidFromJid(from)];\n\n // Returning false would result in the listener being deregistered by Strophe\n if (!room) {\n return true;\n }\n\n room.onMute(iq);\n\n return true;\n }\n\n /**\n * TODO: Document\n * @param iq\n */\n onMuteVideo(iq) {\n const from = iq.getAttribute('from');\n const room = this.rooms[Strophe.getBareJidFromJid(from)];\n\n // Returning false would result in the listener being deregistered by Strophe\n if (!room) {\n return true;\n }\n\n room.onMuteVideo(iq);\n\n return true;\n }\n}\n","import { getLogger } from '@jitsi/logger';\n\nconst logger = getLogger('FeatureFlags');\n\n/**\n * A global module for accessing information about different feature flags state.\n */\nclass FeatureFlags {\n /**\n * Configures the module.\n *\n * @param {boolean} flags.sourceNameSignaling - Enables source names in the signaling.\n */\n init(flags) {\n this._sourceNameSignaling = Boolean(flags.sourceNameSignaling);\n this._sendMultipleVideoStreams = Boolean(flags.sendMultipleVideoStreams);\n\n logger.info(`Source name signaling: ${this._sourceNameSignaling},`\n + ` Send multiple video streams: ${this._sendMultipleVideoStreams}`);\n }\n\n /**\n * Checks if multiple local video streams support is enabled.\n *\n * @returns {boolean}\n */\n isMultiStreamSupportEnabled() {\n return this._sourceNameSignaling && this._sendMultipleVideoStreams;\n }\n\n /**\n * Checks if the source name signaling is enabled.\n *\n * @returns {boolean}\n */\n isSourceNameSignalingEnabled() {\n return this._sourceNameSignaling;\n }\n}\n\nexport default new FeatureFlags();\n","/* global $ */\n\nimport { getLogger } from '@jitsi/logger';\nimport { $build } from 'strophe.js';\n\nimport * as MediaType from '../../service/RTC/MediaType';\nimport FeatureFlags from '../flags/FeatureFlags';\n\nconst logger = getLogger(__filename);\n\n/**\n * Creates a \"source\" XML element for the source described in compact JSON format in [sourceCompactJson].\n * @param {*} owner the endpoint ID of the owner of the source.\n * @param {*} sourceCompactJson the compact JSON representation of the source.\n * @returns the created \"source\" XML element.\n */\nfunction _createSourceExtension(owner, sourceCompactJson) {\n const node = $build('source', {\n xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0',\n ssrc: sourceCompactJson.s,\n name: FeatureFlags.isSourceNameSignalingEnabled() ? sourceCompactJson.n : undefined\n });\n\n if (sourceCompactJson.m) {\n node.c('parameter', {\n name: 'msid',\n value: sourceCompactJson.m\n }).up();\n }\n node.c('ssrc-info', {\n xmlns: 'http://jitsi.org/jitmeet',\n owner\n }).up();\n\n return node.node;\n}\n\n/**\n * Creates an \"ssrc-group\" XML element for the SSRC group described in compact JSON format in [ssrcGroupCompactJson].\n * @param {*} ssrcGroupCompactJson the compact JSON representation of the SSRC group.\n * @returns the created \"ssrc-group\" element.\n */\nfunction _createSsrcGroupExtension(ssrcGroupCompactJson) {\n const node = $build('ssrc-group', {\n xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0',\n semantics: _getSemantics(ssrcGroupCompactJson[0])\n });\n\n for (let i = 1; i < ssrcGroupCompactJson.length; i++) {\n node.c('source', {\n xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0',\n ssrc: ssrcGroupCompactJson[i]\n }).up();\n }\n\n return node.node;\n}\n\n/**\n * Finds in a Jingle IQ the RTP description element with the given media type. If one does not exists, create it (as\n * well as the required \"content\" parent element) and adds it to the IQ.\n * @param {*} iq\n * @param {*} mediaType The media type, \"audio\" or \"video\".\n * @returns the RTP description element with the given media type.\n */\nfunction _getOrCreateRtpDescription(iq, mediaType) {\n const jingle = $(iq).find('jingle')[0];\n let content = $(jingle).find(`content[name=\"${mediaType}\"]`);\n let description;\n\n if (content.length) {\n content = content[0];\n } else {\n // I'm not suree if \"creator\" and \"senders\" are required.\n content = $build('content', {\n name: mediaType\n }).node;\n jingle.appendChild(content);\n }\n\n description = $(content).find('description');\n\n if (description.length) {\n description = description[0];\n } else {\n description = $build('description', {\n xmlns: 'urn:xmpp:jingle:apps:rtp:1',\n media: mediaType\n }).node;\n content.appendChild(description);\n }\n\n return description;\n}\n\n/**\n * Converts the short string representing SSRC group semantics in compact JSON format to the standard representation\n * (i.e. convert \"f\" to \"FID\" and \"s\" to \"SIM\").\n * @param {*} str the compact JSON format representation of an SSRC group's semantics.\n * @returns the SSRC group semantics corresponding to [str].\n */\nfunction _getSemantics(str) {\n if (str === 'f') {\n return 'FID';\n } else if (str === 's') {\n return 'SIM';\n }\n\n return null;\n}\n\n/**\n * Reads a JSON-encoded message (from a \"json-message\" element) and extracts source descriptions. Adds the extracted\n * source descriptions to the given Jingle IQ in the standard Jingle format.\n *\n * Encoding sources in this compact JSON format instead of standard Jingle was introduced in order to reduce the\n * network traffic and load on the XMPP server. The format is described in Jicofo [TODO: insert link].\n *\n * @param {*} iq the IQ to which source descriptions will be added.\n * @param {*} jsonMessageXml The XML node for the \"json-message\" element.\n * @returns {Map} The audio and video ssrcs extracted from the JSON-encoded message with remote\n * endpoint id as the key.\n */\nexport function expandSourcesFromJson(iq, jsonMessageXml) {\n let json;\n\n try {\n json = JSON.parse(jsonMessageXml.textContent);\n } catch (error) {\n logger.error(`json-message XML contained invalid JSON, ignoring: ${jsonMessageXml.textContent}`);\n\n return null;\n }\n\n if (!json?.sources) {\n // It might be a message of a different type, no need to log.\n return null;\n }\n\n // This is where we'll add \"source\" and \"ssrc-group\" elements. Create them elements if they don't exist.\n const audioRtpDescription = _getOrCreateRtpDescription(iq, MediaType.AUDIO);\n const videoRtpDescription = _getOrCreateRtpDescription(iq, MediaType.VIDEO);\n const ssrcMap = new Map();\n\n for (const owner in json.sources) {\n if (json.sources.hasOwnProperty(owner)) {\n const ssrcs = [];\n const ownerSources = json.sources[owner];\n\n // The video sources, video ssrc-groups, audio sources and audio ssrc-groups are encoded in that order in\n // the elements of the array.\n const videoSources = ownerSources?.length && ownerSources[0];\n const videoSsrcGroups = ownerSources?.length > 1 && ownerSources[1];\n const audioSources = ownerSources?.length > 2 && ownerSources[2];\n const audioSsrcGroups = ownerSources?.length > 3 && ownerSources[3];\n\n if (videoSources?.length) {\n for (let i = 0; i < videoSources.length; i++) {\n videoRtpDescription.appendChild(_createSourceExtension(owner, videoSources[i]));\n ssrcs.push(videoSources[i]?.s);\n }\n }\n\n if (videoSsrcGroups?.length) {\n for (let i = 0; i < videoSsrcGroups.length; i++) {\n videoRtpDescription.appendChild(_createSsrcGroupExtension(videoSsrcGroups[i]));\n }\n }\n if (audioSources?.length) {\n for (let i = 0; i < audioSources.length; i++) {\n audioRtpDescription.appendChild(_createSourceExtension(owner, audioSources[i]));\n ssrcs.push(audioSources[i]?.s);\n }\n }\n\n if (audioSsrcGroups?.length) {\n for (let i = 0; i < audioSsrcGroups.length; i++) {\n audioRtpDescription.appendChild(_createSsrcGroupExtension(audioSsrcGroups[i]));\n }\n }\n ssrcMap.set(owner, ssrcs);\n }\n }\n\n return ssrcMap;\n}\n","\nimport JitsiTrackError from '../../JitsiTrackError';\nimport * as JitsiTrackErrors from '../../JitsiTrackErrors';\nimport browser from '../browser';\n\nconst logger = require('@jitsi/logger').getLogger(__filename);\n\n/**\n * The default frame rate for Screen Sharing.\n */\nexport const SS_DEFAULT_FRAME_RATE = 5;\n\n/**\n * Handles obtaining a stream from a screen capture on different browsers.\n */\nconst ScreenObtainer = {\n /**\n * If not null it means that the initialization process is still in\n * progress. It is used to make desktop stream request wait and continue\n * after it's done.\n * {@type Promise|null}\n */\n\n obtainStream: null,\n\n /**\n * Initializes the function used to obtain a screen capture\n * (this.obtainStream).\n *\n * @param {object} options\n */\n init(options = {}) {\n this.options = options;\n this.obtainStream = this._createObtainStreamMethod();\n\n if (!this.obtainStream) {\n logger.info('Desktop sharing disabled');\n }\n },\n\n /**\n * Returns a method which will be used to obtain the screen sharing stream\n * (based on the browser type).\n *\n * @returns {Function}\n * @private\n */\n _createObtainStreamMethod() {\n if (browser.isNWJS()) {\n return (onSuccess, onFailure) => {\n window.JitsiMeetNW.obtainDesktopStream(\n onSuccess,\n (error, constraints) => {\n let jitsiError;\n\n // FIXME:\n // This is very very dirty fix for recognising that the\n // user have clicked the cancel button from the Desktop\n // sharing pick window. The proper solution would be to\n // detect this in the NWJS application by checking the\n // streamId === \"\". Even better solution would be to\n // stop calling GUM from the NWJS app and just pass the\n // streamId to lib-jitsi-meet. This way the desktop\n // sharing implementation for NWJS and chrome extension\n // will be the same and lib-jitsi-meet will be able to\n // control the constraints, check the streamId, etc.\n //\n // I cannot find documentation about \"InvalidStateError\"\n // but this is what we are receiving from GUM when the\n // streamId for the desktop sharing is \"\".\n\n if (error && error.name === 'InvalidStateError') {\n jitsiError = new JitsiTrackError(\n JitsiTrackErrors.SCREENSHARING_USER_CANCELED\n );\n } else {\n jitsiError = new JitsiTrackError(\n error, constraints, [ 'desktop' ]);\n }\n (typeof onFailure === 'function')\n && onFailure(jitsiError);\n });\n };\n } else if (browser.isElectron()) {\n return this.obtainScreenOnElectron;\n } else if (browser.isReactNative() && browser.supportsGetDisplayMedia()) {\n return this.obtainScreenFromGetDisplayMediaRN;\n } else if (browser.supportsGetDisplayMedia()) {\n return this.obtainScreenFromGetDisplayMedia;\n }\n logger.log('Screen sharing not supported on ', browser.getName());\n\n return null;\n },\n\n /**\n * Gets the appropriate constraints for audio sharing.\n *\n * @returns {Object|boolean}\n */\n _getAudioConstraints() {\n const { audioQuality } = this.options;\n const audio = audioQuality?.stereo ? {\n autoGainControl: false,\n channelCount: 2,\n echoCancellation: false,\n noiseSuppression: false\n } : true;\n\n return audio;\n },\n\n /**\n * Checks whether obtaining a screen capture is supported in the current\n * environment.\n * @returns {boolean}\n */\n isSupported() {\n return this.obtainStream !== null;\n },\n\n /**\n * Obtains a screen capture stream on Electron.\n *\n * @param onSuccess - Success callback.\n * @param onFailure - Failure callback.\n */\n obtainScreenOnElectron(onSuccess, onFailure) {\n if (window.JitsiMeetScreenObtainer && window.JitsiMeetScreenObtainer.openDesktopPicker) {\n const { desktopSharingFrameRate, desktopSharingSources } = this.options;\n\n window.JitsiMeetScreenObtainer.openDesktopPicker(\n {\n desktopSharingSources: desktopSharingSources || [ 'screen', 'window' ]\n },\n (streamId, streamType, screenShareAudio = false) => {\n if (streamId) {\n let audioConstraints = false;\n\n if (screenShareAudio) {\n audioConstraints = {};\n const optionalConstraints = this._getAudioConstraints();\n\n if (typeof optionalConstraints !== 'boolean') {\n audioConstraints = {\n optional: optionalConstraints\n };\n }\n\n // Audio screen sharing for electron only works for screen type devices.\n // i.e. when the user shares the whole desktop.\n // Note. The documentation specifies that chromeMediaSourceId should not be present\n // which, in the case a users has multiple monitors, leads to them being shared all\n // at once. However we tested with chromeMediaSourceId present and it seems to be\n // working properly.\n if (streamType === 'screen') {\n audioConstraints.mandatory = {\n chromeMediaSource: 'desktop'\n };\n }\n }\n\n const constraints = {\n audio: audioConstraints,\n video: {\n mandatory: {\n chromeMediaSource: 'desktop',\n chromeMediaSourceId: streamId,\n minFrameRate: desktopSharingFrameRate?.min ?? SS_DEFAULT_FRAME_RATE,\n maxFrameRate: desktopSharingFrameRate?.max ?? SS_DEFAULT_FRAME_RATE,\n maxWidth: window.screen.width,\n maxHeight: window.screen.height\n }\n }\n };\n\n // We have to use the old API on Electron to get a desktop stream.\n navigator.mediaDevices.getUserMedia(constraints)\n .then(stream => onSuccess({\n stream,\n sourceId: streamId,\n sourceType: streamType\n }), onFailure);\n } else {\n // As noted in Chrome Desktop Capture API:\n // If user didn't select any source (i.e. canceled the prompt)\n // then the callback is called with an empty streamId.\n onFailure(new JitsiTrackError(JitsiTrackErrors.SCREENSHARING_USER_CANCELED));\n }\n },\n err => onFailure(new JitsiTrackError(\n JitsiTrackErrors.ELECTRON_DESKTOP_PICKER_ERROR,\n err\n ))\n );\n } else {\n onFailure(new JitsiTrackError(JitsiTrackErrors.ELECTRON_DESKTOP_PICKER_NOT_FOUND));\n }\n },\n\n /**\n * Obtains a screen capture stream using getDisplayMedia.\n *\n * @param callback - The success callback.\n * @param errorCallback - The error callback.\n */\n obtainScreenFromGetDisplayMedia(callback, errorCallback) {\n let getDisplayMedia;\n\n if (navigator.getDisplayMedia) {\n getDisplayMedia = navigator.getDisplayMedia.bind(navigator);\n } else {\n // eslint-disable-next-line max-len\n getDisplayMedia = navigator.mediaDevices.getDisplayMedia.bind(navigator.mediaDevices);\n }\n\n const { desktopSharingFrameRate } = this.options;\n const video = typeof desktopSharingFrameRate === 'object' ? { frameRate: desktopSharingFrameRate } : true;\n const audio = this._getAudioConstraints();\n\n // At the time of this writing 'min' constraint for fps is not supported by getDisplayMedia.\n video.frameRate && delete video.frameRate.min;\n\n const constraints = {\n video,\n audio,\n cursor: 'always'\n };\n\n logger.info('Using getDisplayMedia for screen sharing', constraints);\n\n getDisplayMedia(constraints)\n .then(stream => {\n callback({\n stream,\n sourceId: stream.id\n });\n })\n .catch(error => {\n const errorDetails = {\n errorName: error && error.name,\n errorMsg: error && error.message,\n errorStack: error && error.stack\n };\n\n logger.error('getDisplayMedia error', constraints, errorDetails);\n\n if (errorDetails.errorMsg && errorDetails.errorMsg.indexOf('denied by system') !== -1) {\n // On Chrome this is the only thing different between error returned when user cancels\n // and when no permission was given on the OS level.\n errorCallback(new JitsiTrackError(JitsiTrackErrors.PERMISSION_DENIED));\n\n return;\n }\n\n errorCallback(new JitsiTrackError(JitsiTrackErrors.SCREENSHARING_USER_CANCELED));\n });\n },\n\n /**\n * Obtains a screen capture stream using getDisplayMedia.\n *\n * @param callback - The success callback.\n * @param errorCallback - The error callback.\n */\n obtainScreenFromGetDisplayMediaRN(callback, errorCallback) {\n logger.info('Using getDisplayMedia for screen sharing');\n\n navigator.mediaDevices.getDisplayMedia({ video: true })\n .then(stream => {\n callback({\n stream,\n sourceId: stream.id });\n })\n .catch(() => {\n errorCallback(new JitsiTrackError(JitsiTrackErrors\n .SCREENSHARING_USER_CANCELED));\n });\n },\n\n /**\n * Sets the max frame rate to be used for a desktop track capture.\n *\n * @param {number} maxFps capture frame rate to be used for desktop tracks.\n * @returns {void}\n */\n setDesktopSharingFrameRate(maxFps) {\n logger.info(`Setting the desktop capture rate to ${maxFps}`);\n\n this.options.desktopSharingFrameRate = {\n min: SS_DEFAULT_FRAME_RATE,\n max: maxFps\n };\n }\n};\n\nexport default ScreenObtainer;\n","import { getLogger } from '@jitsi/logger';\nconst logger = getLogger(__filename);\n\nimport CodecMimeType from '../../service/RTC/CodecMimeType';\nimport MediaDirection from '../../service/RTC/MediaDirection';\nimport browser from '../browser';\nimport RandomUtil from '../util/RandomUtil';\n\nconst SDPUtil = {\n filterSpecialChars(text) {\n // XXX Neither one of the falsy values (e.g. null, undefined, false,\n // \"\", etc.) \"contain\" special chars.\n // eslint-disable-next-line no-useless-escape\n return text ? text.replace(/[\\\\\\/\\{,\\}\\+]/g, '') : text;\n },\n iceparams(mediadesc, sessiondesc) {\n let data = null;\n let pwd, ufrag;\n\n if ((ufrag = SDPUtil.findLine(mediadesc, 'a=ice-ufrag:', sessiondesc))\n && (pwd\n = SDPUtil.findLine(\n mediadesc,\n 'a=ice-pwd:',\n sessiondesc))) {\n data = {\n ufrag: SDPUtil.parseICEUfrag(ufrag),\n pwd: SDPUtil.parseICEPwd(pwd)\n };\n }\n\n return data;\n },\n parseICEUfrag(line) {\n return line.substring(12);\n },\n buildICEUfrag(frag) {\n return `a=ice-ufrag:${frag}`;\n },\n parseICEPwd(line) {\n return line.substring(10);\n },\n buildICEPwd(pwd) {\n return `a=ice-pwd:${pwd}`;\n },\n parseMID(line) {\n return line.substring(6);\n },\n\n /**\n * Finds the MSID attribute in the given array of SSRC attribute lines and returns the value.\n *\n * @param {string[]} ssrcLines - an array of lines similar to 'a:213123 msid:stream-id track-id'.\n * @returns {undefined|string}\n */\n parseMSIDAttribute(ssrcLines) {\n const msidLine = ssrcLines.find(line => line.indexOf(' msid:') > 0);\n\n if (!msidLine) {\n return undefined;\n }\n\n const v = msidLine.substring(msidLine.indexOf(' msid:') + 6 /* the length of ' msid:' */);\n\n return SDPUtil.filterSpecialChars(v);\n },\n parseMLine(line) {\n const data = {};\n const parts = line.substring(2).split(' ');\n\n data.media = parts.shift();\n data.port = parts.shift();\n data.proto = parts.shift();\n if (parts[parts.length - 1] === '') { // trailing whitespace\n parts.pop();\n }\n data.fmt = parts;\n\n return data;\n },\n buildMLine(mline) {\n return (\n `m=${mline.media} ${mline.port} ${mline.proto} ${\n mline.fmt.join(' ')}`);\n },\n parseRTPMap(line) {\n const data = {};\n let parts = line.substring(9).split(' ');\n\n data.id = parts.shift();\n parts = parts[0].split('/');\n data.name = parts.shift();\n data.clockrate = parts.shift();\n data.channels = parts.length ? parts.shift() : '1';\n\n return data;\n },\n\n /**\n * Parses SDP line \"a=sctpmap:...\" and extracts SCTP port from it.\n * @param line eg. \"a=sctpmap:5000 webrtc-datachannel\"\n * @returns [SCTP port number, protocol, streams]\n */\n parseSCTPMap(line) {\n const parts = line.substring(10).split(' ');\n const sctpPort = parts[0];\n const protocol = parts[1];\n\n // Stream count is optional\n const streamCount = parts.length > 2 ? parts[2] : null;\n\n\n return [ sctpPort, protocol, streamCount ];// SCTP port\n },\n parseSCTPPort(line) {\n return line.substring(12);\n },\n buildRTPMap(el) {\n let line\n = `a=rtpmap:${el.getAttribute('id')} ${el.getAttribute('name')}/${\n el.getAttribute('clockrate')}`;\n\n if (el.getAttribute('channels')\n && el.getAttribute('channels') !== '1') {\n line += `/${el.getAttribute('channels')}`;\n }\n\n return line;\n },\n parseCrypto(line) {\n const data = {};\n const parts = line.substring(9).split(' ');\n\n data.tag = parts.shift();\n data['crypto-suite'] = parts.shift();\n data['key-params'] = parts.shift();\n if (parts.length) {\n data['session-params'] = parts.join(' ');\n }\n\n return data;\n },\n parseFingerprint(line) { // RFC 4572\n const data = {};\n const parts = line.substring(14).split(' ');\n\n data.hash = parts.shift();\n data.fingerprint = parts.shift();\n\n // TODO assert that fingerprint satisfies 2UHEX *(\":\" 2UHEX) ?\n return data;\n },\n parseFmtp(line) {\n const data = [];\n let parts = line.split(' ');\n\n parts.shift();\n parts = parts.join(' ').split(';');\n for (let i = 0; i < parts.length; i++) {\n let key = parts[i].split('=')[0];\n\n while (key.length && key[0] === ' ') {\n key = key.substring(1);\n }\n const value = parts[i].split('=')[1];\n\n if (key && value) {\n data.push({ name: key,\n value });\n } else if (key) {\n // rfc 4733 (DTMF) style stuff\n data.push({ name: '',\n value: key });\n }\n }\n\n return data;\n },\n parseICECandidate(line) {\n const candidate = {};\n const elems = line.split(' ');\n\n candidate.foundation = elems[0].substring(12);\n candidate.component = elems[1];\n candidate.protocol = elems[2].toLowerCase();\n candidate.priority = elems[3];\n candidate.ip = elems[4];\n candidate.port = elems[5];\n\n // elems[6] => \"typ\"\n candidate.type = elems[7];\n candidate.generation = 0; // default value, may be overwritten below\n for (let i = 8; i < elems.length; i += 2) {\n switch (elems[i]) {\n case 'raddr':\n candidate['rel-addr'] = elems[i + 1];\n break;\n case 'rport':\n candidate['rel-port'] = elems[i + 1];\n break;\n case 'generation':\n candidate.generation = elems[i + 1];\n break;\n case 'tcptype':\n candidate.tcptype = elems[i + 1];\n break;\n default: // TODO\n logger.debug(\n `parseICECandidate not translating \"${\n elems[i]}\" = \"${elems[i + 1]}\"`);\n }\n }\n candidate.network = '1';\n\n // not applicable to SDP -- FIXME: should be unique, not just random\n // eslint-disable-next-line newline-per-chained-call\n candidate.id = Math.random().toString(36).substr(2, 10);\n\n return candidate;\n },\n buildICECandidate(cand) {\n let line = [\n `a=candidate:${cand.foundation}`,\n cand.component,\n cand.protocol,\n cand.priority,\n cand.ip,\n cand.port,\n 'typ',\n cand.type\n ].join(' ');\n\n line += ' ';\n switch (cand.type) {\n case 'srflx':\n case 'prflx':\n case 'relay':\n if (cand.hasOwnAttribute('rel-addr')\n && cand.hasOwnAttribute('rel-port')) {\n line += 'raddr';\n line += ' ';\n line += cand['rel-addr'];\n line += ' ';\n line += 'rport';\n line += ' ';\n line += cand['rel-port'];\n line += ' ';\n }\n break;\n }\n if (cand.hasOwnAttribute('tcptype')) {\n line += 'tcptype';\n line += ' ';\n line += cand.tcptype;\n line += ' ';\n }\n line += 'generation';\n line += ' ';\n line += cand.hasOwnAttribute('generation') ? cand.generation : '0';\n\n return line;\n },\n parseSSRC(desc) {\n // proprietary mapping of a=ssrc lines\n // TODO: see \"Jingle RTP Source Description\" by Juberti and P. Thatcher\n // on google docs and parse according to that\n const data = new Map();\n const lines = desc.split('\\r\\n');\n\n for (let i = 0; i < lines.length; i++) {\n if (lines[i].substring(0, 7) === 'a=ssrc:') {\n // FIXME: Use regex to smartly find the ssrc.\n const ssrc = lines[i].split('a=ssrc:')[1].split(' ')[0];\n\n if (!data.get(ssrc)) {\n data.set(ssrc, []);\n }\n\n data.get(ssrc).push(lines[i]);\n }\n }\n\n return data;\n },\n\n /**\n * Gets the source name out of the name attribute \"a=ssrc:254321 name:name1\".\n *\n * @param {string[]} ssrcLines\n * @returns {string | undefined}\n */\n parseSourceNameLine(ssrcLines) {\n const sourceNameLine = ssrcLines.find(ssrcSdpLine => ssrcSdpLine.indexOf(' name:') > 0);\n\n // Everything past the \"name:\" part\n return sourceNameLine?.substring(sourceNameLine.indexOf(' name:') + 6);\n },\n parseRTCPFB(line) {\n const parts = line.substr(10).split(' ');\n const data = {};\n\n data.pt = parts.shift();\n data.type = parts.shift();\n data.params = parts;\n\n return data;\n },\n parseExtmap(line) {\n const parts = line.substr(9).split(' ');\n const data = {};\n\n data.value = parts.shift();\n if (data.value.indexOf('/') === -1) {\n data.direction = 'both';\n } else {\n data.direction = data.value.substr(data.value.indexOf('/') + 1);\n data.value = data.value.substr(0, data.value.indexOf('/'));\n }\n data.uri = parts.shift();\n data.params = parts;\n\n return data;\n },\n findLine(haystack, needle, sessionpart) {\n let lines = haystack.split('\\r\\n');\n\n for (let i = 0; i < lines.length; i++) {\n if (lines[i].substring(0, needle.length) === needle) {\n return lines[i];\n }\n }\n if (!sessionpart) {\n return false;\n }\n\n // search session part\n lines = sessionpart.split('\\r\\n');\n for (let j = 0; j < lines.length; j++) {\n if (lines[j].substring(0, needle.length) === needle) {\n return lines[j];\n }\n }\n\n return false;\n },\n findLines(haystack, needle, sessionpart) {\n let lines = haystack.split('\\r\\n');\n const needles = [];\n\n for (let i = 0; i < lines.length; i++) {\n if (lines[i].substring(0, needle.length) === needle) {\n needles.push(lines[i]);\n }\n }\n if (needles.length || !sessionpart) {\n return needles;\n }\n\n // search session part\n lines = sessionpart.split('\\r\\n');\n for (let j = 0; j < lines.length; j++) {\n if (lines[j].substring(0, needle.length) === needle) {\n needles.push(lines[j]);\n }\n }\n\n return needles;\n },\n candidateToJingle(line) {\n // a=candidate:2979166662 1 udp 2113937151 192.168.2.100 57698 typ host\n // generation 0\n // \n if (line.indexOf('candidate:') === 0) {\n // eslint-disable-next-line no-param-reassign\n line = `a=${line}`;\n } else if (line.substring(0, 12) !== 'a=candidate:') {\n logger.warn(\n 'parseCandidate called with a line that is not a candidate'\n + ' line');\n logger.warn(line);\n\n return null;\n }\n if (line.substring(line.length - 2) === '\\r\\n') { // chomp it\n // eslint-disable-next-line no-param-reassign\n line = line.substring(0, line.length - 2);\n }\n const candidate = {};\n const elems = line.split(' ');\n\n if (elems[6] !== 'typ') {\n logger.warn('did not find typ in the right place');\n logger.warn(line);\n\n return null;\n }\n candidate.foundation = elems[0].substring(12);\n candidate.component = elems[1];\n candidate.protocol = elems[2].toLowerCase();\n candidate.priority = elems[3];\n candidate.ip = elems[4];\n candidate.port = elems[5];\n\n // elems[6] => \"typ\"\n candidate.type = elems[7];\n\n candidate.generation = '0'; // default, may be overwritten below\n for (let i = 8; i < elems.length; i += 2) {\n switch (elems[i]) {\n case 'raddr':\n candidate['rel-addr'] = elems[i + 1];\n break;\n case 'rport':\n candidate['rel-port'] = elems[i + 1];\n break;\n case 'generation':\n candidate.generation = elems[i + 1];\n break;\n case 'tcptype':\n candidate.tcptype = elems[i + 1];\n break;\n default: // TODO\n logger.debug(`not translating \"${elems[i]}\" = \"${elems[i + 1]}\"`);\n }\n }\n candidate.network = '1';\n\n // not applicable to SDP -- FIXME: should be unique, not just random\n // eslint-disable-next-line newline-per-chained-call\n candidate.id = Math.random().toString(36).substr(2, 10);\n\n return candidate;\n },\n candidateFromJingle(cand) {\n let line = 'a=candidate:';\n\n line += cand.getAttribute('foundation');\n line += ' ';\n line += cand.getAttribute('component');\n line += ' ';\n\n let protocol = cand.getAttribute('protocol');\n\n // use tcp candidates for FF\n\n if (browser.isFirefox() && protocol.toLowerCase() === 'ssltcp') {\n protocol = 'tcp';\n }\n\n line += protocol; // .toUpperCase(); // chrome M23 doesn't like this\n line += ' ';\n line += cand.getAttribute('priority');\n line += ' ';\n line += cand.getAttribute('ip');\n line += ' ';\n line += cand.getAttribute('port');\n line += ' ';\n line += 'typ';\n line += ` ${cand.getAttribute('type')}`;\n line += ' ';\n switch (cand.getAttribute('type')) {\n case 'srflx':\n case 'prflx':\n case 'relay':\n if (cand.getAttribute('rel-addr')\n && cand.getAttribute('rel-port')) {\n line += 'raddr';\n line += ' ';\n line += cand.getAttribute('rel-addr');\n line += ' ';\n line += 'rport';\n line += ' ';\n line += cand.getAttribute('rel-port');\n line += ' ';\n }\n break;\n }\n if (protocol.toLowerCase() === 'tcp') {\n line += 'tcptype';\n line += ' ';\n line += cand.getAttribute('tcptype');\n line += ' ';\n }\n line += 'generation';\n line += ' ';\n line += cand.getAttribute('generation') || '0';\n\n return `${line}\\r\\n`;\n },\n\n /**\n * Parse the 'most' primary video ssrc from the given m line\n * @param {object} mLine object as parsed from transform.parse\n * @return {number} the primary video ssrc from the given m line\n */\n parsePrimaryVideoSsrc(videoMLine) {\n const numSsrcs = videoMLine.ssrcs\n .map(ssrcInfo => ssrcInfo.id)\n .filter((ssrc, index, array) => array.indexOf(ssrc) === index)\n .length;\n const numGroups\n = (videoMLine.ssrcGroups && videoMLine.ssrcGroups.length) || 0;\n\n if (numSsrcs > 1 && numGroups === 0) {\n // Ambiguous, can't figure out the primary\n return;\n }\n let primarySsrc = null;\n\n if (numSsrcs === 1) {\n primarySsrc = videoMLine.ssrcs[0].id;\n } else if (numSsrcs === 2) {\n // Can figure it out if there's an FID group\n const fidGroup\n = videoMLine.ssrcGroups.find(\n group => group.semantics === 'FID');\n\n if (fidGroup) {\n primarySsrc = fidGroup.ssrcs.split(' ')[0];\n }\n } else if (numSsrcs >= 3) {\n // Can figure it out if there's a sim group\n const simGroup\n = videoMLine.ssrcGroups.find(\n group => group.semantics === 'SIM');\n\n if (simGroup) {\n primarySsrc = simGroup.ssrcs.split(' ')[0];\n }\n }\n\n return primarySsrc;\n },\n\n /**\n * Generate an ssrc\n * @returns {number} an ssrc\n */\n generateSsrc() {\n return RandomUtil.randomInt(1, 0xffffffff);\n },\n\n /**\n * Get an attribute for the given ssrc with the given attributeName\n * from the given mline\n * @param {object} mLine an mLine object as parsed from transform.parse\n * @param {number} ssrc the ssrc for which an attribute is desired\n * @param {string} attributeName the name of the desired attribute\n * @returns {string} the value corresponding to the given ssrc\n * and attributeName\n */\n getSsrcAttribute(mLine, ssrc, attributeName) {\n for (let i = 0; i < mLine.ssrcs.length; ++i) {\n const ssrcLine = mLine.ssrcs[i];\n\n if (ssrcLine.id === ssrc\n && ssrcLine.attribute === attributeName) {\n return ssrcLine.value;\n }\n }\n },\n\n /**\n * Parses the ssrcs from the group sdp line and\n * returns them as a list of numbers\n * @param {object} the ssrcGroup object as parsed from\n * sdp-transform\n * @returns {list} a list of the ssrcs in the group\n * parsed as numbers\n */\n parseGroupSsrcs(ssrcGroup) {\n return ssrcGroup\n .ssrcs\n .split(' ')\n .map(ssrcStr => parseInt(ssrcStr, 10));\n },\n\n /**\n * Get the mline of the given type from the given sdp\n * @param {object} sdp sdp as parsed from transform.parse\n * @param {string} type the type of the desired mline (e.g. \"video\")\n * @returns {object} a media object\n */\n getMedia(sdp, type) {\n return sdp.media.find(m => m.type === type);\n },\n\n /**\n * Extracts the ICE username fragment from an SDP string.\n * @param {string} sdp the SDP in raw text format\n */\n getUfrag(sdp) {\n const ufragLines\n = sdp.split('\\n').filter(line => line.startsWith('a=ice-ufrag:'));\n\n if (ufragLines.length > 0) {\n return ufragLines[0].substr('a=ice-ufrag:'.length);\n }\n },\n\n /**\n * Sets the given codecName as the preferred codec by moving it to the beginning\n * of the payload types list (modifies the given mline in place). All instances\n * of the codec are moved up.\n * @param {object} mLine the mline object from an sdp as parsed by transform.parse\n * @param {string} codecName the name of the preferred codec\n */\n preferCodec(mline, codecName) {\n if (!mline || !codecName) {\n return;\n }\n\n const matchingPayloadTypes = mline.rtp\n .filter(rtp => rtp.codec && rtp.codec.toLowerCase() === codecName.toLowerCase())\n .map(rtp => rtp.payload);\n\n if (matchingPayloadTypes) {\n // Call toString() on payloads to get around an issue within SDPTransform that sets\n // payloads as a number, instead of a string, when there is only one payload.\n const payloadTypes\n = mline.payloads\n .toString()\n .split(' ')\n .map(p => parseInt(p, 10));\n\n for (const pt of matchingPayloadTypes.reverse()) {\n const payloadIndex = payloadTypes.indexOf(pt);\n\n payloadTypes.splice(payloadIndex, 1);\n payloadTypes.unshift(pt);\n }\n mline.payloads = payloadTypes.join(' ');\n }\n },\n\n /**\n * Strips the given codec from the given mline. All related RTX payload\n * types are also stripped. If the resulting mline would have no codecs,\n * it's disabled.\n *\n * @param {object} mLine the mline object from an sdp as parsed by transform.parse.\n * @param {string} codecName the name of the codec which will be stripped.\n * @param {boolean} highProfile determines if only the high profile H264 codec needs to be\n * stripped from the sdp when the passed codecName is H264.\n */\n stripCodec(mLine, codecName, highProfile = false) {\n if (!mLine || !codecName) {\n return;\n }\n\n const h264Pts = [];\n let removePts = [];\n const stripH264HighCodec = codecName.toLowerCase() === CodecMimeType.H264 && highProfile;\n\n for (const rtp of mLine.rtp) {\n if (rtp.codec\n && rtp.codec.toLowerCase() === codecName.toLowerCase()) {\n if (stripH264HighCodec) {\n h264Pts.push(rtp.payload);\n } else {\n removePts.push(rtp.payload);\n }\n }\n }\n\n // high profile H264 codecs have 64 as the first two bytes of the profile-level-id.\n if (stripH264HighCodec) {\n removePts = mLine.fmtp\n .filter(item => h264Pts.indexOf(item.payload) > -1 && item.config.includes('profile-level-id=64'))\n .map(item => item.payload);\n }\n\n if (removePts.length > 0) {\n // We also need to remove the payload types that are related to RTX\n // for the codecs we want to disable.\n const rtxApts = removePts.map(item => `apt=${item}`);\n const rtxPts = mLine.fmtp.filter(\n item => rtxApts.indexOf(item.config) !== -1);\n\n removePts.push(...rtxPts.map(item => item.payload));\n\n // Call toString() on payloads to get around an issue within\n // SDPTransform that sets payloads as a number, instead of a string,\n // when there is only one payload.\n const allPts = mLine.payloads\n .toString()\n .split(' ')\n .map(Number);\n const keepPts = allPts.filter(pt => removePts.indexOf(pt) === -1);\n\n if (keepPts.length === 0) {\n // There are no other codecs, disable the stream.\n mLine.port = 0;\n mLine.direction = MediaDirection.INACTIVE;\n mLine.payloads = '*';\n } else {\n mLine.payloads = keepPts.join(' ');\n }\n\n mLine.rtp = mLine.rtp.filter(\n item => keepPts.indexOf(item.payload) !== -1);\n mLine.fmtp = mLine.fmtp.filter(\n item => keepPts.indexOf(item.payload) !== -1);\n if (mLine.rtcpFb) {\n mLine.rtcpFb = mLine.rtcpFb.filter(\n item => keepPts.indexOf(item.payload) !== -1);\n }\n }\n }\n};\n\nexport default SDPUtil;\n","/* global $ */\n\nimport clonedeep from 'lodash.clonedeep';\nimport transform from 'sdp-transform';\n\nimport MediaDirection from '../../service/RTC/MediaDirection';\nimport browser from '../browser';\nimport FeatureFlags from '../flags/FeatureFlags';\n\nimport SDPUtil from './SDPUtil';\n\n/**\n *\n * @param sdp\n */\nexport default function SDP(sdp) {\n const media = sdp.split('\\r\\nm=');\n\n for (let i = 1, length = media.length; i < length; i++) {\n let mediaI = `m=${media[i]}`;\n\n if (i !== length - 1) {\n mediaI += '\\r\\n';\n }\n media[i] = mediaI;\n }\n const session = `${media.shift()}\\r\\n`;\n\n this.media = media;\n this.raw = session + media.join('');\n this.session = session;\n}\n\n/**\n * A flag will make {@link transportToJingle} and {@link jingle2media} replace\n * ICE candidates IPs with invalid value of '1.1.1.1' which will cause ICE\n * failure. The flag is used in the automated testing.\n * @type {boolean}\n */\nSDP.prototype.failICE = false;\n\n/**\n * Whether or not to remove TCP ice candidates when translating from/to jingle.\n * @type {boolean}\n */\nSDP.prototype.removeTcpCandidates = false;\n\n/**\n * Whether or not to remove UDP ice candidates when translating from/to jingle.\n * @type {boolean}\n */\nSDP.prototype.removeUdpCandidates = false;\n\n/**\n * Adds a new m-line to the description so that a new local source can then be attached to the transceiver that gets\n * added after a reneogtiation cycle.\n *\n * @param {Mediatype} mediaType media type of the new source that is being added.\n */\nSDP.prototype.addMlineForNewLocalSource = function(mediaType) {\n const mid = this.media.length;\n const sdp = transform.parse(this.raw);\n const mline = clonedeep(sdp.media.find(m => m.type === mediaType));\n\n // Edit media direction, mid and remove the existing ssrc lines in the m-line.\n mline.mid = mid;\n mline.direction = MediaDirection.RECVONLY;\n\n // Remove the ssrcs and source groups.\n mline.msid = undefined;\n mline.ssrcs = undefined;\n mline.ssrcGroups = undefined;\n\n sdp.media = sdp.media.concat(mline);\n\n // We regenerate the BUNDLE group (since we added a new m-line)\n sdp.groups.forEach(group => {\n if (group.type === 'BUNDLE') {\n const mids = group.mids.split(' ');\n\n mids.push(mid);\n group.mids = mids.join(' ');\n }\n });\n this.raw = transform.write(sdp);\n};\n\n/**\n * Returns map of MediaChannel mapped per channel idx.\n */\nSDP.prototype.getMediaSsrcMap = function() {\n const mediaSSRCs = {};\n\n for (let mediaindex = 0; mediaindex < this.media.length; mediaindex++) {\n const mid\n = SDPUtil.parseMID(\n SDPUtil.findLine(this.media[mediaindex], 'a=mid:'));\n const media = {\n mediaindex,\n mid,\n ssrcs: {},\n ssrcGroups: []\n };\n\n mediaSSRCs[mediaindex] = media;\n\n SDPUtil.findLines(this.media[mediaindex], 'a=ssrc:').forEach(line => {\n const linessrc = line.substring(7).split(' ')[0];\n\n // allocate new ChannelSsrc\n\n if (!media.ssrcs[linessrc]) {\n media.ssrcs[linessrc] = {\n ssrc: linessrc,\n lines: []\n };\n }\n media.ssrcs[linessrc].lines.push(line);\n });\n SDPUtil.findLines(this.media[mediaindex], 'a=ssrc-group:').forEach(line => {\n const idx = line.indexOf(' ');\n const semantics = line.substr(0, idx).substr(13);\n const ssrcs = line.substr(14 + semantics.length).split(' ');\n\n if (ssrcs.length) {\n media.ssrcGroups.push({\n semantics,\n ssrcs\n });\n }\n });\n }\n\n return mediaSSRCs;\n};\n\n/**\n * Returns true if this SDP contains given SSRC.\n * @param ssrc the ssrc to check.\n * @returns {boolean} true if this SDP contains given SSRC.\n */\nSDP.prototype.containsSSRC = function(ssrc) {\n // FIXME this code is really strange - improve it if you can\n const medias = this.getMediaSsrcMap();\n let result = false;\n\n Object.keys(medias).forEach(mediaindex => {\n if (result) {\n return;\n }\n if (medias[mediaindex].ssrcs[ssrc]) {\n result = true;\n }\n });\n\n return result;\n};\n\n// add content's to a jingle element\nSDP.prototype.toJingle = function(elem, thecreator) {\n // https://xmpp.org/extensions/xep-0338.html\n SDPUtil.findLines(this.session, 'a=group:').forEach(line => {\n const parts = line.split(' ');\n const semantics = parts.shift().substr(8);\n\n elem.c('group', { xmlns: 'urn:xmpp:jingle:apps:grouping:0',\n semantics });\n for (let j = 0; j < parts.length; j++) {\n elem.c('content', { name: parts[j] }).up();\n }\n elem.up();\n });\n\n for (let i = 0; i < this.media.length; i++) {\n const mline = SDPUtil.parseMLine(this.media[i].split('\\r\\n')[0]);\n\n if (!(mline.media === 'audio'\n || mline.media === 'video'\n || mline.media === 'application')) {\n continue; // eslint-disable-line no-continue\n }\n\n let ssrc;\n const assrcline = SDPUtil.findLine(this.media[i], 'a=ssrc:');\n\n if (assrcline) {\n ssrc = assrcline.substring(7).split(' ')[0]; // take the first\n } else {\n ssrc = false;\n }\n\n elem.c('content', { creator: thecreator,\n name: mline.media });\n const amidline = SDPUtil.findLine(this.media[i], 'a=mid:');\n\n if (amidline) {\n // prefer identifier from a=mid if present\n const mid = SDPUtil.parseMID(amidline);\n\n elem.attrs({ name: mid });\n }\n\n if (mline.media === 'audio' || mline.media === 'video') {\n elem.c('description',\n { xmlns: 'urn:xmpp:jingle:apps:rtp:1',\n media: mline.media });\n if (ssrc) {\n elem.attrs({ ssrc });\n }\n for (let j = 0; j < mline.fmt.length; j++) {\n const rtpmap\n = SDPUtil.findLine(\n this.media[i],\n `a=rtpmap:${mline.fmt[j]}`);\n\n elem.c('payload-type', SDPUtil.parseRTPMap(rtpmap));\n\n // put any 'a=fmtp:' + mline.fmt[j] lines into \n const afmtpline\n = SDPUtil.findLine(\n this.media[i],\n `a=fmtp:${mline.fmt[j]}`);\n\n if (afmtpline) {\n const fmtpParameters = SDPUtil.parseFmtp(afmtpline);\n\n // eslint-disable-next-line max-depth\n for (let k = 0; k < fmtpParameters.length; k++) {\n elem.c('parameter', fmtpParameters[k]).up();\n }\n }\n\n // XEP-0293 -- map a=rtcp-fb\n this.rtcpFbToJingle(i, elem, mline.fmt[j]);\n\n elem.up();\n }\n\n if (ssrc) {\n const ssrcMap = SDPUtil.parseSSRC(this.media[i]);\n\n for (const [ availableSsrc, ssrcParameters ] of ssrcMap) {\n const sourceName = SDPUtil.parseSourceNameLine(ssrcParameters);\n\n elem.c('source', {\n ssrc: availableSsrc,\n name: FeatureFlags.isSourceNameSignalingEnabled() ? sourceName : undefined,\n xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0'\n });\n\n const msid = SDPUtil.parseMSIDAttribute(ssrcParameters);\n\n // eslint-disable-next-line max-depth\n if (msid) {\n elem.c('parameter');\n elem.attrs({ name: 'msid' });\n elem.attrs({ value: msid });\n elem.up();\n }\n\n elem.up();\n }\n\n // XEP-0339 handle ssrc-group attributes\n const ssrcGroupLines\n = SDPUtil.findLines(this.media[i], 'a=ssrc-group:');\n\n ssrcGroupLines.forEach(line => {\n const idx = line.indexOf(' ');\n const semantics = line.substr(0, idx).substr(13);\n const ssrcs = line.substr(14 + semantics.length).split(' ');\n\n if (ssrcs.length) {\n elem.c('ssrc-group', { semantics,\n xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0' });\n ssrcs.forEach(s => elem.c('source', { ssrc: s }).up());\n elem.up();\n }\n });\n }\n\n const ridLines = SDPUtil.findLines(this.media[i], 'a=rid:');\n\n if (ridLines.length && browser.usesRidsForSimulcast()) {\n // Map a line which looks like \"a=rid:2 send\" to just\n // the rid (\"2\")\n const rids = ridLines\n .map(ridLine => ridLine.split(':')[1])\n .map(ridInfo => ridInfo.split(' ')[0]);\n\n rids.forEach(rid => {\n elem.c('source', {\n rid,\n xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0'\n });\n elem.up();\n });\n const unifiedSimulcast\n = SDPUtil.findLine(this.media[i], 'a=simulcast:');\n\n if (unifiedSimulcast) {\n elem.c('rid-group', {\n semantics: 'SIM',\n xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0'\n });\n rids.forEach(rid => {\n elem.c('source', { rid }).up();\n });\n elem.up();\n }\n }\n\n if (SDPUtil.findLine(this.media[i], 'a=rtcp-mux')) {\n elem.c('rtcp-mux').up();\n }\n\n // XEP-0293 -- map a=rtcp-fb:*\n this.rtcpFbToJingle(i, elem, '*');\n\n // XEP-0294\n const extmapLines = SDPUtil.findLines(this.media[i], 'a=extmap:');\n\n for (let j = 0; j < extmapLines.length; j++) {\n const extmap = SDPUtil.parseExtmap(extmapLines[j]);\n\n elem.c('rtp-hdrext', {\n xmlns: 'urn:xmpp:jingle:apps:rtp:rtp-hdrext:0',\n uri: extmap.uri,\n id: extmap.value\n });\n\n // eslint-disable-next-line max-depth\n if (extmap.hasOwnProperty('direction')) {\n\n // eslint-disable-next-line max-depth\n switch (extmap.direction) {\n case MediaDirection.SENDONLY:\n elem.attrs({ senders: 'responder' });\n break;\n case MediaDirection.RECVONLY:\n elem.attrs({ senders: 'initiator' });\n break;\n case MediaDirection.SENDRECV:\n elem.attrs({ senders: 'both' });\n break;\n case MediaDirection.INACTIVE:\n elem.attrs({ senders: 'none' });\n break;\n }\n }\n\n // TODO: handle params\n elem.up();\n }\n elem.up(); // end of description\n }\n\n // map ice-ufrag/pwd, dtls fingerprint, candidates\n this.transportToJingle(i, elem);\n\n const m = this.media[i];\n\n if (SDPUtil.findLine(m, `a=${MediaDirection.SENDRECV}`, this.session)) {\n elem.attrs({ senders: 'both' });\n } else if (SDPUtil.findLine(m, `a=${MediaDirection.SENDONLY}`, this.session)) {\n elem.attrs({ senders: 'initiator' });\n } else if (SDPUtil.findLine(m, `a=${MediaDirection.RECVONLY}`, this.session)) {\n elem.attrs({ senders: 'responder' });\n } else if (SDPUtil.findLine(m, `a=${MediaDirection.INACTIVE}`, this.session)) {\n elem.attrs({ senders: 'none' });\n }\n\n // Reject an m-line only when port is 0 and a=bundle-only is not present in the section.\n // The port is automatically set to 0 when bundle-only is used.\n if (mline.port === '0' && !SDPUtil.findLine(m, 'a=bundle-only', this.session)) {\n // estos hack to reject an m-line\n elem.attrs({ senders: 'rejected' });\n }\n elem.up(); // end of content\n }\n elem.up();\n\n return elem;\n};\n\nSDP.prototype.transportToJingle = function(mediaindex, elem) {\n elem.c('transport');\n\n // XEP-0343 DTLS/SCTP\n const sctpport\n = SDPUtil.findLine(this.media[mediaindex], 'a=sctp-port:', this.session);\n const sctpmap\n = SDPUtil.findLine(this.media[mediaindex], 'a=sctpmap:', this.session);\n\n if (sctpport) {\n const sctpAttrs = SDPUtil.parseSCTPPort(sctpport);\n\n elem.c('sctpmap', {\n xmlns: 'urn:xmpp:jingle:transports:dtls-sctp:1',\n number: sctpAttrs, /* SCTP port */\n protocol: 'webrtc-datachannel' /* protocol */\n });\n\n // The parser currently requires streams to be present\n elem.attrs({ streams: 0 });\n elem.up();\n } else if (sctpmap) {\n const sctpAttrs = SDPUtil.parseSCTPMap(sctpmap);\n\n elem.c('sctpmap', {\n xmlns: 'urn:xmpp:jingle:transports:dtls-sctp:1',\n number: sctpAttrs[0], /* SCTP port */\n protocol: sctpAttrs[1] /* protocol */\n });\n\n // Optional stream count attribute\n if (sctpAttrs.length > 2) {\n elem.attrs({ streams: sctpAttrs[2] });\n } else {\n elem.attrs({ streams: 0 });\n }\n elem.up();\n }\n\n // XEP-0320\n const fingerprints\n = SDPUtil.findLines(\n this.media[mediaindex],\n 'a=fingerprint:',\n this.session);\n\n fingerprints.forEach(line => {\n const fingerprint = SDPUtil.parseFingerprint(line);\n\n fingerprint.xmlns = 'urn:xmpp:jingle:apps:dtls:0';\n elem.c('fingerprint').t(fingerprint.fingerprint);\n delete fingerprint.fingerprint;\n\n const setupLine\n = SDPUtil.findLine(\n this.media[mediaindex],\n 'a=setup:',\n this.session);\n\n if (setupLine) {\n fingerprint.setup = setupLine.substr(8);\n }\n elem.attrs(fingerprint);\n elem.up(); // end of fingerprint\n });\n const iceParameters = SDPUtil.iceparams(this.media[mediaindex], this.session);\n\n if (iceParameters) {\n iceParameters.xmlns = 'urn:xmpp:jingle:transports:ice-udp:1';\n elem.attrs(iceParameters);\n\n // XEP-0176\n const candidateLines\n = SDPUtil.findLines(\n this.media[mediaindex],\n 'a=candidate:',\n this.session);\n\n candidateLines.forEach(line => { // add any a=candidate lines\n const candidate = SDPUtil.candidateToJingle(line);\n\n if (this.failICE) {\n candidate.ip = '1.1.1.1';\n }\n const protocol\n = candidate && typeof candidate.protocol === 'string'\n ? candidate.protocol.toLowerCase()\n : '';\n\n if ((this.removeTcpCandidates\n && (protocol === 'tcp' || protocol === 'ssltcp'))\n || (this.removeUdpCandidates && protocol === 'udp')) {\n return;\n }\n elem.c('candidate', candidate).up();\n });\n }\n elem.up(); // end of transport\n};\n\n// XEP-0293\nSDP.prototype.rtcpFbToJingle = function(mediaindex, elem, payloadtype) {\n const lines\n = SDPUtil.findLines(\n this.media[mediaindex],\n `a=rtcp-fb:${payloadtype}`);\n\n lines.forEach(line => {\n const feedback = SDPUtil.parseRTCPFB(line);\n\n if (feedback.type === 'trr-int') {\n elem.c('rtcp-fb-trr-int', {\n xmlns: 'urn:xmpp:jingle:apps:rtp:rtcp-fb:0',\n value: feedback.params[0]\n });\n elem.up();\n } else {\n elem.c('rtcp-fb', {\n xmlns: 'urn:xmpp:jingle:apps:rtp:rtcp-fb:0',\n type: feedback.type\n });\n if (feedback.params.length > 0) {\n elem.attrs({ 'subtype': feedback.params[0] });\n }\n elem.up();\n }\n });\n};\n\nSDP.prototype.rtcpFbFromJingle = function(elem, payloadtype) { // XEP-0293\n let sdp = '';\n const feedbackElementTrrInt\n = elem.find(\n '>rtcp-fb-trr-int[xmlns=\"urn:xmpp:jingle:apps:rtp:rtcp-fb:0\"]');\n\n if (feedbackElementTrrInt.length) {\n sdp += 'a=rtcp-fb:* trr-int ';\n if (feedbackElementTrrInt.attr('value')) {\n sdp += feedbackElementTrrInt.attr('value');\n } else {\n sdp += '0';\n }\n sdp += '\\r\\n';\n }\n\n const feedbackElements = elem.find('>rtcp-fb[xmlns=\"urn:xmpp:jingle:apps:rtp:rtcp-fb:0\"]');\n\n feedbackElements.each((_, fb) => {\n sdp += `a=rtcp-fb:${payloadtype} ${fb.getAttribute('type')}`;\n if (fb.hasAttribute('subtype')) {\n sdp += ` ${fb.getAttribute('subtype')}`;\n }\n sdp += '\\r\\n';\n });\n\n return sdp;\n};\n\n// construct an SDP from a jingle stanza\nSDP.prototype.fromJingle = function(jingle) {\n const sessionId = Date.now();\n\n // Use a unique session id for every TPC.\n this.raw = 'v=0\\r\\n'\n + `o=- ${sessionId} 2 IN IP4 0.0.0.0\\r\\n`\n + 's=-\\r\\n'\n + 't=0 0\\r\\n';\n\n // http://tools.ietf.org/html/draft-ietf-mmusic-sdp-bundle-negotiation-04\n // #section-8\n const groups\n = $(jingle).find('>group[xmlns=\"urn:xmpp:jingle:apps:grouping:0\"]');\n\n if (groups.length) {\n groups.each((idx, group) => {\n const contents\n = $(group)\n .find('>content')\n .map((_, content) => content.getAttribute('name'))\n .get();\n\n if (contents.length > 0) {\n this.raw\n += `a=group:${\n group.getAttribute('semantics')\n || group.getAttribute('type')} ${\n contents.join(' ')}\\r\\n`;\n }\n });\n }\n\n this.session = this.raw;\n jingle.find('>content').each((_, content) => {\n const m = this.jingle2media($(content));\n\n this.media.push(m);\n });\n\n // reconstruct msid-semantic -- apparently not necessary\n /*\n var msid = SDPUtil.parseSSRC(this.raw);\n if (msid.hasOwnProperty('mslabel')) {\n this.session += \"a=msid-semantic: WMS \" + msid.mslabel + \"\\r\\n\";\n }\n */\n\n this.raw = this.session + this.media.join('');\n};\n\n// translate a jingle content element into an an SDP media part\nSDP.prototype.jingle2media = function(content) {\n const desc = content.find('>description');\n const transport = content.find('>transport[xmlns=\"urn:xmpp:jingle:transports:ice-udp:1\"]');\n let sdp = '';\n const sctp = transport.find(\n '>sctpmap[xmlns=\"urn:xmpp:jingle:transports:dtls-sctp:1\"]');\n\n const media = { media: desc.attr('media') };\n\n media.port = '9';\n if (content.attr('senders') === 'rejected') {\n // estos hack to reject an m-line.\n media.port = '0';\n }\n if (transport.find('>fingerprint[xmlns=\"urn:xmpp:jingle:apps:dtls:0\"]').length) {\n media.proto = sctp.length ? 'UDP/DTLS/SCTP' : 'UDP/TLS/RTP/SAVPF';\n } else {\n media.proto = 'UDP/TLS/RTP/SAVPF';\n }\n if (sctp.length) {\n sdp += `m=application ${media.port} UDP/DTLS/SCTP webrtc-datachannel\\r\\n`;\n sdp += `a=sctp-port:${sctp.attr('number')}\\r\\n`;\n sdp += 'a=max-message-size:262144\\r\\n';\n } else {\n media.fmt\n = desc\n .find('>payload-type')\n .map((_, payloadType) => payloadType.getAttribute('id'))\n .get();\n sdp += `${SDPUtil.buildMLine(media)}\\r\\n`;\n }\n\n sdp += 'c=IN IP4 0.0.0.0\\r\\n';\n if (!sctp.length) {\n sdp += 'a=rtcp:1 IN IP4 0.0.0.0\\r\\n';\n }\n\n // XEP-0176 ICE parameters\n if (transport.length) {\n if (transport.attr('ufrag')) {\n sdp += `${SDPUtil.buildICEUfrag(transport.attr('ufrag'))}\\r\\n`;\n }\n if (transport.attr('pwd')) {\n sdp += `${SDPUtil.buildICEPwd(transport.attr('pwd'))}\\r\\n`;\n }\n transport.find('>fingerprint[xmlns=\"urn:xmpp:jingle:apps:dtls:0\"]').each((_, fingerprint) => {\n sdp += `a=fingerprint:${fingerprint.getAttribute('hash')}`;\n sdp += ` ${$(fingerprint).text()}`;\n sdp += '\\r\\n';\n if (fingerprint.hasAttribute('setup')) {\n sdp += `a=setup:${fingerprint.getAttribute('setup')}\\r\\n`;\n }\n });\n }\n\n // XEP-0176 ICE candidates\n transport.find('>candidate')\n .each((_, candidate) => {\n let protocol = candidate.getAttribute('protocol');\n\n protocol\n = typeof protocol === 'string' ? protocol.toLowerCase() : '';\n\n if ((this.removeTcpCandidates\n && (protocol === 'tcp' || protocol === 'ssltcp'))\n || (this.removeUdpCandidates && protocol === 'udp')) {\n return;\n } else if (this.failICE) {\n candidate.setAttribute('ip', '1.1.1.1');\n }\n\n sdp += SDPUtil.candidateFromJingle(candidate);\n });\n\n switch (content.attr('senders')) {\n case 'initiator':\n sdp += `a=${MediaDirection.SENDONLY}\\r\\n`;\n break;\n case 'responder':\n sdp += `a=${MediaDirection.RECVONLY}\\r\\n`;\n break;\n case 'none':\n sdp += `a=${MediaDirection.INACTIVE}\\r\\n`;\n break;\n case 'both':\n sdp += `a=${MediaDirection.SENDRECV}\\r\\n`;\n break;\n }\n sdp += `a=mid:${content.attr('name')}\\r\\n`;\n\n // \n // see http://code.google.com/p/libjingle/issues/detail?id=309 -- no spec\n // though\n // and http://mail.jabber.org/pipermail/jingle/2011-December/001761.html\n if (desc.find('>rtcp-mux').length) {\n sdp += 'a=rtcp-mux\\r\\n';\n }\n\n desc.find('>payload-type').each((_, payloadType) => {\n sdp += `${SDPUtil.buildRTPMap(payloadType)}\\r\\n`;\n if ($(payloadType).find('>parameter').length) {\n sdp += `a=fmtp:${payloadType.getAttribute('id')} `;\n sdp\n += $(payloadType)\n .find('>parameter')\n .map((__, parameter) => {\n const name = parameter.getAttribute('name');\n\n return (\n (name ? `${name}=` : '')\n + parameter.getAttribute('value'));\n })\n .get()\n .join('; ');\n sdp += '\\r\\n';\n }\n\n // xep-0293\n sdp += this.rtcpFbFromJingle($(payloadType), payloadType.getAttribute('id'));\n });\n\n // xep-0293\n sdp += this.rtcpFbFromJingle(desc, '*');\n\n // xep-0294\n desc\n .find('>rtp-hdrext[xmlns=\"urn:xmpp:jingle:apps:rtp:rtp-hdrext:0\"]')\n .each((_, hdrExt) => {\n sdp\n += `a=extmap:${hdrExt.getAttribute('id')} ${\n hdrExt.getAttribute('uri')}\\r\\n`;\n });\n\n // XEP-0339 handle ssrc-group attributes\n desc\n .find('>ssrc-group[xmlns=\"urn:xmpp:jingle:apps:rtp:ssma:0\"]')\n .each((_, ssrcGroup) => {\n const semantics = ssrcGroup.getAttribute('semantics');\n const ssrcs\n = $(ssrcGroup)\n .find('>source')\n .map((__, source) => source.getAttribute('ssrc'))\n .get();\n\n if (ssrcs.length) {\n sdp += `a=ssrc-group:${semantics} ${ssrcs.join(' ')}\\r\\n`;\n }\n });\n\n // XEP-0339 handle source attributes\n let userSources = '';\n let nonUserSources = '';\n\n desc\n .find('>source[xmlns=\"urn:xmpp:jingle:apps:rtp:ssma:0\"]')\n .each((_, source) => {\n const ssrc = source.getAttribute('ssrc');\n let isUserSource = true;\n let sourceStr = '';\n\n $(source)\n .find('>parameter')\n .each((__, parameter) => {\n const name = parameter.getAttribute('name');\n let value = parameter.getAttribute('value');\n\n value = SDPUtil.filterSpecialChars(value);\n sourceStr += `a=ssrc:${ssrc} ${name}`;\n\n if (value && value.length) {\n sourceStr += `:${value}`;\n }\n\n sourceStr += '\\r\\n';\n\n if (value?.includes('mixedmslabel')) {\n isUserSource = false;\n }\n });\n\n if (isUserSource) {\n userSources += sourceStr;\n } else {\n nonUserSources += sourceStr;\n }\n });\n\n // The sdp-interop package is relying the mixedmslabel m line to be the first one in order to set the direction\n // to sendrecv.\n sdp += nonUserSources + userSources;\n\n return sdp;\n};\n","import FeatureFlags from '../flags/FeatureFlags';\n\nimport SDPUtil from './SDPUtil';\n\n// this could be useful in Array.prototype.\n/**\n *\n * @param array1\n * @param array2\n */\nfunction arrayEquals(array1, array2) {\n // if the other array is a falsy value, return\n if (!array2) {\n return false;\n }\n\n // compare lengths - can save a lot of time\n if (array1.length !== array2.length) {\n return false;\n }\n\n for (let i = 0, l = array1.length; i < l; i++) {\n // Check if we have nested arrays\n if (array1[i] instanceof Array && array2[i] instanceof Array) {\n // recurse into the nested arrays\n if (!array1[i].equals(array2[i])) {\n return false;\n }\n } else if (array1[i] !== array2[i]) {\n // Warning - two different object instances will never be\n // equal: {x:20} != {x:20}\n return false;\n }\n }\n\n return true;\n}\n\n/**\n *\n * @param mySDP\n * @param otherSDP\n */\nexport default function SDPDiffer(mySDP, otherSDP) {\n this.mySDP = mySDP;\n this.otherSDP = otherSDP;\n if (!mySDP) {\n throw new Error('\"mySDP\" is undefined!');\n } else if (!otherSDP) {\n throw new Error('\"otherSDP\" is undefined!');\n }\n}\n\n/**\n * Returns map of MediaChannel that contains media contained in\n * 'mySDP', but not contained in 'otherSdp'. Mapped by channel idx.\n */\nSDPDiffer.prototype.getNewMedia = function() {\n\n const myMedias = this.mySDP.getMediaSsrcMap();\n const othersMedias = this.otherSDP.getMediaSsrcMap();\n const newMedia = {};\n\n Object.keys(othersMedias).forEach(othersMediaIdx => {\n const myMedia = myMedias[othersMediaIdx];\n const othersMedia = othersMedias[othersMediaIdx];\n\n if (!myMedia && othersMedia) {\n // Add whole channel\n newMedia[othersMediaIdx] = othersMedia;\n\n return;\n }\n\n // Look for new ssrcs across the channel\n Object.keys(othersMedia.ssrcs).forEach(ssrc => {\n if (Object.keys(myMedia.ssrcs).indexOf(ssrc) === -1) {\n // Allocate channel if we've found ssrc that doesn't exist in\n // our channel\n if (!newMedia[othersMediaIdx]) {\n newMedia[othersMediaIdx] = {\n mediaindex: othersMedia.mediaindex,\n mid: othersMedia.mid,\n ssrcs: {},\n ssrcGroups: []\n };\n }\n newMedia[othersMediaIdx].ssrcs[ssrc] = othersMedia.ssrcs[ssrc];\n } else if (othersMedia.ssrcs[ssrc].lines\n && myMedia.ssrcs[ssrc].lines) {\n // we want to detect just changes in adding/removing msid\n const myContainMsid = myMedia.ssrcs[ssrc].lines.find(\n line => line.indexOf('msid') !== -1) !== undefined;\n const newContainMsid = othersMedia.ssrcs[ssrc].lines.find(\n line => line.indexOf('msid') !== -1) !== undefined;\n\n if (myContainMsid !== newContainMsid) {\n if (!newMedia[othersMediaIdx]) {\n newMedia[othersMediaIdx] = {\n mediaindex: othersMedia.mediaindex,\n mid: othersMedia.mid,\n ssrcs: {},\n ssrcGroups: []\n };\n }\n newMedia[othersMediaIdx].ssrcs[ssrc]\n = othersMedia.ssrcs[ssrc];\n }\n }\n });\n\n // Look for new ssrc groups across the channels\n othersMedia.ssrcGroups.forEach(otherSsrcGroup => {\n\n // try to match the other ssrc-group with an ssrc-group of ours\n let matched = false;\n\n for (let i = 0; i < myMedia.ssrcGroups.length; i++) {\n const mySsrcGroup = myMedia.ssrcGroups[i];\n\n if (otherSsrcGroup.semantics === mySsrcGroup.semantics\n && arrayEquals(otherSsrcGroup.ssrcs, mySsrcGroup.ssrcs)) {\n\n matched = true;\n break;\n }\n }\n\n if (!matched) {\n // Allocate channel if we've found an ssrc-group that doesn't\n // exist in our channel\n\n if (!newMedia[othersMediaIdx]) {\n newMedia[othersMediaIdx] = {\n mediaindex: othersMedia.mediaindex,\n mid: othersMedia.mid,\n ssrcs: {},\n ssrcGroups: []\n };\n }\n newMedia[othersMediaIdx].ssrcGroups.push(otherSsrcGroup);\n }\n });\n });\n\n return newMedia;\n};\n\n/**\n * TODO: document!\n */\nSDPDiffer.prototype.toJingle = function(modify) {\n const sdpMediaSsrcs = this.getNewMedia();\n\n let modified = false;\n\n Object.keys(sdpMediaSsrcs).forEach(mediaindex => {\n modified = true;\n const media = sdpMediaSsrcs[mediaindex];\n\n modify.c('content', { name: media.mid });\n\n modify.c('description',\n { xmlns: 'urn:xmpp:jingle:apps:rtp:1',\n media: media.mid });\n\n // FIXME: not completely sure this operates on blocks and / or handles\n // different ssrcs correctly\n // generate sources from lines\n Object.keys(media.ssrcs).forEach(ssrcNum => {\n const mediaSsrc = media.ssrcs[ssrcNum];\n const ssrcLines = mediaSsrc.lines;\n const sourceName = SDPUtil.parseSourceNameLine(ssrcLines);\n\n modify.c('source', { xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0' });\n modify.attrs({\n name: FeatureFlags.isSourceNameSignalingEnabled() ? sourceName : undefined,\n ssrc: mediaSsrc.ssrc\n });\n\n // Only MSID attribute is sent\n const msid = SDPUtil.parseMSIDAttribute(ssrcLines);\n\n if (msid) {\n modify.c('parameter');\n modify.attrs({ name: 'msid' });\n modify.attrs({ value: msid });\n modify.up();\n }\n\n modify.up(); // end of source\n });\n\n // generate source groups from lines\n media.ssrcGroups.forEach(ssrcGroup => {\n if (ssrcGroup.ssrcs.length) {\n\n modify.c('ssrc-group', {\n semantics: ssrcGroup.semantics,\n xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0'\n });\n\n ssrcGroup.ssrcs.forEach(ssrc => {\n modify.c('source', { ssrc })\n .up(); // end of source\n });\n modify.up(); // end of ssrc-group\n }\n });\n\n modify.up(); // end of description\n modify.up(); // end of content\n });\n\n return modified;\n};\n","import { getLogger } from '@jitsi/logger';\nimport async from 'async';\n\nconst logger = getLogger(__filename);\n\n/**\n * A queue for async task execution.\n */\nexport default class AsyncQueue {\n /**\n * Creates new instance.\n */\n constructor() {\n this._queue = async.queue(this._processQueueTasks.bind(this), 1);\n this._stopped = false;\n }\n\n /**\n * Removes any pending tasks from the queue.\n */\n clear() {\n this._queue.kill();\n }\n\n /**\n * Internal task processing implementation which makes things work.\n */\n _processQueueTasks(task, finishedCallback) {\n try {\n task(finishedCallback);\n } catch (error) {\n logger.error(`Task failed: ${error?.stack}`);\n finishedCallback(error);\n }\n }\n\n /**\n * The 'task' function will be given a callback it MUST call with either:\n * 1) No arguments if it was successful or\n * 2) An error argument if there was an error\n * If the task wants to process the success or failure of the task, it\n * should pass the {@code callback} to the push function, e.g.:\n * queue.push(task, (err) => {\n * if (err) {\n * // error handling\n * } else {\n * // success handling\n * }\n * });\n *\n * @param {function} task - The task to be executed. See the description above.\n * @param {function} [callback] - Optional callback to be called after the task has been executed.\n */\n push(task, callback) {\n if (this._stopped) {\n callback && callback(new Error('The queue has been stopped'));\n\n return;\n }\n this._queue.push(task, callback);\n }\n\n /**\n * Shutdowns the queue. All already queued tasks will execute, but no future tasks can be added. If a task is added\n * after the queue has been shutdown then the callback will be called with an error.\n */\n shutdown() {\n this._stopped = true;\n }\n}\n","/**\n * The pending Jingle session state which means the session as defined in\n * XEP-0166(before 'session-invite/session-accept' took place).\n *\n * @type {string}\n */\nexport const PENDING = 'pending';\n\n/**\n * The active Jingle session state as defined in XEP-0166\n * (after 'session-invite'/'session-accept').\n *\n * @type {string}\n */\nexport const ACTIVE = 'active';\n\n/**\n * The ended Jingle session state as defined in XEP-0166\n * (after 'session-terminate').\n * @type {string}\n */\nexport const ENDED = 'ended';\n","import { getLogger } from '@jitsi/logger';\n\nimport Listenable from '../util/Listenable';\n\nimport * as JingleSessionState from './JingleSessionState';\n\nconst logger = getLogger(__filename);\n\n/**\n * JingleSession provides an API to manage a single Jingle session. We will\n * have different implementations depending on the underlying interface used\n * (i.e. WebRTC and ORTC) and here we hold the code common to all of them.\n */\nexport default class JingleSession extends Listenable {\n\n /* eslint-disable max-params */\n\n /**\n * Creates new JingleSession.\n * @param {string} sid the Jingle session identifier\n * @param {string} localJid our JID\n * @param {string} remoteJid the JID of the remote peer\n * @param {XmppConnection} connection the XMPP connection\n * @param {Object} mediaConstraints the media constraints object passed to the PeerConnection onCreateAnswer/Offer.\n * @param {Object} pcConfig The {@code RTCConfiguration} object passed to the PeerConnection's constructor.\n * @param {boolean} isInitiator indicates if it will be the side which initiates the session.\n */\n constructor(\n sid,\n localJid,\n remoteJid,\n connection,\n mediaConstraints,\n pcConfig,\n isInitiator) {\n super();\n this.sid = sid;\n this.localJid = localJid;\n this.remoteJid = remoteJid;\n this.connection = connection;\n this.mediaConstraints = mediaConstraints;\n this.pcConfig = pcConfig;\n\n /**\n * Indicates whether this instance is an initiator or an answerer of\n * the Jingle session.\n * @type {boolean}\n */\n this.isInitiator = isInitiator;\n\n /**\n * Whether to use dripping or not. Dripping is sending trickle\n * candidates not one-by-one.\n */\n this.usedrip = true;\n\n /**\n * When dripping is used, stores ICE candidates which are to be sent.\n */\n this.dripContainer = [];\n\n /**\n * The chat room instance associated with the session.\n * @type {ChatRoom}\n */\n this.room = null;\n\n /**\n * The signaling layer.\n * @type {SignalingLayerImpl | null}\n * @private\n */\n this._signalingLayer = null;\n\n /**\n * Jingle session state - uninitialized until {@link initialize} is\n * called @type {JingleSessionState}\n */\n this.state = null;\n\n /**\n * The RTC service instance\n * @type {RTC}\n */\n this.rtc = null;\n }\n\n /**\n * Returns XMPP address of this session's initiator.\n * @return {string}\n */\n get initiatorJid() {\n return this.isInitiator ? this.localJid : this.remoteJid;\n }\n\n /**\n * Returns XMPP address of this session's responder.\n * @return {string}\n */\n get responderJid() {\n return this.isInitiator ? this.remoteJid : this.localJid;\n }\n\n /* eslint-enable max-params */\n\n /**\n * Prepares this object to initiate a session.\n * @param {ChatRoom} room the chat room for the conference associated with\n * this session\n * @param {RTC} rtc the RTC service instance\n * @param {SignalingLayerImpl} signalingLayer - The signaling layer instance.\n * @param {object} options - the options, see implementing class's\n * {@link #doInitialize} description for more details.\n */\n initialize(room, rtc, signalingLayer, options) {\n if (this.state !== null) {\n const errmsg\n = `attempt to initiate on session ${this.sid}\n in state ${this.state}`;\n\n logger.error(errmsg);\n throw new Error(errmsg);\n }\n\n // TODO decouple from room\n this.room = room;\n this.rtc = rtc;\n this._signalingLayer = signalingLayer;\n this.state = JingleSessionState.PENDING;\n this.doInitialize(options);\n }\n\n /**\n * The implementing class finishes initialization here. Called at the end of\n * {@link initialize}.\n * @param {Object} options - The options specific to the implementing class.\n * @protected\n */\n doInitialize(options) { } // eslint-disable-line no-unused-vars, no-empty-function, max-len\n\n /* eslint-disable no-unused-vars, no-empty-function */\n\n /**\n * Adds the ICE candidates found in the 'contents' array as remote\n * candidates?\n * Note: currently only used on transport-info\n *\n * @param contents\n */\n addIceCandidates(contents) {}\n\n /* eslint-enable no-unused-vars, no-empty-function */\n\n /**\n * Returns current state of this JingleSession instance.\n * @returns {JingleSessionState} the current state of this session instance.\n */\n getState() {\n return this.state;\n }\n\n /* eslint-disable no-unused-vars, no-empty-function */\n\n /**\n * Handles an 'add-source' event.\n *\n * @param contents an array of Jingle 'content' elements.\n */\n addSources(contents) {}\n\n /**\n * Handles a 'remove-source' event.\n *\n * @param contents an array of Jingle 'content' elements.\n */\n removeSources(contents) {}\n\n /**\n * Terminates this Jingle session by sending session-terminate\n * @param success a callback called once the 'session-terminate' packet has\n * been acknowledged with RESULT.\n * @param failure a callback called when either timeout occurs or ERROR\n * response is received.\n * @param {Object} options\n * @param {string} [options.reason] XMPP Jingle error condition\n * @param {string} [options.reasonDescription] some meaningful error message\n * @param {boolean} [options.requestRestart=false] set to true to ask Jicofo to start a new session one this once is\n * terminated.\n * @param {boolean} [options.sendSessionTerminate=true] set to false to skip\n * sending session-terminate. It may not make sense to send it if the XMPP\n * connection has been closed already or if the remote peer has disconnected\n */\n terminate(success, failure, options) {}\n\n /**\n * Handles an offer from the remote peer (prepares to accept a session).\n * @param jingle the 'jingle' XML element.\n * @param success callback called when we the incoming session has been\n * accepted\n * @param failure callback called when we fail for any reason, will supply\n * error object with details(which is meant more to be printed to the logger\n * than analysed in the code, as the error is unrecoverable anyway)\n */\n acceptOffer(jingle, success, failure) {}\n\n /**\n * Returns the JID of the initiator of the jingle session.\n */\n _getInitiatorJid() {\n return this.isInitiator ? this.localJid : this.remoteJid;\n }\n\n /* eslint-enable no-unused-vars, no-empty-function */\n}\n","enum MediaSessionEvents {\n /**\n * Event triggered when the remote party signals it's receive video max frame height.\n */\n REMOTE_VIDEO_CONSTRAINTS_CHANGED = 'media_session.REMOTE_VIDEO_CONSTRAINTS_CHANGED'\n};\n\nexport default MediaSessionEvents;","/* global $ */\n\nimport { getLogger } from '@jitsi/logger';\nimport { $iq, Strophe } from 'strophe.js';\n\nimport * as CodecMimeType from '../../service/RTC/CodecMimeType';\nimport MediaDirection from '../../service/RTC/MediaDirection';\nimport * as MediaType from '../../service/RTC/MediaType';\nimport {\n ICE_DURATION,\n ICE_STATE_CHANGED\n} from '../../service/statistics/AnalyticsEvents';\nimport XMPPEvents from '../../service/xmpp/XMPPEvents';\nimport { SS_DEFAULT_FRAME_RATE } from '../RTC/ScreenObtainer';\nimport FeatureFlags from '../flags/FeatureFlags';\nimport SDP from '../sdp/SDP';\nimport SDPDiffer from '../sdp/SDPDiffer';\nimport SDPUtil from '../sdp/SDPUtil';\nimport Statistics from '../statistics/statistics';\nimport AsyncQueue from '../util/AsyncQueue';\nimport GlobalOnErrorHandler from '../util/GlobalOnErrorHandler';\nimport { integerHash } from '../util/StringUtils';\n\nimport browser from './../browser';\nimport JingleSession from './JingleSession';\nimport * as JingleSessionState from './JingleSessionState';\nimport MediaSessionEvents from './MediaSessionEvents';\nimport XmppConnection from './XmppConnection';\n\nconst logger = getLogger(__filename);\n\n/**\n * Constant tells how long we're going to wait for IQ response, before timeout\n * error is triggered.\n * @type {number}\n */\nconst IQ_TIMEOUT = 10000;\n\n/*\n * The default number of samples (per stat) to keep when webrtc stats gathering\n * is enabled in TraceablePeerConnection.\n */\nconst DEFAULT_MAX_STATS = 300;\n\n/**\n * The time duration for which the client keeps gathering ICE candidates to be sent out in a single IQ.\n * @type {number} timeout in ms.\n */\nconst ICE_CAND_GATHERING_TIMEOUT = 150;\n\n/**\n * Reads the endpoint ID given a string which represents either the endpoint's full JID, or the endpoint ID itself.\n * @param {String} jidOrEndpointId A string which is either the full JID of a participant, or the ID of an\n * endpoint/participant.\n * @returns The endpoint ID associated with 'jidOrEndpointId'.\n */\nfunction getEndpointId(jidOrEndpointId) {\n return Strophe.getResourceFromJid(jidOrEndpointId) || jidOrEndpointId;\n}\n\n/**\n * @typedef {Object} JingleSessionPCOptions\n * @property {Object} abTesting - A/B testing related options (ask George).\n * @property {boolean} abTesting.enableSuspendVideoTest - enables the suspend\n * video test ?(ask George).\n * @property {boolean} disableH264 - Described in the config.js[1].\n * @property {boolean} disableRtx - Described in the config.js[1].\n * @property {boolean} disableSimulcast - Described in the config.js[1].\n * @property {boolean} enableInsertableStreams - Set to true when the insertable streams constraints is to be enabled\n * on the PeerConnection.\n * @property {boolean} enableLayerSuspension - Described in the config.js[1].\n * @property {boolean} failICE - it's an option used in the tests. Set to\n * true to block any real candidates and make the ICE fail.\n * @property {boolean} gatherStats - Described in the config.js[1].\n * @property {object} p2p - Peer to peer related options (FIXME those could be\n * fetched from config.p2p on the upper level).\n * @property {boolean} preferH264 - Described in the config.js[1].\n * @property {Object} testing - Testing and/or experimental options.\n * @property {boolean} webrtcIceUdpDisable - Described in the config.js[1].\n * @property {boolean} webrtcIceTcpDisable - Described in the config.js[1].\n *\n * [1]: https://github.com/jitsi/jitsi-meet/blob/master/config.js\n */\n/**\n *\n */\nexport default class JingleSessionPC extends JingleSession {\n /**\n * Parses 'senders' attribute of the video content.\n * @param {jQuery} jingleContents\n * @return {string|null} one of the values of content \"senders\" attribute\n * defined by Jingle. If there is no \"senders\" attribute or if the value is\n * invalid then null will be returned.\n * @private\n */\n static parseVideoSenders(jingleContents) {\n const videoContents = jingleContents.find('>content[name=\"video\"]');\n\n if (videoContents.length) {\n const senders = videoContents[0].getAttribute('senders');\n\n if (senders === 'both'\n || senders === 'initiator'\n || senders === 'responder'\n || senders === 'none') {\n return senders;\n }\n }\n\n return null;\n }\n\n /**\n * Parses the video max frame height value out of the 'content-modify' IQ.\n *\n * @param {jQuery} jingleContents - A jQuery selector pointing to the '>jingle' element.\n * @returns {Number|null}\n */\n static parseMaxFrameHeight(jingleContents) {\n const maxFrameHeightSel = jingleContents.find('>content[name=\"video\"]>max-frame-height');\n\n return maxFrameHeightSel.length ? Number(maxFrameHeightSel.text()) : null;\n }\n\n /* eslint-disable max-params */\n\n /**\n * Creates new JingleSessionPC\n * @param {string} sid the Jingle Session ID - random string which identifies the session\n * @param {string} localJid our JID\n * @param {string} remoteJid remote peer JID\n * @param {XmppConnection} connection - The XMPP connection instance.\n * @param mediaConstraints the media constraints object passed to createOffer/Answer, as defined\n * by the WebRTC standard\n * @param pcConfig The {@code RTCConfiguration} to use for the WebRTC peer connection.\n * @param {boolean} isP2P indicates whether this instance is meant to be used in a direct, peer to\n * peer connection or false if it's a JVB connection.\n * @param {boolean} isInitiator indicates if it will be the side which initiates the session.\n * @constructor\n *\n * @implements {SignalingLayer}\n */\n constructor(\n sid,\n localJid,\n remoteJid,\n connection,\n mediaConstraints,\n pcConfig,\n isP2P,\n isInitiator) {\n super(\n sid,\n localJid,\n remoteJid, connection, mediaConstraints, pcConfig, isInitiator);\n\n /**\n * The bridge session's identifier. One Jingle session can during\n * it's lifetime participate in multiple bridge sessions managed by\n * Jicofo. A new bridge session is started whenever Jicofo sends\n * 'session-initiate' or 'transport-replace'.\n *\n * @type {?string}\n * @private\n */\n this._bridgeSessionId = null;\n\n /**\n * The oldest SDP passed to {@link notifyMySSRCUpdate} while the XMPP connection was offline that will be\n * used to update Jicofo once the XMPP connection goes back online.\n * @type {SDP|undefined}\n * @private\n */\n this._cachedOldLocalSdp = undefined;\n\n /**\n * The latest SDP passed to {@link notifyMySSRCUpdate} while the XMPP connection was offline that will be\n * used to update Jicofo once the XMPP connection goes back online.\n * @type {SDP|undefined}\n * @private\n */\n this._cachedNewLocalSdp = undefined;\n\n /**\n * Stores result of {@link window.performance.now()} at the time when\n * ICE enters 'checking' state.\n * @type {number|null} null if no value has been stored yet\n * @private\n */\n this._iceCheckingStartedTimestamp = null;\n\n /**\n * Stores result of {@link window.performance.now()} at the time when\n * first ICE candidate is spawned by the peerconnection to mark when\n * ICE gathering started. That's, because ICE gathering state changed\n * events are not supported by most of the browsers, so we try something\n * that will work everywhere. It may not be as accurate, but given that\n * 'host' candidate usually comes first, the delay should be minimal.\n * @type {number|null} null if no value has been stored yet\n * @private\n */\n this._gatheringStartedTimestamp = null;\n\n /**\n * Local preference for the receive video max frame height.\n *\n * @type {Number|undefined}\n */\n this.localRecvMaxFrameHeight = undefined;\n\n /**\n * Indicates whether or not this session is willing to send/receive\n * video media. When set to false the underlying peer\n * connection will disable local video transfer and the remote peer will\n * be will be asked to stop sending video via 'content-modify' IQ\n * (the senders attribute of video contents will be adjusted\n * accordingly). Note that this notification is sent only in P2P\n * session, because Jicofo does not support it yet. Obviously when\n * the value is changed from false to true another\n * notification will be sent to resume video transfer on the remote\n * side.\n * @type {boolean}\n * @private\n */\n this._localVideoActive = true;\n\n /**\n * Indicates whether or not the remote peer has video transfer active.\n * When set to true it means that remote peer is neither\n * sending nor willing to receive video. In such case we'll ask\n * our peerconnection to stop sending video by calling\n * {@link TraceablePeerConnection.setVideoTransferActive} with\n * false.\n * @type {boolean}\n * @private\n */\n this._remoteVideoActive = true;\n\n /**\n * Marks that ICE gathering duration has been reported already. That\n * prevents reporting it again, after eventual 'transport-replace' (JVB\n * conference migration/ICE restart).\n * @type {boolean}\n * @private\n */\n this._gatheringReported = false;\n\n this.lasticecandidate = false;\n this.closed = false;\n\n /**\n * Indicates whether or not this JingleSessionPC is used in\n * a peer to peer type of session.\n * @type {boolean} true if it's a peer to peer\n * session or false if it's a JVB session\n */\n this.isP2P = isP2P;\n\n /**\n * Remote preference for the receive video max frame height.\n *\n * @type {Number|undefined}\n */\n this.remoteRecvMaxFrameHeight = undefined;\n\n /**\n * The queue used to serialize operations done on the peerconnection.\n *\n * @type {AsyncQueue}\n */\n this.modificationQueue = new AsyncQueue();\n\n /**\n * Flag used to guarantee that the connection established event is\n * triggered just once.\n * @type {boolean}\n */\n this.wasConnected = false;\n\n /**\n * Keeps track of how long (in ms) it took from ICE start to ICE\n * connect.\n *\n * @type {number}\n */\n this.establishmentDuration = undefined;\n\n this._xmppListeners = [];\n this._xmppListeners.push(\n connection.addEventListener(\n XmppConnection.Events.CONN_STATUS_CHANGED,\n this.onXmppStatusChanged.bind(this))\n );\n\n this._removeSenderVideoConstraintsChangeListener = undefined;\n }\n\n /* eslint-enable max-params */\n\n /**\n * Checks whether or not this session instance is still operational.\n * @private\n * @returns {boolean} {@code true} if operation or {@code false} otherwise.\n */\n _assertNotEnded() {\n return this.state !== JingleSessionState.ENDED;\n }\n\n /**\n * @inheritDoc\n * @param {JingleSessionPCOptions} options - a set of config options.\n */\n doInitialize(options) {\n this.failICE = Boolean(options.failICE);\n this.lasticecandidate = false;\n this.options = options;\n\n /**\n * {@code true} if reconnect is in progress.\n * @type {boolean}\n */\n this.isReconnect = false;\n\n /**\n * Set to {@code true} if the connection was ever stable\n * @type {boolean}\n */\n this.wasstable = false;\n this.webrtcIceUdpDisable = Boolean(options.webrtcIceUdpDisable);\n this.webrtcIceTcpDisable = Boolean(options.webrtcIceTcpDisable);\n\n const pcOptions = { disableRtx: options.disableRtx };\n\n if (options.gatherStats) {\n pcOptions.maxstats = DEFAULT_MAX_STATS;\n }\n pcOptions.capScreenshareBitrate = false;\n pcOptions.enableInsertableStreams = options.enableInsertableStreams;\n pcOptions.videoQuality = options.videoQuality;\n pcOptions.forceTurnRelay = options.forceTurnRelay;\n pcOptions.audioQuality = options.audioQuality;\n pcOptions.usesUnifiedPlan = this.usesUnifiedPlan\n = browser.supportsUnifiedPlan()\n && (browser.isFirefox()\n || browser.isWebKitBased()\n || (browser.isChromiumBased()\n\n // Provide a way to control the behavior for jvb and p2p connections independently.\n && this.isP2P\n ? options.p2p?.enableUnifiedOnChrome ?? true\n : options.enableUnifiedOnChrome ?? true));\n\n if (this.isP2P) {\n // simulcast needs to be disabled for P2P (121) calls\n pcOptions.disableSimulcast = true;\n const abtestSuspendVideo = this._abtestSuspendVideoEnabled(options);\n\n if (typeof abtestSuspendVideo !== 'undefined') {\n pcOptions.abtestSuspendVideo = abtestSuspendVideo;\n }\n } else {\n // H264 does not support simulcast, so it needs to be disabled.\n pcOptions.disableSimulcast\n = options.disableSimulcast\n || (options.preferH264 && !options.disableH264)\n || (options.videoQuality && options.videoQuality.preferredCodec === CodecMimeType.H264);\n\n // Disable simulcast for low fps screenshare and enable it for high fps screenshare.\n // testing.capScreenshareBitrate config.js setting has now been deprecated.\n pcOptions.capScreenshareBitrate = pcOptions.disableSimulcast\n || !(typeof options.desktopSharingFrameRate?.max === 'number'\n && options.desktopSharingFrameRate?.max > SS_DEFAULT_FRAME_RATE);\n\n // add the capScreenshareBitrate to the permanent properties so that it's included with every event that we\n // send to the analytics backend.\n Statistics.analytics.addPermanentProperties({ capScreenshareBitrate: pcOptions.capScreenshareBitrate });\n }\n\n if (options.startSilent) {\n pcOptions.startSilent = true;\n }\n\n this.peerconnection\n = this.rtc.createPeerConnection(\n this._signalingLayer,\n this.pcConfig,\n this.isP2P,\n pcOptions);\n\n this.peerconnection.onicecandidate = ev => {\n if (!ev) {\n // There was an incomplete check for ev before which left\n // the last line of the function unprotected from a potential\n // throw of an exception. Consequently, it may be argued that\n // the check is unnecessary. Anyway, I'm leaving it and making\n // the check complete.\n return;\n }\n\n // XXX this is broken, candidate is not parsed.\n const candidate = ev.candidate;\n const now = window.performance.now();\n\n if (candidate) {\n if (this._gatheringStartedTimestamp === null) {\n this._gatheringStartedTimestamp = now;\n }\n\n // Discard candidates of disabled protocols.\n let protocol = candidate.protocol;\n\n if (typeof protocol === 'string') {\n protocol = protocol.toLowerCase();\n if (protocol === 'tcp' || protocol === 'ssltcp') {\n if (this.webrtcIceTcpDisable) {\n return;\n }\n } else if (protocol === 'udp') {\n if (this.webrtcIceUdpDisable) {\n return;\n }\n }\n }\n } else if (!this._gatheringReported) {\n // End of gathering\n Statistics.sendAnalytics(\n ICE_DURATION,\n {\n phase: 'gathering',\n value: now - this._gatheringStartedTimestamp,\n p2p: this.isP2P,\n initiator: this.isInitiator\n });\n this._gatheringReported = true;\n }\n this.sendIceCandidate(candidate);\n };\n\n // Note there is a change in the spec about closed:\n // This value moved into the RTCPeerConnectionState enum in\n // the May 13, 2016 draft of the specification, as it reflects the state\n // of the RTCPeerConnection, not the signaling connection. You now\n // detect a closed connection by checking for connectionState to be\n // \"closed\" instead.\n // I suppose at some point this will be moved to onconnectionstatechange\n this.peerconnection.onsignalingstatechange = () => {\n if (this.peerconnection.signalingState === 'stable') {\n this.wasstable = true;\n } else if (this.peerconnection.signalingState === 'closed'\n || this.peerconnection.connectionState === 'closed') {\n this.room.eventEmitter.emit(XMPPEvents.SUSPEND_DETECTED, this);\n }\n };\n\n /**\n * The oniceconnectionstatechange event handler contains the code to\n * execute when the iceconnectionstatechange event, of type Event,\n * is received by this RTCPeerConnection. Such an event is sent when\n * the value of RTCPeerConnection.iceConnectionState changes.\n */\n this.peerconnection.oniceconnectionstatechange = () => {\n const now = window.performance.now();\n let isStable = false;\n\n if (!this.isP2P) {\n this.room.connectionTimes[\n `ice.state.${this.peerconnection.iceConnectionState}`]\n = now;\n }\n logger.log(`(TIME) ICE ${this.peerconnection.iceConnectionState} ${this.isP2P ? 'P2P' : 'JVB'}:\\t`, now);\n\n Statistics.sendAnalytics(\n ICE_STATE_CHANGED,\n {\n p2p: this.isP2P,\n state: this.peerconnection.iceConnectionState,\n 'signaling_state': this.peerconnection.signalingState,\n reconnect: this.isReconnect,\n value: now\n });\n\n this.room.eventEmitter.emit(\n XMPPEvents.ICE_CONNECTION_STATE_CHANGED,\n this,\n this.peerconnection.iceConnectionState);\n switch (this.peerconnection.iceConnectionState) {\n case 'checking':\n this._iceCheckingStartedTimestamp = now;\n break;\n case 'connected':\n // Informs interested parties that the connection has been restored. This includes the case when\n // media connection to the bridge has been restored after an ICE failure by using session-terminate.\n if (this.peerconnection.signalingState === 'stable') {\n isStable = true;\n const usesTerminateForRestart = !this.options.enableIceRestart\n && this.room.supportsRestartByTerminate();\n\n if (this.isReconnect || usesTerminateForRestart) {\n this.room.eventEmitter.emit(\n XMPPEvents.CONNECTION_RESTORED, this);\n }\n }\n\n // Add a workaround for an issue on chrome in Unified plan when the local endpoint is the offerer.\n // The 'signalingstatechange' event for 'stable' is handled after the 'iceconnectionstatechange' event\n // for 'completed' is handled by the client. This prevents the client from firing a\n // CONNECTION_ESTABLISHED event for the p2p session. As a result, the offerer continues to stay on the\n // jvb connection while the remote peer switches to the p2p connection breaking the media flow between\n // the endpoints.\n // TODO - file a chromium bug and add the information here.\n if (!this.wasConnected\n && (this.wasstable\n || isStable\n || (this.usesUnifiedPlan && this.isInitiator && browser.isChromiumBased()))) {\n\n Statistics.sendAnalytics(\n ICE_DURATION,\n {\n phase: 'checking',\n value: now - this._iceCheckingStartedTimestamp,\n p2p: this.isP2P,\n initiator: this.isInitiator\n });\n\n // Switch between ICE gathering and ICE checking whichever\n // started first (scenarios are different for initiator\n // vs responder)\n const iceStarted\n = Math.min(\n this._iceCheckingStartedTimestamp,\n this._gatheringStartedTimestamp);\n\n this.establishmentDuration = now - iceStarted;\n\n Statistics.sendAnalytics(\n ICE_DURATION,\n {\n phase: 'establishment',\n value: this.establishmentDuration,\n p2p: this.isP2P,\n initiator: this.isInitiator\n });\n\n this.wasConnected = true;\n this.room.eventEmitter.emit(\n XMPPEvents.CONNECTION_ESTABLISHED, this);\n }\n this.isReconnect = false;\n break;\n case 'disconnected':\n this.isReconnect = true;\n\n // Informs interested parties that the connection has been\n // interrupted.\n if (this.wasstable) {\n this.room.eventEmitter.emit(\n XMPPEvents.CONNECTION_INTERRUPTED, this);\n }\n break;\n case 'failed':\n this.room.eventEmitter.emit(\n XMPPEvents.CONNECTION_ICE_FAILED, this);\n break;\n }\n };\n\n\n /**\n * The connection state event is fired whenever the aggregate of underlying\n * transports change their state.\n */\n this.peerconnection.onconnectionstatechange = () => {\n const icestate = this.peerconnection.iceConnectionState;\n\n switch (this.peerconnection.connectionState) {\n case 'failed':\n // Since version 76 Chrome no longer switches ICE connection\n // state to failed (see\n // https://bugs.chromium.org/p/chromium/issues/detail?id=982793\n // for details) we use this workaround to recover from lost connections\n if (icestate === 'disconnected') {\n this.room.eventEmitter.emit(\n XMPPEvents.CONNECTION_ICE_FAILED, this);\n }\n break;\n }\n };\n\n /**\n * The negotiationneeded event is fired whenever we shake the media on the\n * RTCPeerConnection object.\n */\n this.peerconnection.onnegotiationneeded = () => {\n const state = this.peerconnection.signalingState;\n const remoteDescription = this.peerconnection.remoteDescription;\n\n if (this.usesUnifiedPlan\n && !this.isP2P\n && state === 'stable'\n && remoteDescription\n && typeof remoteDescription.sdp === 'string') {\n logger.info(`${this} onnegotiationneeded fired on ${this.peerconnection}`);\n\n const workFunction = finishedCallback => {\n const oldSdp = new SDP(this.peerconnection.localDescription.sdp);\n\n this._renegotiate()\n .then(() => this.peerconnection.configureSenderVideoEncodings())\n .then(() => {\n const newSdp = new SDP(this.peerconnection.localDescription.sdp);\n\n this.notifyMySSRCUpdate(oldSdp, newSdp);\n })\n .then(() => finishedCallback(), error => finishedCallback(error));\n };\n\n this.modificationQueue.push(\n workFunction,\n error => {\n if (error) {\n logger.error(`${this} onnegotiationneeded error`, error);\n } else {\n logger.debug(`${this} onnegotiationneeded executed - OK`);\n }\n });\n }\n };\n }\n\n /**\n * Remote preference for receive video max frame height.\n *\n * @returns {Number|undefined}\n */\n getRemoteRecvMaxFrameHeight() {\n if (this.isP2P) {\n return this.remoteRecvMaxFrameHeight;\n }\n\n return undefined;\n }\n\n /**\n * Sends given candidate in Jingle 'transport-info' message.\n * @param {RTCIceCandidate} candidate the WebRTC ICE candidate instance\n * @private\n */\n sendIceCandidate(candidate) {\n const localSDP = new SDP(this.peerconnection.localDescription.sdp);\n\n if (candidate && candidate.candidate.length && !this.lasticecandidate) {\n const ice = SDPUtil.iceparams(localSDP.media[candidate.sdpMLineIndex], localSDP.session);\n const jcand = SDPUtil.candidateToJingle(candidate.candidate);\n\n if (!(ice && jcand)) {\n const errorMesssage = 'failed to get ice && jcand';\n\n GlobalOnErrorHandler.callErrorHandler(new Error(errorMesssage));\n logger.error(errorMesssage);\n\n return;\n }\n ice.xmlns = 'urn:xmpp:jingle:transports:ice-udp:1';\n\n if (this.usedrip) {\n if (this.dripContainer.length === 0) {\n setTimeout(() => {\n if (this.dripContainer.length === 0) {\n return;\n }\n this.sendIceCandidates(this.dripContainer);\n this.dripContainer = [];\n }, ICE_CAND_GATHERING_TIMEOUT);\n }\n this.dripContainer.push(candidate);\n } else {\n this.sendIceCandidates([ candidate ]);\n }\n } else {\n logger.log(`${this} sendIceCandidate: last candidate`);\n\n // FIXME: remember to re-think in ICE-restart\n this.lasticecandidate = true;\n }\n }\n\n /**\n * Sends given candidates in Jingle 'transport-info' message.\n * @param {Array} candidates an array of the WebRTC ICE\n * candidate instances\n * @private\n */\n sendIceCandidates(candidates) {\n if (!this._assertNotEnded('sendIceCandidates')) {\n\n return;\n }\n\n logger.log(`${this} sendIceCandidates ${JSON.stringify(candidates)}`);\n const cand = $iq({ to: this.remoteJid,\n type: 'set' })\n .c('jingle', { xmlns: 'urn:xmpp:jingle:1',\n action: 'transport-info',\n initiator: this.initiatorJid,\n sid: this.sid });\n\n const localSDP = new SDP(this.peerconnection.localDescription.sdp);\n\n for (let mid = 0; mid < localSDP.media.length; mid++) {\n const cands = candidates.filter(el => el.sdpMLineIndex === mid);\n const mline\n = SDPUtil.parseMLine(localSDP.media[mid].split('\\r\\n')[0]);\n\n if (cands.length > 0) {\n const ice\n = SDPUtil.iceparams(localSDP.media[mid], localSDP.session);\n\n ice.xmlns = 'urn:xmpp:jingle:transports:ice-udp:1';\n cand.c('content', {\n creator: this.initiatorJid === this.localJid\n ? 'initiator' : 'responder',\n name: cands[0].sdpMid ? cands[0].sdpMid : mline.media\n }).c('transport', ice);\n for (let i = 0; i < cands.length; i++) {\n const candidate\n = SDPUtil.candidateToJingle(cands[i].candidate);\n\n // Mangle ICE candidate if 'failICE' test option is enabled\n\n if (this.failICE) {\n candidate.ip = '1.1.1.1';\n }\n cand.c('candidate', candidate).up();\n }\n\n // add fingerprint\n const fingerprintLine\n = SDPUtil.findLine(\n localSDP.media[mid],\n 'a=fingerprint:', localSDP.session);\n\n if (fingerprintLine) {\n const tmp = SDPUtil.parseFingerprint(fingerprintLine);\n\n tmp.required = true;\n cand.c(\n 'fingerprint',\n { xmlns: 'urn:xmpp:jingle:apps:dtls:0' })\n .t(tmp.fingerprint);\n delete tmp.fingerprint;\n cand.attrs(tmp);\n cand.up();\n }\n cand.up(); // transport\n cand.up(); // content\n }\n }\n\n // might merge last-candidate notification into this, but it is called\n // a lot later. See webrtc issue #2340\n // logger.log('was this the last candidate', this.lasticecandidate);\n this.connection.sendIQ(\n cand, null, this.newJingleErrorHandler(cand), IQ_TIMEOUT);\n }\n\n /**\n * Sends Jingle 'session-info' message which includes custom Jitsi Meet\n * 'ice-state' element with the text value 'failed' to let Jicofo know\n * that the ICE connection has entered the failed state. It can then\n * choose to re-create JVB channels and send 'transport-replace' to\n * retry the connection.\n */\n sendIceFailedNotification() {\n const sessionInfo\n = $iq({\n to: this.remoteJid,\n type: 'set' })\n .c('jingle', { xmlns: 'urn:xmpp:jingle:1',\n action: 'session-info',\n initiator: this.initiatorJid,\n sid: this.sid })\n .c('ice-state', { xmlns: 'http://jitsi.org/protocol/focus' })\n .t('failed')\n .up();\n\n this._bridgeSessionId\n && sessionInfo.c(\n 'bridge-session', {\n xmlns: 'http://jitsi.org/protocol/focus',\n id: this._bridgeSessionId\n });\n\n this.connection.sendIQ2(\n sessionInfo, {\n /*\n * This message will be often sent when there are connectivity\n * issues, so make it slightly longer than Prosody's default BOSH\n * inactivity timeout of 60 seconds.\n */\n timeout: 65\n })\n .catch(this.newJingleErrorHandler(sessionInfo));\n }\n\n /**\n * {@inheritDoc}\n */\n addIceCandidates(elem) {\n if (this.peerconnection.signalingState === 'closed') {\n logger.warn(`${this} Ignored add ICE candidate when in closed state`);\n\n return;\n }\n\n const iceCandidates = [];\n\n elem.find('>content>transport>candidate')\n .each((idx, candidate) => {\n let line = SDPUtil.candidateFromJingle(candidate);\n\n line = line.replace('\\r\\n', '').replace('a=', '');\n\n // FIXME this code does not care to handle\n // non-bundle transport\n const rtcCandidate = new RTCIceCandidate({\n sdpMLineIndex: 0,\n\n // FF comes up with more complex names like audio-23423,\n // Given that it works on both Chrome and FF without\n // providing it, let's leave it like this for the time\n // being...\n // sdpMid: 'audio',\n sdpMid: '',\n candidate: line\n });\n\n iceCandidates.push(rtcCandidate);\n });\n\n if (!iceCandidates.length) {\n logger.error(`${this} No ICE candidates to add ?`, elem[0] && elem[0].outerHTML);\n\n return;\n }\n\n // We want to have this task queued, so that we know it is executed,\n // after the initial sRD/sLD offer/answer cycle was done (based on\n // the assumption that candidates are spawned after the offer/answer\n // and XMPP preserves order).\n const workFunction = finishedCallback => {\n for (const iceCandidate of iceCandidates) {\n this.peerconnection.addIceCandidate(iceCandidate)\n .then(\n () => logger.debug(`${this} addIceCandidate ok!`),\n err => logger.error(`${this} addIceCandidate failed!`, err));\n }\n\n finishedCallback();\n logger.debug(`${this} ICE candidates task finished`);\n };\n\n logger.debug(`${this} Queued add (${iceCandidates.length}) ICE candidates task`);\n this.modificationQueue.push(workFunction);\n }\n\n /**\n *\n * @param contents\n */\n readSsrcInfo(contents) {\n const ssrcs\n = $(contents).find(\n '>description>'\n + 'source[xmlns=\"urn:xmpp:jingle:apps:rtp:ssma:0\"]');\n\n ssrcs.each((i, ssrcElement) => {\n const ssrc = Number(ssrcElement.getAttribute('ssrc'));\n\n if (this.isP2P) {\n // In P2P all SSRCs are owner by the remote peer\n this._signalingLayer.setSSRCOwner(\n ssrc, Strophe.getResourceFromJid(this.remoteJid));\n } else {\n if (FeatureFlags.isSourceNameSignalingEnabled()) {\n // Only set sourceName for non-P2P case\n if (ssrcElement.hasAttribute('name')) {\n const sourceName = ssrcElement.getAttribute('name');\n\n this._signalingLayer.setTrackSourceName(ssrc, sourceName);\n }\n }\n $(ssrcElement)\n .find('>ssrc-info[xmlns=\"http://jitsi.org/jitmeet\"]')\n .each((i3, ssrcInfoElement) => {\n const owner = ssrcInfoElement.getAttribute('owner');\n\n if (owner && owner.length) {\n if (isNaN(ssrc) || ssrc < 0) {\n logger.warn(`${this} Invalid SSRC ${ssrc} value received for ${owner}`);\n } else {\n this._signalingLayer.setSSRCOwner(\n ssrc,\n getEndpointId(owner));\n }\n }\n });\n }\n });\n }\n\n /**\n * Makes the underlying TraceablePeerConnection generate new SSRC for\n * the recvonly video stream.\n * @deprecated\n */\n generateRecvonlySsrc() {\n if (this.peerconnection) {\n this.peerconnection.generateRecvonlySsrc();\n } else {\n logger.error(`${this} Unable to generate recvonly SSRC - no peerconnection`);\n }\n }\n\n /**\n * Returns the video codec configured as the preferred codec on the peerconnection.\n */\n getConfiguredVideoCodec() {\n return this.peerconnection.getConfiguredVideoCodec();\n }\n\n /* eslint-disable max-params */\n /**\n * Accepts incoming Jingle 'session-initiate' and should send\n * 'session-accept' in result.\n * @param jingleOffer jQuery selector pointing to the jingle element of\n * the offer IQ\n * @param success callback called when we accept incoming session\n * successfully and receive RESULT packet to 'session-accept' sent.\n * @param failure function(error) called if for any reason we fail to accept\n * the incoming offer. 'error' argument can be used to log some details\n * about the error.\n * @param {Array} [localTracks] the optional list of\n * the local tracks that will be added, before the offer/answer cycle\n * executes. We allow the localTracks to optionally be passed in so that\n * the addition of the local tracks and the processing of the initial offer\n * can all be done atomically. We want to make sure that any other\n * operations which originate in the XMPP Jingle messages related with\n * this session to be executed with an assumption that the initial\n * offer/answer cycle has been executed already.\n */\n acceptOffer(jingleOffer, success, failure, localTracks) {\n this.setOfferAnswerCycle(\n jingleOffer,\n () => {\n // FIXME we may not care about RESULT packet for session-accept\n // then we should either call 'success' here immediately or\n // modify sendSessionAccept method to do that\n this.sendSessionAccept(() => {\n success();\n\n this.room.eventEmitter.emit(XMPPEvents.SESSION_ACCEPT, this);\n },\n error => {\n failure(error);\n\n this.room.eventEmitter.emit(XMPPEvents.SESSION_ACCEPT_ERROR, this, error);\n });\n },\n failure,\n localTracks);\n }\n\n /* eslint-enable max-params */\n\n /**\n * Creates an offer and sends Jingle 'session-initiate' to the remote peer.\n * @param {Array} localTracks the local tracks that will be\n * added, before the offer/answer cycle executes (for the local track\n * addition to be an atomic operation together with the offer/answer).\n */\n invite(localTracks = []) {\n if (!this.isInitiator) {\n throw new Error('Trying to invite from the responder session');\n }\n const workFunction = finishedCallback => {\n const addTracks = [];\n\n for (const localTrack of localTracks) {\n addTracks.push(this.peerconnection.addTrack(localTrack, this.isInitiator));\n }\n\n Promise.all(addTracks)\n .then(() => this.peerconnection.createOffer(this.mediaConstraints))\n .then(offerSdp => this.peerconnection.setLocalDescription(offerSdp))\n .then(() => {\n // NOTE that the offer is obtained from the localDescription getter as it needs to go though\n // the transformation chain.\n this.sendSessionInitiate(this.peerconnection.localDescription.sdp);\n })\n .then(() => finishedCallback(), error => finishedCallback(error));\n };\n\n logger.debug(`${this} Queued invite task`);\n this.modificationQueue.push(\n workFunction,\n error => {\n if (error) {\n logger.error(`${this} invite error`, error);\n } else {\n logger.debug(`${this} invite executed - OK`);\n }\n });\n }\n\n /**\n * Sends 'session-initiate' to the remote peer.\n *\n * NOTE this method is synchronous and we're not waiting for the RESULT\n * response which would delay the startup process.\n *\n * @param {string} offerSdp - The local session description which will be\n * used to generate an offer.\n * @private\n */\n sendSessionInitiate(offerSdp) {\n let init = $iq({\n to: this.remoteJid,\n type: 'set'\n }).c('jingle', {\n xmlns: 'urn:xmpp:jingle:1',\n action: 'session-initiate',\n initiator: this.initiatorJid,\n sid: this.sid\n });\n\n new SDP(offerSdp).toJingle(\n init,\n this.isInitiator ? 'initiator' : 'responder');\n init = init.tree();\n logger.debug(`${this} Session-initiate: `, init);\n this.connection.sendIQ(init,\n () => {\n logger.info(`${this} Got RESULT for \"session-initiate\"`);\n },\n error => {\n logger.error(`${this} \"session-initiate\" error`, error);\n },\n IQ_TIMEOUT);\n }\n\n /**\n * Sets the answer received from the remote peer.\n * @param jingleAnswer\n */\n setAnswer(jingleAnswer) {\n if (!this.isInitiator) {\n throw new Error('Trying to set an answer on the responder session');\n }\n this.setOfferAnswerCycle(\n jingleAnswer,\n () => {\n logger.info(`${this} setAnswer - succeeded`);\n if (this.usesUnifiedPlan && browser.isChromiumBased()) {\n const workFunction = finishedCallback => {\n // This hack is needed for Chrome to create a decoder for the ssrcs in the remote SDP when\n // the local endpoint is the offerer and starts muted.\n const remoteSdp = this.peerconnection.remoteDescription.sdp;\n const remoteDescription = new RTCSessionDescription({\n type: 'offer',\n sdp: remoteSdp\n });\n\n return this._responderRenegotiate(remoteDescription)\n .then(() => finishedCallback(), error => finishedCallback(error));\n };\n\n logger.debug(`${this} Queued responderRenegotiate task`);\n this.modificationQueue.push(\n workFunction,\n error => {\n if (error) {\n logger.error(`${this} failed to renegotiate a decoder for muted endpoint ${error}`);\n } else {\n logger.debug(`${this} renegotiate a decoder for muted endpoint`);\n }\n });\n }\n },\n error => {\n logger.error(`${this} setAnswer failed: `, error);\n });\n }\n\n /* eslint-disable max-params */\n /**\n * This is a setRemoteDescription/setLocalDescription cycle which starts at\n * converting Strophe Jingle IQ into remote offer SDP. Once converted\n * setRemoteDescription, createAnswer and setLocalDescription calls follow.\n * @param jingleOfferAnswerIq jQuery selector pointing to the jingle element\n * of the offer (or answer) IQ\n * @param success callback called when sRD/sLD cycle finishes successfully.\n * @param failure callback called with an error object as an argument if we\n * fail at any point during setRD, createAnswer, setLD.\n * @param {Array} [localTracks] the optional list of\n * the local tracks that will be added, before the offer/answer cycle\n * executes (for the local track addition to be an atomic operation together\n * with the offer/answer).\n */\n setOfferAnswerCycle(jingleOfferAnswerIq, success, failure, localTracks = []) {\n const workFunction = finishedCallback => {\n const addTracks = [];\n\n for (const track of localTracks) {\n addTracks.push(this.peerconnection.addTrack(track, this.isInitiator));\n }\n\n const newRemoteSdp\n = this._processNewJingleOfferIq(jingleOfferAnswerIq);\n const oldLocalSdp\n = this.peerconnection.localDescription.sdp;\n\n const bridgeSession\n = $(jingleOfferAnswerIq)\n .find('>bridge-session['\n + 'xmlns=\"http://jitsi.org/protocol/focus\"]');\n const bridgeSessionId = bridgeSession.attr('id');\n\n if (bridgeSessionId !== this._bridgeSessionId) {\n this._bridgeSessionId = bridgeSessionId;\n }\n\n Promise.all(addTracks)\n .then(() => this._renegotiate(newRemoteSdp.raw))\n .then(() => {\n if (this.state === JingleSessionState.PENDING) {\n this.state = JingleSessionState.ACTIVE;\n\n // #1 Sync up video transfer active/inactive only after\n // the initial O/A cycle. We want to adjust the video\n // media direction only in the local SDP and the Jingle\n // contents direction included in the initial\n // offer/answer is mapped to the remote SDP. Jingle\n // 'content-modify' IQ is processed in a way that it\n // will only modify local SDP when remote peer is no\n // longer interested in receiving video content.\n // Changing media direction in the remote SDP will mess\n // up our SDP translation chain (simulcast, video mute,\n // RTX etc.)\n //\n // #2 Sends the max frame height if it was set, before the session-initiate/accept\n if (this.isP2P\n && (!this._localVideoActive || this.localRecvMaxFrameHeight)) {\n this.sendContentModify();\n }\n }\n\n // Old local SDP will be available when we're setting answer\n // for the first time, but not when offer and it's fine\n // since we're generating an answer now it will contain all\n // our SSRCs\n if (oldLocalSdp) {\n const newLocalSdp\n = new SDP(this.peerconnection.localDescription.sdp);\n\n this.notifyMySSRCUpdate(\n new SDP(oldLocalSdp), newLocalSdp);\n }\n })\n .then(() => finishedCallback(), error => finishedCallback(error));\n };\n\n logger.debug(`${this} Queued setOfferAnswerCycle task`);\n this.modificationQueue.push(\n workFunction,\n error => {\n if (error) {\n logger.error(`${this} setOfferAnswerCycle task failed: ${error}`);\n failure(error);\n } else {\n logger.debug(`${this} setOfferAnswerCycle task done`);\n success();\n }\n });\n }\n\n /**\n * Updates the codecs on the peerconnection and initiates a renegotiation for the\n * new codec config to take effect.\n *\n * @param {CodecMimeType} preferred the preferred codec.\n * @param {CodecMimeType} disabled the codec that needs to be disabled.\n */\n setVideoCodecs(preferred = null, disabled = null) {\n const current = this.peerconnection.getConfiguredVideoCodec();\n\n if (this._assertNotEnded() && preferred !== current) {\n logger.info(`${this} Switching video codec from ${current} to ${preferred}`);\n this.peerconnection.setVideoCodecs(preferred, disabled);\n\n // Initiate a renegotiate for the codec setting to take effect.\n const workFunction = finishedCallback => {\n this._renegotiate().then(\n () => {\n logger.debug(`${this} setVideoCodecs task is done`);\n\n return finishedCallback();\n }, error => {\n logger.error(`${this} setVideoCodecs task failed: ${error}`);\n\n return finishedCallback(error);\n });\n };\n\n logger.debug(`${this} Queued setVideoCodecs task`);\n\n // Queue and execute\n this.modificationQueue.push(workFunction);\n }\n }\n\n /* eslint-enable max-params */\n\n /**\n * Although it states \"replace transport\" it does accept full Jingle offer\n * which should contain new ICE transport details.\n * @param jingleOfferElem an element Jingle IQ that contains new offer and\n * transport info.\n * @param success callback called when we succeed to accept new offer.\n * @param failure function(error) called when we fail to accept new offer.\n */\n replaceTransport(jingleOfferElem, success, failure) {\n if (this.options.enableForcedReload) {\n const sdp = new SDP(this.peerconnection.localDescription.sdp);\n\n this.sendTransportAccept(sdp, success, failure);\n this.room.eventEmitter.emit(XMPPEvents.CONNECTION_RESTARTED, this);\n\n return;\n }\n this.room.eventEmitter.emit(XMPPEvents.ICE_RESTARTING, this);\n\n // We need to first reject the 'data' section to have the SCTP stack\n // cleaned up to signal the known data channel is now invalid. After\n // that the original offer is set to have the SCTP connection\n // established with the new bridge.\n const originalOffer = jingleOfferElem.clone();\n\n jingleOfferElem\n .find('>content[name=\\'data\\']')\n .attr('senders', 'rejected');\n\n // Remove all remote sources in order to reset the client's state\n // for the remote MediaStreams. When a conference is moved to\n // another bridge it will start streaming with a sequence number\n // that is not in sync with the most recently seen by the client.\n // The symptoms include frozen or black video and lots of \"failed to\n // unprotect SRTP packets\" in Chrome logs.\n jingleOfferElem\n .find('>content>description>source')\n .remove();\n jingleOfferElem\n .find('>content>description>ssrc-group')\n .remove();\n\n // On the JVB it's not a real ICE restart and all layers are re-initialized from scratch as Jicofo does\n // the restart by re-allocating new channels. Chrome (or WebRTC stack) needs to have the DTLS transport layer\n // reset to start a new handshake with fresh DTLS transport on the bridge. Make it think that the DTLS\n // fingerprint has changed by setting an all zeros key.\n const newFingerprint = jingleOfferElem.find('>content>transport>fingerprint');\n\n newFingerprint.attr('hash', 'sha-1');\n newFingerprint.text('00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00');\n\n // First set an offer with a rejected 'data' section\n this.setOfferAnswerCycle(\n jingleOfferElem,\n () => {\n // Now set the original offer(with the 'data' section)\n this.setOfferAnswerCycle(\n originalOffer,\n () => {\n const localSDP\n = new SDP(this.peerconnection.localDescription.sdp);\n\n this.sendTransportAccept(localSDP, success, failure);\n\n this.room.eventEmitter.emit(\n XMPPEvents.ICE_RESTART_SUCCESS,\n this,\n originalOffer);\n },\n failure);\n },\n failure\n );\n }\n\n /**\n * Sends Jingle 'session-accept' message.\n * @param {function()} success callback called when we receive 'RESULT'\n * packet for the 'session-accept'\n * @param {function(error)} failure called when we receive an error response\n * or when the request has timed out.\n * @private\n */\n sendSessionAccept(success, failure) {\n // NOTE: since we're just reading from it, we don't need to be within\n // the modification queue to access the local description\n const localSDP = new SDP(this.peerconnection.localDescription.sdp);\n const accept = $iq({ to: this.remoteJid,\n type: 'set' })\n .c('jingle', { xmlns: 'urn:xmpp:jingle:1',\n action: 'session-accept',\n initiator: this.initiatorJid,\n responder: this.responderJid,\n sid: this.sid });\n\n if (this.webrtcIceTcpDisable) {\n localSDP.removeTcpCandidates = true;\n }\n if (this.webrtcIceUdpDisable) {\n localSDP.removeUdpCandidates = true;\n }\n if (this.failICE) {\n localSDP.failICE = true;\n }\n localSDP.toJingle(\n accept,\n this.initiatorJid === this.localJid ? 'initiator' : 'responder');\n\n logger.info(`${this} Sending session-accept`);\n logger.debug(accept.tree());\n this.connection.sendIQ(accept,\n success,\n this.newJingleErrorHandler(accept, error => {\n failure(error);\n\n // 'session-accept' is a critical timeout and we'll\n // have to restart\n this.room.eventEmitter.emit(\n XMPPEvents.SESSION_ACCEPT_TIMEOUT, this);\n }),\n IQ_TIMEOUT);\n\n // XXX Videobridge needs WebRTC's answer (ICE ufrag and pwd, DTLS\n // fingerprint and setup) ASAP in order to start the connection\n // establishment.\n //\n // FIXME Flushing the connection at this point triggers an issue with\n // BOSH request handling in Prosody on slow connections.\n //\n // The problem is that this request will be quite large and it may take\n // time before it reaches Prosody. In the meantime Strophe may decide\n // to send the next one. And it was observed that a small request with\n // 'transport-info' usually follows this one. It does reach Prosody\n // before the previous one was completely received. 'rid' on the server\n // is increased and Prosody ignores the request with 'session-accept'.\n // It will never reach Jicofo and everything in the request table is\n // lost. Removing the flush does not guarantee it will never happen, but\n // makes it much less likely('transport-info' is bundled with\n // 'session-accept' and any immediate requests).\n //\n // this.connection.flush();\n }\n\n /**\n * Will send 'content-modify' IQ in order to ask the remote peer to\n * either stop or resume sending video media or to adjust sender's video constraints.\n * @private\n */\n sendContentModify() {\n const maxFrameHeight = this.localRecvMaxFrameHeight;\n const senders = this._localVideoActive ? 'both' : 'none';\n\n let sessionModify\n = $iq({\n to: this.remoteJid,\n type: 'set'\n })\n .c('jingle', {\n xmlns: 'urn:xmpp:jingle:1',\n action: 'content-modify',\n initiator: this.initiatorJid,\n sid: this.sid\n })\n .c('content', {\n name: MediaType.VIDEO,\n senders\n });\n\n if (typeof maxFrameHeight !== 'undefined') {\n sessionModify = sessionModify\n .c('max-frame-height', { xmlns: 'http://jitsi.org/jitmeet/video' })\n .t(maxFrameHeight);\n }\n\n logger.info(`${this} sending content-modify, video senders: ${senders}, max frame height: ${maxFrameHeight}`);\n logger.debug(sessionModify.tree());\n\n this.connection.sendIQ(\n sessionModify,\n null,\n this.newJingleErrorHandler(sessionModify),\n IQ_TIMEOUT);\n }\n\n /**\n * Adjust the preference for max video frame height that the local party is willing to receive. Signals\n * the remote party.\n *\n * @param {Number} maxFrameHeight - the new value to set.\n */\n setReceiverVideoConstraint(maxFrameHeight) {\n logger.info(`${this} setReceiverVideoConstraint - max frame height: ${maxFrameHeight}`);\n\n this.localRecvMaxFrameHeight = maxFrameHeight;\n\n if (this.isP2P) {\n // Tell the remote peer about our receive constraint. If Jingle session is not yet active the state will\n // be synced after offer/answer.\n if (this.state === JingleSessionState.ACTIVE) {\n this.sendContentModify();\n }\n } else {\n this.rtc.setReceiverVideoConstraint(maxFrameHeight);\n }\n }\n\n /**\n * Sends Jingle 'transport-accept' message which is a response to\n * 'transport-replace'.\n * @param localSDP the 'SDP' object with local session description\n * @param success callback called when we receive 'RESULT' packet for\n * 'transport-replace'\n * @param failure function(error) called when we receive an error response\n * or when the request has timed out.\n * @private\n */\n sendTransportAccept(localSDP, success, failure) {\n const transportAccept = $iq({ to: this.remoteJid,\n type: 'set' })\n .c('jingle', {\n xmlns: 'urn:xmpp:jingle:1',\n action: 'transport-accept',\n initiator: this.initiatorJid,\n sid: this.sid\n });\n\n localSDP.media.forEach((medialines, idx) => {\n const mline = SDPUtil.parseMLine(medialines.split('\\r\\n')[0]);\n\n transportAccept.c('content',\n {\n creator:\n this.initiatorJid === this.localJid\n ? 'initiator'\n : 'responder',\n name: mline.media\n }\n );\n localSDP.transportToJingle(idx, transportAccept);\n transportAccept.up();\n });\n\n logger.info(`${this} Sending transport-accept`);\n logger.debug(transportAccept.tree());\n\n this.connection.sendIQ(transportAccept,\n success,\n this.newJingleErrorHandler(transportAccept, failure),\n IQ_TIMEOUT);\n }\n\n /**\n * Sends Jingle 'transport-reject' message which is a response to\n * 'transport-replace'.\n * @param success callback called when we receive 'RESULT' packet for\n * 'transport-replace'\n * @param failure function(error) called when we receive an error response\n * or when the request has timed out.\n *\n * FIXME method should be marked as private, but there's some spaghetti that\n * needs to be fixed prior doing that\n */\n sendTransportReject(success, failure) {\n // Send 'transport-reject', so that the focus will\n // know that we've failed\n const transportReject = $iq({ to: this.remoteJid,\n type: 'set' })\n .c('jingle', {\n xmlns: 'urn:xmpp:jingle:1',\n action: 'transport-reject',\n initiator: this.initiatorJid,\n sid: this.sid\n });\n\n logger.info(`${this} Sending 'transport-reject'`);\n logger.debug(transportReject.tree());\n\n this.connection.sendIQ(transportReject,\n success,\n this.newJingleErrorHandler(transportReject, failure),\n IQ_TIMEOUT);\n }\n\n /**\n * Sets the resolution constraint on the local camera track.\n * @param {number} maxFrameHeight - The user preferred max frame height.\n * @param {string} sourceName - The source name of the track.\n * @returns {Promise} promise that will be resolved when the operation is\n * successful and rejected otherwise.\n */\n setSenderVideoConstraint(maxFrameHeight, sourceName = null) {\n if (this._assertNotEnded()) {\n logger.info(`${this} setSenderVideoConstraint: ${maxFrameHeight}, sourceName: ${sourceName}`);\n\n // RN doesn't support RTCRtpSenders yet, aggresive layer suspension on RN is implemented\n // by changing the media direction in the SDP. This is applicable to jvb sessions only.\n if (!this.isP2P && browser.isReactNative() && typeof maxFrameHeight !== 'undefined') {\n const audioActive = this.peerconnection.audioTransferActive;\n const videoActive = this.peerconnection.videoTransferActive && maxFrameHeight > 0;\n\n return this.setMediaTransferActive(audioActive, videoActive);\n }\n\n const jitsiLocalTrack = sourceName\n ? this.rtc.getLocalVideoTracks().find(track => track.getSourceName() === sourceName)\n : this.rtc.getLocalVideoTrack();\n\n return this.peerconnection.setSenderVideoConstraints(maxFrameHeight, jitsiLocalTrack);\n }\n\n return Promise.resolve();\n }\n\n /**\n * @inheritDoc\n */\n terminate(success, failure, options) {\n if (this.state === JingleSessionState.ENDED) {\n return;\n }\n\n if (!options || Boolean(options.sendSessionTerminate)) {\n const sessionTerminate\n = $iq({\n to: this.remoteJid,\n type: 'set'\n })\n .c('jingle', {\n xmlns: 'urn:xmpp:jingle:1',\n action: 'session-terminate',\n initiator: this.initiatorJid,\n sid: this.sid\n })\n .c('reason')\n .c((options && options.reason) || 'success')\n .up();\n\n if (options && options.reasonDescription) {\n sessionTerminate\n .c('text')\n .t(options.reasonDescription)\n .up()\n .up();\n } else {\n sessionTerminate.up();\n }\n\n this._bridgeSessionId\n && sessionTerminate.c(\n 'bridge-session', {\n xmlns: 'http://jitsi.org/protocol/focus',\n id: this._bridgeSessionId,\n restart: options && options.requestRestart === true\n }).up();\n\n logger.info(`${this} Sending session-terminate`);\n logger.debug(sessionTerminate.tree());\n\n this.connection.sendIQ(\n sessionTerminate,\n success,\n this.newJingleErrorHandler(sessionTerminate, failure),\n IQ_TIMEOUT);\n } else {\n logger.info(`${this} Skipped sending session-terminate`);\n }\n\n // this should result in 'onTerminated' being called by strope.jingle.js\n this.connection.jingle.terminate(this.sid);\n }\n\n /**\n *\n * @param reasonCondition\n * @param reasonText\n */\n onTerminated(reasonCondition, reasonText) {\n // Do something with reason and reasonCondition when we start to care\n // this.reasonCondition = reasonCondition;\n // this.reasonText = reasonText;\n logger.info(`${this} Session terminated`, reasonCondition, reasonText);\n\n this._xmppListeners.forEach(removeListener => removeListener());\n this._xmppListeners = [];\n\n if (this._removeSenderVideoConstraintsChangeListener) {\n this._removeSenderVideoConstraintsChangeListener();\n }\n\n this.close();\n }\n\n /**\n * Handles XMPP connection state changes.\n *\n * @param {XmppConnection.Status} status - The new status.\n */\n onXmppStatusChanged(status) {\n if (status === XmppConnection.Status.CONNECTED && this._cachedOldLocalSdp) {\n logger.info(`${this} Sending SSRC update on reconnect`);\n this.notifyMySSRCUpdate(\n this._cachedOldLocalSdp,\n this._cachedNewLocalSdp);\n }\n }\n\n /**\n * Parse the information from the xml sourceAddElem and translate it\n * into sdp lines\n * @param {jquery xml element} sourceAddElem the source-add\n * element from jingle\n * @param {SDP object} currentRemoteSdp the current remote\n * sdp (as of this new source-add)\n * @returns {list} a list of SDP line strings that should\n * be added to the remote SDP\n */\n _parseSsrcInfoFromSourceAdd(sourceAddElem, currentRemoteSdp) {\n const addSsrcInfo = [];\n const self = this;\n\n $(sourceAddElem).each((i1, content) => {\n const name = $(content).attr('name');\n let lines = '';\n\n $(content)\n .find('ssrc-group[xmlns=\"urn:xmpp:jingle:apps:rtp:ssma:0\"]')\n .each(function() {\n // eslint-disable-next-line no-invalid-this\n const semantics = this.getAttribute('semantics');\n const ssrcs\n = $(this) // eslint-disable-line no-invalid-this\n .find('>source')\n .map(function() {\n // eslint-disable-next-line no-invalid-this\n return this.getAttribute('ssrc');\n })\n .get();\n\n if (ssrcs.length) {\n lines += `a=ssrc-group:${semantics} ${ssrcs.join(' ')}\\r\\n`;\n }\n });\n\n // handles both >source and >description>source\n const tmp\n = $(content).find(\n 'source[xmlns=\"urn:xmpp:jingle:apps:rtp:ssma:0\"]');\n\n /* eslint-disable no-invalid-this */\n tmp.each(function() {\n const ssrc = $(this).attr('ssrc');\n\n if (currentRemoteSdp.containsSSRC(ssrc)) {\n\n // Do not print the warning for unified plan p2p case since ssrcs are never removed from the SDP.\n !(self.usesUnifiedPlan && self.isP2P)\n && logger.warn(`${self} Source-add request for existing SSRC: ${ssrc}`);\n\n return;\n }\n\n // eslint-disable-next-line newline-per-chained-call\n $(this).find('>parameter').each(function() {\n lines += `a=ssrc:${ssrc} ${$(this).attr('name')}`;\n if ($(this).attr('value') && $(this).attr('value').length) {\n lines += `:${$(this).attr('value')}`;\n }\n lines += '\\r\\n';\n });\n });\n\n /* eslint-enable no-invalid-this */\n currentRemoteSdp.media.forEach((media, i2) => {\n if (!SDPUtil.findLine(media, `a=mid:${name}`)) {\n return;\n }\n if (!addSsrcInfo[i2]) {\n addSsrcInfo[i2] = '';\n }\n addSsrcInfo[i2] += lines;\n });\n });\n\n return addSsrcInfo;\n }\n\n /**\n * Handles a Jingle source-add message for this Jingle session.\n * @param elem An array of Jingle \"content\" elements.\n */\n addRemoteStream(elem) {\n this._addOrRemoveRemoteStream(true /* add */, elem);\n }\n\n /**\n * Handles a Jingle source-remove message for this Jingle session.\n * @param elem An array of Jingle \"content\" elements.\n */\n removeRemoteStream(elem) {\n this._addOrRemoveRemoteStream(false /* remove */, elem);\n }\n\n /**\n * Handles the deletion of SSRCs associated with a remote user from the remote description when the user leaves.\n *\n * @param {string} id Endpoint id of the participant that has left the call.\n * @returns {void}\n */\n removeRemoteStreamsOnLeave(id) {\n const workFunction = finishCallback => {\n const removeSsrcInfo = this.peerconnection.getRemoteSourceInfoByParticipant(id);\n\n if (removeSsrcInfo.length) {\n const oldLocalSdp = new SDP(this.peerconnection.localDescription.sdp);\n const newRemoteSdp = this._processRemoteRemoveSource(removeSsrcInfo);\n\n this._renegotiate(newRemoteSdp.raw)\n .then(() => {\n const newLocalSDP = new SDP(this.peerconnection.localDescription.sdp);\n\n this.notifyMySSRCUpdate(oldLocalSdp, newLocalSDP);\n finishCallback();\n })\n .catch(err => finishCallback(err));\n } else {\n finishCallback();\n }\n };\n\n logger.debug(`${this} Queued removeRemoteStreamsOnLeave task for participant ${id}`);\n\n this.modificationQueue.push(\n workFunction,\n error => {\n if (error) {\n logger.error(`${this} removeRemoteStreamsOnLeave error:`, error);\n } else {\n logger.info(`${this} removeRemoteStreamsOnLeave done!`);\n }\n });\n }\n\n /**\n * Handles either Jingle 'source-add' or 'source-remove' message for this\n * Jingle session.\n * @param {boolean} isAdd true for 'source-add' or false\n * otherwise.\n * @param {Array} elem an array of Jingle \"content\" elements.\n * @private\n */\n _addOrRemoveRemoteStream(isAdd, elem) {\n const logPrefix = isAdd ? 'addRemoteStream' : 'removeRemoteStream';\n\n if (isAdd) {\n this.readSsrcInfo(elem);\n }\n\n const workFunction = finishedCallback => {\n if (!this.peerconnection.localDescription\n || !this.peerconnection.localDescription.sdp) {\n const errMsg = `${logPrefix} - localDescription not ready yet`;\n\n logger.error(errMsg);\n finishedCallback(errMsg);\n\n return;\n }\n\n logger.log(`${this} Processing ${logPrefix}`);\n\n const oldLocalSdp = new SDP(this.peerconnection.localDescription.sdp);\n const sdp = new SDP(this.peerconnection.remoteDescription.sdp);\n const addOrRemoveSsrcInfo\n = isAdd\n ? this._parseSsrcInfoFromSourceAdd(elem, sdp)\n : this._parseSsrcInfoFromSourceRemove(elem, sdp);\n const newRemoteSdp\n = isAdd\n ? this._processRemoteAddSource(addOrRemoveSsrcInfo)\n : this._processRemoteRemoveSource(addOrRemoveSsrcInfo);\n\n // Add a workaround for a bug in Chrome (unified plan) for p2p connection. When the media direction on\n // the transceiver goes from \"inactive\" (both users join muted) to \"recvonly\" (peer unmutes), the browser\n // doesn't seem to create a decoder if the signaling state changes from \"have-local-offer\" to \"stable\".\n // Therefore, initiate a responder renegotiate even if the endpoint is the offerer to workaround this issue.\n // TODO - open a chrome bug and update the comments.\n const remoteDescription = new RTCSessionDescription({\n type: 'offer',\n sdp: newRemoteSdp.raw\n });\n const promise = isAdd && this.usesUnifiedPlan && this.isP2P && browser.isChromiumBased()\n ? this._responderRenegotiate(remoteDescription)\n : this._renegotiate(newRemoteSdp.raw);\n\n promise.then(() => {\n const newLocalSdp = new SDP(this.peerconnection.localDescription.sdp);\n\n logger.log(`${this} ${logPrefix} - OK`);\n this.notifyMySSRCUpdate(oldLocalSdp, newLocalSdp);\n finishedCallback();\n }, error => {\n logger.error(`${this} ${logPrefix} failed:`, error);\n finishedCallback(error);\n });\n };\n\n logger.debug(`${this} Queued ${logPrefix} task`);\n\n // Queue and execute\n this.modificationQueue.push(workFunction);\n }\n\n /**\n * Takes in a jingle offer iq, returns the new sdp offer\n * @param {jquery xml element} offerIq the incoming offer\n * @returns {SDP object} the jingle offer translated to SDP\n */\n _processNewJingleOfferIq(offerIq) {\n const remoteSdp = new SDP('');\n\n if (this.webrtcIceTcpDisable) {\n remoteSdp.removeTcpCandidates = true;\n }\n if (this.webrtcIceUdpDisable) {\n remoteSdp.removeUdpCandidates = true;\n }\n if (this.failICE) {\n remoteSdp.failICE = true;\n }\n\n remoteSdp.fromJingle(offerIq);\n this.readSsrcInfo($(offerIq).find('>content'));\n\n return remoteSdp;\n }\n\n /**\n * Remove the given ssrc lines from the current remote sdp\n * @param {list} removeSsrcInfo a list of SDP line strings that\n * should be removed from the remote SDP\n * @returns type {SDP Object} the new remote SDP (after removing the lines\n * in removeSsrcInfo\n */\n _processRemoteRemoveSource(removeSsrcInfo) {\n const remoteSdp = this.usesUnifiedPlan\n ? new SDP(this.peerconnection.peerconnection.remoteDescription.sdp)\n : new SDP(this.peerconnection.remoteDescription.sdp);\n\n removeSsrcInfo.forEach((lines, idx) => {\n // eslint-disable-next-line no-param-reassign\n lines = lines.split('\\r\\n');\n lines.pop(); // remove empty last element;\n if (this.usesUnifiedPlan) {\n lines.forEach(line => {\n const mid = remoteSdp.media.findIndex(mLine => mLine.includes(line));\n\n if (mid > -1) {\n const mediaType = SDPUtil.parseMLine(remoteSdp.media[mid].split('\\r\\n')[0])?.media;\n\n if (this.isP2P) {\n // Do not remove ssrcs from m-line in p2p mode. If the ssrc is removed and added back to\n // the same m-line (on source-add), Chrome/Safari do not render the media even if it is\n // being received and decoded from the remote peer. The webrtc spec is not clear about\n // m-line re-use and the browser vendors have implemented this differently. Currently work\n // around this by changing the media direction, that should be enough for the browser to\n // fire the \"removetrack\" event on the associated MediaStream. Also, the current direction\n // of the transceiver for p2p will depend on whether a local sources is added or not. It\n // will be 'sendrecv' if the local source is present, 'sendonly' otherwise.\n const desiredDirection = this.peerconnection.getDesiredMediaDirection(mediaType, false);\n\n [ MediaDirection.SENDRECV, MediaDirection.SENDONLY ].forEach(direction => {\n remoteSdp.media[mid] = remoteSdp.media[mid]\n .replace(`a=${direction}`, `a=${desiredDirection}`);\n });\n } else {\n // Change the port to 0 to reject the m-line associated with the source. The rejected\n // m-lines are recycled when new ssrcs need to be added to the remote description.\n const port = SDPUtil.parseMLine(remoteSdp.media[mid].split('\\r\\n')[0])?.port;\n\n remoteSdp.media[mid] = remoteSdp.media[mid].replace(`${line}\\r\\n`, '');\n remoteSdp.media[mid] = remoteSdp.media[mid].replace(\n `m=${mediaType} ${port}`,\n `m=${mediaType} 0`);\n }\n }\n });\n } else {\n lines.forEach(line => {\n remoteSdp.media[idx] = remoteSdp.media[idx].replace(`${line}\\r\\n`, '');\n });\n }\n });\n remoteSdp.raw = remoteSdp.session + remoteSdp.media.join('');\n\n return remoteSdp;\n }\n\n /**\n * Add the given ssrc lines to the current remote sdp\n * @param {list} addSsrcInfo a list of SDP line strings that\n * should be added to the remote SDP\n * @returns type {SDP Object} the new remote SDP (after removing the lines\n * in removeSsrcInfo\n */\n _processRemoteAddSource(addSsrcInfo) {\n const remoteSdp = new SDP(this.peerconnection.remoteDescription.sdp);\n\n addSsrcInfo.forEach((lines, idx) => {\n remoteSdp.media[idx] += lines;\n\n // Make sure to change the direction to 'sendrecv/sendonly' only for p2p connections. For jvb connections,\n // a new m-line is added for the new remote sources.\n if (this.isP2P && this.usesUnifiedPlan) {\n const mediaType = SDPUtil.parseMLine(remoteSdp.media[idx].split('\\r\\n')[0])?.media;\n const desiredDirection = this.peerconnection.getDesiredMediaDirection(mediaType, true);\n\n [ MediaDirection.RECVONLY, MediaDirection.INACTIVE ].forEach(direction => {\n remoteSdp.media[idx] = remoteSdp.media[idx]\n .replace(`a=${direction}`, `a=${desiredDirection}`);\n });\n }\n });\n remoteSdp.raw = remoteSdp.session + remoteSdp.media.join('');\n\n return remoteSdp;\n }\n\n /**\n * Do a new o/a flow using the existing remote description\n * @param {string} [optionalRemoteSdp] optional, raw remote sdp\n * to use. If not provided, the remote sdp from the\n * peerconnection will be used\n * @returns {Promise} promise which resolves when the\n * o/a flow is complete with no arguments or\n * rejects with an error {string}\n */\n _renegotiate(optionalRemoteSdp) {\n if (this.peerconnection.signalingState === 'closed') {\n const error = new Error('Attempted to renegotiate in state closed');\n\n this.room.eventEmitter.emit(XMPPEvents.RENEGOTIATION_FAILED, error, this);\n\n return Promise.reject(error);\n }\n\n const remoteSdp\n = optionalRemoteSdp || this.peerconnection.remoteDescription.sdp;\n\n if (!remoteSdp) {\n const error = new Error(`Can not renegotiate without remote description, current state: ${this.state}`);\n\n this.room.eventEmitter.emit(XMPPEvents.RENEGOTIATION_FAILED, error, this);\n\n return Promise.reject(error);\n }\n\n const remoteDescription = new RTCSessionDescription({\n type: this.isInitiator ? 'answer' : 'offer',\n sdp: remoteSdp\n });\n\n if (this.isInitiator) {\n return this._initiatorRenegotiate(remoteDescription);\n }\n\n return this._responderRenegotiate(remoteDescription);\n }\n\n /**\n * Renegotiate cycle implementation for the responder case.\n * @param {object} remoteDescription the SDP object as defined by the WebRTC\n * which will be used as remote description in the cycle.\n * @private\n */\n _responderRenegotiate(remoteDescription) {\n logger.debug(`${this} Renegotiate: setting remote description`);\n\n return this.peerconnection.setRemoteDescription(remoteDescription)\n .then(() => {\n logger.debug(`${this} Renegotiate: creating answer`);\n\n return this.peerconnection.createAnswer(this.mediaConstraints)\n .then(answer => {\n logger.debug(`${this} Renegotiate: setting local description`);\n\n return this.peerconnection.setLocalDescription(answer);\n });\n });\n }\n\n /**\n * Renegotiate cycle implementation for the initiator's case.\n * @param {object} remoteDescription the SDP object as defined by the WebRTC\n * which will be used as remote description in the cycle.\n * @private\n */\n _initiatorRenegotiate(remoteDescription) {\n logger.debug(`${this} Renegotiate: creating offer`);\n\n return this.peerconnection.createOffer(this.mediaConstraints)\n .then(offer => {\n logger.debug(`${this} Renegotiate: setting local description`);\n\n return this.peerconnection.setLocalDescription(offer)\n .then(() => {\n logger.debug(`${this} Renegotiate: setting remote description`);\n\n // eslint-disable-next-line max-len\n return this.peerconnection.setRemoteDescription(remoteDescription);\n });\n });\n }\n\n /**\n * Adds a new track to the peerconnection. This method needs to be called only when a secondary JitsiLocalTrack is\n * being added to the peerconnection for the first time.\n *\n * @param {JitsiLocalTrack} localTrack track to be added to the peer connection.\n * @returns {Promise} that resolves when the track is successfully added to the peerconnection, rejected\n * otherwise.\n */\n addTrack(localTrack) {\n if (!FeatureFlags.isMultiStreamSupportEnabled()\n || !this.usesUnifiedPlan\n || localTrack.type !== MediaType.VIDEO) {\n return Promise.reject(new Error('Multiple tracks of a given media type are not supported'));\n }\n\n const workFunction = finishedCallback => {\n const remoteSdp = new SDP(this.peerconnection.peerconnection.remoteDescription.sdp);\n\n // Add a new transceiver by adding a new mline in the remote description.\n remoteSdp.addMlineForNewLocalSource(MediaType.VIDEO);\n this._renegotiate(remoteSdp.raw)\n .then(() => finishedCallback(), error => finishedCallback(error));\n };\n\n return new Promise((resolve, reject) => {\n logger.debug(`${this} Queued renegotiation after addTrack`);\n\n this.modificationQueue.push(\n workFunction,\n error => {\n if (error) {\n logger.error(`${this} renegotiation after addTrack error`, error);\n reject(error);\n } else {\n logger.debug(`${this} renegotiation after addTrack executed - OK`);\n\n // Replace the track on the newly generated transceiver.\n return this.replaceTrack(null, localTrack)\n .then(() => resolve())\n .catch(() => reject());\n }\n });\n });\n }\n\n /**\n * Replaces oldTrack with newTrack and performs a single\n * offer/answer cycle after both operations are done. Either\n * oldTrack or newTrack can be null; replacing a valid\n * oldTrack with a null newTrack effectively just removes\n * oldTrack\n * @param {JitsiLocalTrack|null} oldTrack the current track in use to be\n * replaced\n * @param {JitsiLocalTrack|null} newTrack the new track to use\n * @returns {Promise} which resolves once the replacement is complete\n * with no arguments or rejects with an error {string}\n */\n replaceTrack(oldTrack, newTrack) {\n const workFunction = finishedCallback => {\n logger.debug(`${this} replaceTrack worker started. oldTrack = ${oldTrack}, newTrack = ${newTrack}`);\n\n const oldLocalSdp = this.peerconnection.localDescription.sdp;\n\n if (!this.usesUnifiedPlan) {\n // NOTE the code below assumes that no more than 1 video track\n // can be added to the peer connection.\n // Transition from camera to desktop share\n // or transition from one camera source to another.\n if (this.peerconnection.options.capScreenshareBitrate\n && oldTrack && newTrack && newTrack.isVideoTrack()) {\n // Clearing current primary SSRC will make\n // the SdpConsistency generate a new one which will result\n // with:\n // 1. source-remove for the old video stream.\n // 2. source-add for the new video stream.\n this.peerconnection.clearRecvonlySsrc();\n }\n\n // Transition from no video to video (unmute).\n if (!oldTrack && newTrack && newTrack.isVideoTrack()) {\n // Clearing current primary SSRC will make\n // the SdpConsistency generate a new one which will result\n // with:\n // 1. source-remove for the recvonly\n // 2. source-add for the new video stream\n this.peerconnection.clearRecvonlySsrc();\n\n // Transition from video to no video\n } else if (oldTrack && oldTrack.isVideoTrack() && !newTrack) {\n // Clearing current primary SSRC and generating the recvonly\n // will result in:\n // 1. source-remove for the old video stream\n // 2. source-add for the recvonly stream\n this.peerconnection.clearRecvonlySsrc();\n this.peerconnection.generateRecvonlySsrc();\n }\n }\n\n this.peerconnection.replaceTrack(oldTrack, newTrack)\n .then(shouldRenegotiate => {\n let promise = Promise.resolve();\n\n logger.debug(`${this} TPC.replaceTrack finished. shouldRenegotiate = ${\n shouldRenegotiate}, JingleSessionState = ${this.state}`);\n\n if (shouldRenegotiate\n && (oldTrack || newTrack)\n && this.state === JingleSessionState.ACTIVE) {\n promise = this._renegotiate().then(() => {\n const newLocalSDP = new SDP(this.peerconnection.localDescription.sdp);\n\n this.notifyMySSRCUpdate(new SDP(oldLocalSdp), newLocalSDP);\n });\n }\n\n return promise.then(() => {\n if (newTrack?.isVideoTrack()) {\n logger.debug(`${this} replaceTrack worker: configuring video stream`);\n\n // Configure the video encodings after the track is replaced.\n return this.peerconnection.configureSenderVideoEncodings(newTrack);\n }\n });\n })\n .then(() => finishedCallback(), error => finishedCallback(error));\n };\n\n return new Promise((resolve, reject) => {\n logger.debug(`${this} Queued replaceTrack task. Old track = ${oldTrack}, new track = ${newTrack}`);\n\n this.modificationQueue.push(\n workFunction,\n error => {\n if (error) {\n logger.error(`${this} Replace track error:`, error);\n reject(error);\n } else {\n logger.info(`${this} Replace track done!`);\n resolve();\n }\n });\n });\n }\n\n /**\n * Parse the information from the xml sourceRemoveElem and translate it\n * into sdp lines\n * @param {jquery xml element} sourceRemoveElem the source-remove\n * element from jingle\n * @param {SDP object} currentRemoteSdp the current remote\n * sdp (as of this new source-remove)\n * @returns {list} a list of SDP line strings that should\n * be removed from the remote SDP\n */\n _parseSsrcInfoFromSourceRemove(sourceRemoveElem, currentRemoteSdp) {\n const removeSsrcInfo = [];\n\n $(sourceRemoveElem).each((i1, content) => {\n const name = $(content).attr('name');\n let lines = '';\n\n $(content)\n .find('ssrc-group[xmlns=\"urn:xmpp:jingle:apps:rtp:ssma:0\"]')\n .each(function() {\n /* eslint-disable no-invalid-this */\n const semantics = this.getAttribute('semantics');\n const ssrcs\n = $(this)\n .find('>source')\n .map(function() {\n return this.getAttribute('ssrc');\n })\n .get();\n\n if (ssrcs.length) {\n lines\n += `a=ssrc-group:${semantics} ${\n ssrcs.join(' ')}\\r\\n`;\n }\n\n /* eslint-enable no-invalid-this */\n });\n const ssrcs = [];\n\n // handles both >source and >description>source versions\n const tmp\n = $(content).find(\n 'source[xmlns=\"urn:xmpp:jingle:apps:rtp:ssma:0\"]');\n\n tmp.each(function() {\n // eslint-disable-next-line no-invalid-this\n const ssrc = $(this).attr('ssrc');\n\n ssrcs.push(ssrc);\n });\n currentRemoteSdp.media.forEach((media, i2) => {\n if (!SDPUtil.findLine(media, `a=mid:${name}`)) {\n return;\n }\n if (!removeSsrcInfo[i2]) {\n removeSsrcInfo[i2] = '';\n }\n ssrcs.forEach(ssrc => {\n const ssrcLines\n = SDPUtil.findLines(media, `a=ssrc:${ssrc}`);\n\n if (ssrcLines.length) {\n removeSsrcInfo[i2] += `${ssrcLines.join('\\r\\n')}\\r\\n`;\n }\n });\n removeSsrcInfo[i2] += lines;\n });\n });\n\n return removeSsrcInfo;\n }\n\n /**\n * Will print an error if there is any difference, between the SSRCs given\n * in the oldSDP and the ones currently described in\n * the peerconnection's local description.\n * @param {string} operationName the operation's name which will be printed\n * in the error message.\n * @param {SDP} oldSDP the old local SDP which will be compared with\n * the current one.\n * @return {boolean} true if there was any change or false\n * otherwise.\n * @private\n */\n _verifyNoSSRCChanged(operationName, oldSDP) {\n const currentLocalSDP\n = new SDP(this.peerconnection.localDescription.sdp);\n let sdpDiff = new SDPDiffer(oldSDP, currentLocalSDP);\n const addedMedia = sdpDiff.getNewMedia();\n\n if (Object.keys(addedMedia).length) {\n logger.error(`${this} - some SSRC were added on ${operationName}`, addedMedia);\n\n return false;\n }\n\n sdpDiff = new SDPDiffer(currentLocalSDP, oldSDP);\n const removedMedia = sdpDiff.getNewMedia();\n\n if (Object.keys(removedMedia).length) {\n logger.error(`${this} - some SSRCs were removed on ${operationName}`, removedMedia);\n\n return false;\n }\n\n return true;\n }\n\n /**\n * Adds local track back to this session, as part of the unmute operation.\n * @param {JitsiLocalTrack} track\n * @return {Promise} a promise that will resolve once the local track is\n * added back to this session and renegotiation succeeds. Will be rejected\n * with a string that provides some error details in case something\n * goes wrong.\n */\n addTrackAsUnmute(track) {\n return this._addRemoveTrackAsMuteUnmute(\n false /* add as unmute */, track)\n .then(() => {\n // Configure the video encodings after the track is unmuted. If the user joins the call muted and\n // unmutes it the first time, all the parameters need to be configured.\n if (track.isVideoTrack()) {\n return this.peerconnection.configureSenderVideoEncodings(track);\n }\n });\n }\n\n /**\n * Remove local track as part of the mute operation.\n * @param {JitsiLocalTrack} track the local track to be removed\n * @return {Promise} a promise which will be resolved once the local track\n * is removed from this session and the renegotiation is performed.\n * The promise will be rejected with a string that the describes\n * the error if anything goes wrong.\n */\n removeTrackAsMute(track) {\n return this._addRemoveTrackAsMuteUnmute(\n true /* remove as mute */, track);\n }\n\n /**\n * See {@link addTrackAsUnmute} and {@link removeTrackAsMute}.\n * @param {boolean} isMute true for \"remove as mute\" or\n * false for \"add as unmute\".\n * @param {JitsiLocalTrack} track the track that will be added/removed\n * @private\n */\n _addRemoveTrackAsMuteUnmute(isMute, track) {\n if (!track) {\n return Promise.reject('invalid \"track\" argument value');\n }\n const operationName = isMute ? 'removeTrackMute' : 'addTrackUnmute';\n const workFunction = finishedCallback => {\n const tpc = this.peerconnection;\n\n if (!tpc) {\n finishedCallback(\n `Error: tried ${operationName} track with no active peer`\n + 'connection');\n\n return;\n }\n const oldLocalSDP = tpc.localDescription.sdp;\n const operationPromise\n = isMute\n ? tpc.removeTrackMute(track)\n : tpc.addTrackUnmute(track);\n\n operationPromise\n .then(shouldRenegotiate => {\n if (shouldRenegotiate && oldLocalSDP && tpc.remoteDescription.sdp) {\n this._renegotiate()\n .then(() => {\n // The results are ignored, as this check failure is not enough to fail the whole\n // operation. It will log an error inside for plan-b.\n !this.usesUnifiedPlan && this._verifyNoSSRCChanged(operationName, new SDP(oldLocalSDP));\n const newLocalSdp = tpc.localDescription.sdp;\n\n // Signal the ssrc if an unmute operation results in a new ssrc being generated.\n this.notifyMySSRCUpdate(new SDP(oldLocalSDP), new SDP(newLocalSdp));\n finishedCallback();\n });\n } else {\n finishedCallback();\n }\n },\n finishedCallback /* will be called with an error */);\n };\n\n logger.debug(`${this} Queued ${operationName} task`);\n\n return new Promise((resolve, reject) => {\n this.modificationQueue.push(\n workFunction,\n error => {\n if (error) {\n logger.error(`${this} ${operationName} failed`);\n reject(error);\n } else {\n logger.debug(`${this} ${operationName} done`);\n resolve();\n }\n });\n });\n }\n\n /**\n * Resumes or suspends media transfer over the underlying peer connection.\n * @param {boolean} audioActive true to enable audio media\n * transfer or false to suspend audio media transmission.\n * @param {boolean} videoActive true to enable video media\n * transfer or false to suspend video media transmission.\n * @return {Promise} a Promise which will resolve once\n * the operation is done. It will be rejected with an error description as\n * a string in case anything goes wrong.\n */\n setMediaTransferActive(audioActive, videoActive) {\n if (!this.peerconnection) {\n return Promise.reject(\n 'Can not modify transfer active state,'\n + ' before \"initialize\" is called');\n }\n\n const logAudioStr = audioActive ? 'audio active' : 'audio inactive';\n const logVideoStr = videoActive ? 'video active' : 'video inactive';\n\n logger.info(`${this} Queued make ${logVideoStr}, ${logAudioStr} task`);\n\n const workFunction = finishedCallback => {\n const isSessionActive = this.state === JingleSessionState.ACTIVE;\n\n // Because the value is modified on the queue it's impossible to\n // check it's final value reliably prior to submitting the task.\n // The rule here is that the last submitted state counts.\n // Check the values here to avoid unnecessary renegotiation cycle.\n const audioActiveChanged\n = this.peerconnection.setAudioTransferActive(audioActive);\n\n if (this._localVideoActive !== videoActive) {\n this._localVideoActive = videoActive;\n\n // Do only for P2P - Jicofo will reply with 'bad-request'\n // We don't want to send 'content-modify', before the initial\n // O/A (state === JingleSessionState.ACTIVE), because that will\n // mess up video media direction in the remote SDP.\n // 'content-modify' when processed only affects the media\n // direction in the local SDP. We're doing that, because setting\n // 'inactive' on video media in remote SDP will mess up our SDP\n // translation chain (simulcast, RTX, video mute etc.).\n if (this.isP2P && isSessionActive) {\n this.sendContentModify();\n }\n }\n\n const pcVideoActiveChanged\n = this.peerconnection.setVideoTransferActive(\n this._localVideoActive && this._remoteVideoActive);\n\n // Will do the sRD/sLD cycle to update SDPs and adjust the media\n // direction\n if (isSessionActive\n && (audioActiveChanged || pcVideoActiveChanged)) {\n this._renegotiate()\n .then(\n finishedCallback,\n finishedCallback /* will be called with an error */);\n } else {\n finishedCallback();\n }\n };\n\n return new Promise((resolve, reject) => {\n this.modificationQueue.push(\n workFunction,\n error => {\n if (error) {\n logger.error(`${this} Make ${logVideoStr}, ${logAudioStr} task failed!`);\n reject(error);\n } else {\n logger.debug(`${this} Make ${logVideoStr}, ${logAudioStr} task done!`);\n resolve();\n }\n });\n });\n }\n\n /**\n * Will put and execute on the queue a session modify task. Currently it\n * only checks the senders attribute of the video content in order to figure\n * out if the remote peer has video in the inactive state (stored locally\n * in {@link _remoteVideoActive} - see field description for more info).\n * @param {jQuery} jingleContents jQuery selector pointing to the jingle\n * element of the session modify IQ.\n * @see {@link _remoteVideoActive}\n * @see {@link _localVideoActive}\n */\n modifyContents(jingleContents) {\n const newVideoSenders\n = JingleSessionPC.parseVideoSenders(jingleContents);\n const newMaxFrameHeight\n = JingleSessionPC.parseMaxFrameHeight(jingleContents);\n\n // frame height is optional in our content-modify protocol\n if (newMaxFrameHeight) {\n logger.info(`${this} received remote max frame height: ${newMaxFrameHeight}`);\n this.remoteRecvMaxFrameHeight = newMaxFrameHeight;\n this.eventEmitter.emit(\n MediaSessionEvents.REMOTE_VIDEO_CONSTRAINTS_CHANGED, this);\n }\n\n if (newVideoSenders === null) {\n logger.error(\n `${this} - failed to parse video \"senders\" attribute in`\n + '\"content-modify\" action');\n\n return;\n }\n\n const workFunction = finishedCallback => {\n if (this._assertNotEnded('content-modify')\n && this._modifyRemoteVideoActive(newVideoSenders)) {\n // Will do the sRD/sLD cycle to update SDPs and adjust\n // the media direction\n this._renegotiate()\n .then(finishedCallback, finishedCallback /* (error) */);\n } else {\n finishedCallback();\n }\n };\n\n logger.debug(`${this} queued \"content-modify\" task(video senders=\"${newVideoSenders}\")`);\n\n this.modificationQueue.push(\n workFunction,\n error => {\n if (error) {\n logger.error(`${this} \"content-modify\" failed`, error);\n } else {\n logger.debug(`${this} \"content-modify\" task(video senders=\"${newVideoSenders}\") done`);\n }\n });\n }\n\n /**\n * Processes new value of remote video \"senders\" Jingle attribute and tries\n * to apply it for {@link _remoteVideoActive}.\n * @param {string} remoteVideoSenders the value of \"senders\" attribute of\n * Jingle video content element advertised by remote peer.\n * @return {boolean} true if the change affected state of\n * the underlying peerconnection and renegotiation is required for\n * the changes to take effect.\n * @private\n */\n _modifyRemoteVideoActive(remoteVideoSenders) {\n const isRemoteVideoActive\n = remoteVideoSenders === 'both'\n || (remoteVideoSenders === 'initiator' && this.isInitiator)\n || (remoteVideoSenders === 'responder' && !this.isInitiator);\n\n if (isRemoteVideoActive !== this._remoteVideoActive) {\n logger.debug(`${this} new remote video active: ${isRemoteVideoActive}`);\n this._remoteVideoActive = isRemoteVideoActive;\n }\n\n return this.peerconnection.setVideoTransferActive(\n this._localVideoActive && this._remoteVideoActive);\n }\n\n /**\n * Figures out added/removed ssrcs and send update IQs.\n * @param oldSDP SDP object for old description.\n * @param newSDP SDP object for new description.\n */\n notifyMySSRCUpdate(oldSDP, newSDP) {\n if (this.state !== JingleSessionState.ACTIVE) {\n logger.warn(`${this} Skipping SSRC update in '${this.state} ' state.`);\n\n return;\n }\n\n if (!this.connection.connected) {\n // The goal is to compare the oldest SDP with the latest one upon reconnect\n if (!this._cachedOldLocalSdp) {\n this._cachedOldLocalSdp = oldSDP;\n }\n this._cachedNewLocalSdp = newSDP;\n logger.warn(`${this} Not sending SSRC update while the signaling is disconnected`);\n\n return;\n }\n\n this._cachedOldLocalSdp = undefined;\n this._cachedNewLocalSdp = undefined;\n\n const getSignaledSourceInfo = sdpDiffer => {\n const newMedia = sdpDiffer.getNewMedia();\n let ssrcs = [];\n let mediaType = null;\n\n // It is assumed that sources are signaled one at a time.\n Object.keys(newMedia).forEach(mediaIndex => {\n const signaledSsrcs = Object.keys(newMedia[mediaIndex].ssrcs);\n\n mediaType = newMedia[mediaIndex].mid;\n if (signaledSsrcs?.length) {\n ssrcs = ssrcs.concat(signaledSsrcs);\n }\n });\n\n return {\n mediaType,\n ssrcs\n };\n };\n\n // send source-remove IQ.\n let sdpDiffer = new SDPDiffer(newSDP, oldSDP);\n const remove = $iq({ to: this.remoteJid,\n type: 'set' })\n .c('jingle', {\n xmlns: 'urn:xmpp:jingle:1',\n action: 'source-remove',\n initiator: this.initiatorJid,\n sid: this.sid\n }\n );\n const removedAnySSRCs = sdpDiffer.toJingle(remove);\n\n // context a common object for one run of ssrc update (source-add and source-remove) so we can match them if we\n // need to\n const ctx = {};\n\n if (removedAnySSRCs) {\n const sourceInfo = getSignaledSourceInfo(sdpDiffer);\n\n // Log only the SSRCs instead of the full IQ.\n logger.info(`${this} Sending source-remove for ${sourceInfo.mediaType} ssrcs=${sourceInfo.ssrcs}`);\n this.connection.sendIQ(\n remove,\n () => {\n this.room.eventEmitter.emit(XMPPEvents.SOURCE_REMOVE, this, ctx);\n },\n this.newJingleErrorHandler(remove, error => {\n this.room.eventEmitter.emit(XMPPEvents.SOURCE_REMOVE_ERROR, this, error, ctx);\n }),\n IQ_TIMEOUT);\n }\n\n // send source-add IQ.\n sdpDiffer = new SDPDiffer(oldSDP, newSDP);\n const add = $iq({ to: this.remoteJid,\n type: 'set' })\n .c('jingle', {\n xmlns: 'urn:xmpp:jingle:1',\n action: 'source-add',\n initiator: this.initiatorJid,\n sid: this.sid\n }\n );\n\n const containsNewSSRCs = sdpDiffer.toJingle(add);\n\n if (containsNewSSRCs) {\n const sourceInfo = getSignaledSourceInfo(sdpDiffer);\n\n // Log only the SSRCs instead of the full IQ.\n logger.info(`${this} Sending source-add for ${sourceInfo.mediaType} ssrcs=${sourceInfo.ssrcs}`);\n this.connection.sendIQ(\n add,\n () => {\n this.room.eventEmitter.emit(XMPPEvents.SOURCE_ADD, this, ctx);\n },\n this.newJingleErrorHandler(add, error => {\n this.room.eventEmitter.emit(XMPPEvents.SOURCE_ADD_ERROR, this, error, sourceInfo.mediaType, ctx);\n }),\n IQ_TIMEOUT);\n }\n }\n\n /**\n * Method returns function(errorResponse) which is a callback to be passed\n * to Strophe connection.sendIQ method. An 'error' structure is created that\n * is passed as 1st argument to given failureCb. The format of this\n * structure is as follows:\n * {\n * code: {XMPP error response code}\n * reason: {the name of XMPP error reason element or 'timeout' if the\n * request has timed out within IQ_TIMEOUT milliseconds}\n * source: {request.tree() that provides original request}\n * session: {this JingleSessionPC.toString()}\n * }\n * @param request Strophe IQ instance which is the request to be dumped into\n * the error structure\n * @param failureCb function(error) called when error response was returned\n * or when a timeout has occurred.\n * @returns {function(this:JingleSessionPC)}\n */\n newJingleErrorHandler(request, failureCb) {\n return errResponse => {\n\n const error = {};\n\n // Get XMPP error code and condition(reason)\n const errorElSel = $(errResponse).find('error');\n\n if (errorElSel.length) {\n error.code = errorElSel.attr('code');\n const errorReasonSel = $(errResponse).find('error :first');\n\n if (errorReasonSel.length) {\n error.reason = errorReasonSel[0].tagName;\n }\n\n const errorMsgSel = errorElSel.find('>text');\n\n if (errorMsgSel.length) {\n error.msg = errorMsgSel.text();\n }\n }\n\n if (!errResponse) {\n error.reason = 'timeout';\n }\n\n error.session = this.toString();\n\n if (failureCb) {\n failureCb(error);\n } else if (this.state === JingleSessionState.ENDED\n && error.reason === 'item-not-found') {\n // When remote peer decides to terminate the session, but it\n // still have few messages on the queue for processing,\n // it will first send us 'session-terminate' (we enter ENDED)\n // and then follow with 'item-not-found' for the queued requests\n // We don't want to have that logged on error level.\n logger.debug(`${this} Jingle error: ${JSON.stringify(error)}`);\n } else {\n GlobalOnErrorHandler.callErrorHandler(\n new Error(\n `Jingle error: ${JSON.stringify(error)}`));\n }\n };\n }\n\n /**\n * Returns the ice connection state for the peer connection.\n * @returns the ice connection state for the peer connection.\n */\n getIceConnectionState() {\n return this.peerconnection.getConnectionState();\n }\n\n /**\n * Closes the peerconnection.\n */\n close() {\n this.state = JingleSessionState.ENDED;\n this.establishmentDuration = undefined;\n\n if (this.peerconnection) {\n this.peerconnection.onicecandidate = null;\n this.peerconnection.oniceconnectionstatechange = null;\n this.peerconnection.onnegotiationneeded = null;\n this.peerconnection.onsignalingstatechange = null;\n }\n\n logger.debug(`${this} Clearing modificationQueue`);\n\n // Remove any pending tasks from the queue\n this.modificationQueue.clear();\n\n logger.debug(`${this} Queued PC close task`);\n this.modificationQueue.push(finishCallback => {\n // do not try to close if already closed.\n this.peerconnection && this.peerconnection.close();\n finishCallback();\n logger.debug(`${this} PC close task done!`);\n });\n\n logger.debug(`${this} Shutdown modificationQueue!`);\n\n // No more tasks can go in after the close task\n this.modificationQueue.shutdown();\n }\n\n /**\n * Converts to string with minor summary.\n * @return {string}\n */\n toString() {\n return `JingleSessionPC[session=${this.isP2P ? 'P2P' : 'JVB'},initiator=${this.isInitiator},sid=${this.sid}]`;\n }\n\n /**\n * If the A/B test for suspend video is disabled according to the room's\n * configuration, returns undefined. Otherwise returns a boolean which\n * indicates whether the suspend video option should be enabled or disabled.\n * @param {JingleSessionPCOptions} options - The config options.\n */\n _abtestSuspendVideoEnabled({ abTesting }) {\n if (!abTesting || !abTesting.enableSuspendVideoTest) {\n return;\n }\n\n // We want the two participants in a P2P call to agree on the value of\n // the \"suspend\" option. We use the JID of the initiator, because it is\n // both randomly selected and agreed upon by both participants.\n const jid = this._getInitiatorJid();\n\n return integerHash(jid) % 2 === 0;\n }\n}\n","/* global $ */\n\nimport { getLogger } from '@jitsi/logger';\nimport { $iq, Strophe } from 'strophe.js';\n\nimport * as MediaType from '../../service/RTC/MediaType';\nimport {\n ACTION_JINGLE_TR_RECEIVED,\n ACTION_JINGLE_TR_SUCCESS,\n createJingleEvent\n} from '../../service/statistics/AnalyticsEvents';\nimport XMPPEvents from '../../service/xmpp/XMPPEvents';\nimport Statistics from '../statistics/statistics';\nimport GlobalOnErrorHandler from '../util/GlobalOnErrorHandler';\nimport RandomUtil from '../util/RandomUtil';\n\nimport ConnectionPlugin from './ConnectionPlugin';\nimport { expandSourcesFromJson } from './JingleHelperFunctions';\nimport JingleSessionPC from './JingleSessionPC';\n\nconst logger = getLogger(__filename);\n\n// XXX Strophe is build around the idea of chaining function calls so allow long\n// function call chains.\n/* eslint-disable newline-per-chained-call */\n\n/**\n * Parses the transport XML element and returns the list of ICE candidates formatted as text.\n *\n * @param {*} transport Transport XML element extracted from the IQ.\n * @returns {Array}\n */\nfunction _parseIceCandidates(transport) {\n const candidates = $(transport).find('>candidate');\n const parseCandidates = [];\n\n // Extract the candidate information from the IQ.\n candidates.each((_, candidate) => {\n const attributes = candidate.attributes;\n const candidateAttrs = [];\n\n for (let i = 0; i < attributes.length; i++) {\n const attr = attributes[i];\n\n candidateAttrs.push(`${attr.name}: ${attr.value}`);\n }\n parseCandidates.push(candidateAttrs.join(' '));\n });\n\n return parseCandidates;\n}\n\n/**\n *\n */\nexport default class JingleConnectionPlugin extends ConnectionPlugin {\n /**\n * Creates new JingleConnectionPlugin\n * @param {XMPP} xmpp\n * @param {EventEmitter} eventEmitter\n * @param {Object} iceConfig an object that holds the iceConfig to be passed\n * to the p2p and the jvb PeerConnection.\n */\n constructor(xmpp, eventEmitter, iceConfig) {\n super();\n this.xmpp = xmpp;\n this.eventEmitter = eventEmitter;\n this.sessions = {};\n this.jvbIceConfig = iceConfig.jvb;\n this.p2pIceConfig = iceConfig.p2p;\n this.mediaConstraints = {\n offerToReceiveAudio: true,\n offerToReceiveVideo: true\n };\n }\n\n /**\n *\n * @param connection\n */\n init(connection) {\n super.init(connection);\n this.connection.addHandler(this.onJingle.bind(this),\n 'urn:xmpp:jingle:1', 'iq', 'set', null, null);\n }\n\n /**\n *\n * @param iq\n */\n onJingle(iq) {\n const sid = $(iq).find('jingle').attr('sid');\n const action = $(iq).find('jingle').attr('action');\n const fromJid = iq.getAttribute('from');\n\n // send ack first\n const ack = $iq({ type: 'result',\n to: fromJid,\n id: iq.getAttribute('id')\n });\n\n let sess = this.sessions[sid];\n\n if (action !== 'session-initiate') {\n if (!sess) {\n ack.attrs({ type: 'error' });\n ack.c('error', { type: 'cancel' })\n .c('item-not-found', {\n xmlns: 'urn:ietf:params:xml:ns:xmpp-stanzas'\n })\n .up()\n .c('unknown-session', {\n xmlns: 'urn:xmpp:jingle:errors:1'\n });\n logger.warn(`invalid session id: ${sid}`);\n logger.debug(iq);\n this.connection.send(ack);\n\n return true;\n }\n\n // local jid is not checked\n if (fromJid !== sess.remoteJid) {\n logger.warn(\n 'jid mismatch for session id', sid, sess.remoteJid, iq);\n ack.attrs({ type: 'error' });\n ack.c('error', { type: 'cancel' })\n .c('item-not-found', {\n xmlns: 'urn:ietf:params:xml:ns:xmpp-stanzas'\n })\n .up()\n .c('unknown-session', {\n xmlns: 'urn:xmpp:jingle:errors:1'\n });\n this.connection.send(ack);\n\n return true;\n }\n } else if (sess !== undefined) {\n // Existing session with same session id. This might be out-of-order\n // if the sess.remoteJid is the same as from.\n ack.attrs({ type: 'error' });\n ack.c('error', { type: 'cancel' })\n .c('service-unavailable', {\n xmlns: 'urn:ietf:params:xml:ns:xmpp-stanzas'\n })\n .up();\n logger.warn('duplicate session id', sid, iq);\n this.connection.send(ack);\n\n return true;\n }\n const now = window.performance.now();\n\n // FIXME that should work most of the time, but we'd have to\n // think how secure it is to assume that user with \"focus\"\n // nickname is Jicofo.\n const isP2P = Strophe.getResourceFromJid(fromJid) !== 'focus';\n\n // see http://xmpp.org/extensions/xep-0166.html#concepts-session\n\n const jsonMessages = $(iq).find('jingle>json-message');\n\n if (jsonMessages?.length) {\n let audioVideoSsrcs;\n\n logger.info(`Found a JSON-encoded element in ${action}, translating to standard Jingle.`);\n for (let i = 0; i < jsonMessages.length; i++) {\n // Currently there is always a single json-message in the IQ with the source information.\n audioVideoSsrcs = expandSourcesFromJson(iq, jsonMessages[i]);\n }\n\n if (audioVideoSsrcs?.size) {\n const logMessage = [];\n\n for (const endpoint of audioVideoSsrcs.keys()) {\n logMessage.push(`${endpoint}:[${audioVideoSsrcs.get(endpoint)}]`);\n }\n logger.debug(`Received ${action} from ${fromJid} with sources=${logMessage.join(', ')}`);\n }\n\n // TODO: is there a way to remove the json-message elements once we've extracted the information?\n // removeChild doesn't seem to work.\n }\n\n switch (action) {\n case 'session-initiate': {\n logger.log('(TIME) received session-initiate:\\t', now);\n const startMuted = $(iq).find('jingle>startmuted');\n\n isP2P && logger.debug(`Received ${action} from ${fromJid}`);\n if (startMuted?.length) {\n const audioMuted = startMuted.attr(MediaType.AUDIO);\n const videoMuted = startMuted.attr(MediaType.VIDEO);\n\n this.eventEmitter.emit(\n XMPPEvents.START_MUTED_FROM_FOCUS,\n audioMuted === 'true',\n videoMuted === 'true');\n }\n const pcConfig = isP2P ? this.p2pIceConfig : this.jvbIceConfig;\n\n sess\n = new JingleSessionPC(\n $(iq).find('jingle').attr('sid'),\n $(iq).attr('to'),\n fromJid,\n this.connection,\n this.mediaConstraints,\n\n // Makes a copy in order to prevent exception thrown on RN when either this.p2pIceConfig or\n // this.jvbIceConfig is modified and there's a PeerConnection instance holding a reference\n JSON.parse(JSON.stringify(pcConfig)),\n isP2P,\n /* initiator */ false);\n\n this.sessions[sess.sid] = sess;\n this.eventEmitter.emit(XMPPEvents.CALL_INCOMING, sess, $(iq).find('>jingle'), now);\n break;\n }\n case 'session-accept': {\n const ssrcs = [];\n const contents = $(iq).find('jingle>content');\n\n // Extract the SSRCs from the session-accept received from a p2p peer.\n for (const content of contents) {\n const ssrc = $(content).find('description').attr('ssrc');\n\n ssrc && ssrcs.push(ssrc);\n }\n logger.debug(`Received ${action} from ${fromJid} with ssrcs=${ssrcs}`);\n this.eventEmitter.emit(XMPPEvents.CALL_ACCEPTED, sess, $(iq).find('>jingle'));\n break;\n }\n case 'content-modify': {\n const height = $(iq).find('jingle>content[name=\"video\"]>max-frame-height');\n\n logger.debug(`Received ${action} from ${fromJid} with a max-frame-height=${height?.text()}`);\n sess.modifyContents($(iq).find('>jingle'));\n break;\n }\n case 'transport-info': {\n const candidates = _parseIceCandidates($(iq).find('jingle>content>transport'));\n\n logger.debug(`Received ${action} from ${fromJid} for candidates=${candidates.join(', ')}`);\n this.eventEmitter.emit(XMPPEvents.TRANSPORT_INFO, sess, $(iq).find('>jingle'));\n break;\n }\n case 'session-terminate': {\n logger.log('terminating...', sess.sid);\n let reasonCondition = null;\n let reasonText = null;\n\n if ($(iq).find('>jingle>reason').length) {\n reasonCondition\n = $(iq).find('>jingle>reason>:first')[0].tagName;\n reasonText = $(iq).find('>jingle>reason>text').text();\n }\n logger.debug(`Received ${action} from ${fromJid} disconnect reason=${reasonText}`);\n this.terminate(sess.sid, reasonCondition, reasonText);\n this.eventEmitter.emit(XMPPEvents.CALL_ENDED, sess, reasonCondition, reasonText);\n break;\n }\n case 'transport-replace': {\n logger.info('(TIME) Start transport replace:\\t', now);\n const transport = $(iq).find('jingle>content>transport');\n const candidates = _parseIceCandidates(transport);\n const iceUfrag = $(transport).attr('ufrag');\n const icePwd = $(transport).attr('pwd');\n const dtlsFingerprint = $(transport).find('>fingerprint')?.text();\n\n logger.debug(`Received ${action} from ${fromJid} with iceUfrag=${iceUfrag},`\n + ` icePwd=${icePwd}, DTLS fingerprint=${dtlsFingerprint}, candidates=${candidates.join(', ')}`);\n\n Statistics.sendAnalytics(createJingleEvent(\n ACTION_JINGLE_TR_RECEIVED,\n {\n p2p: isP2P,\n value: now\n }));\n\n sess.replaceTransport($(iq).find('>jingle'), () => {\n const successTime = window.performance.now();\n\n logger.info('(TIME) Transport replace success:\\t', successTime);\n Statistics.sendAnalytics(createJingleEvent(\n ACTION_JINGLE_TR_SUCCESS,\n {\n p2p: isP2P,\n value: successTime\n }));\n }, error => {\n GlobalOnErrorHandler.callErrorHandler(error);\n logger.error('Transport replace failed', error);\n sess.sendTransportReject();\n });\n break;\n }\n case 'source-add':\n sess.addRemoteStream($(iq).find('>jingle>content'));\n break;\n case 'source-remove':\n sess.removeRemoteStream($(iq).find('>jingle>content'));\n break;\n default:\n logger.warn('jingle action not implemented', action);\n ack.attrs({ type: 'error' });\n ack.c('error', { type: 'cancel' })\n .c('bad-request',\n { xmlns: 'urn:ietf:params:xml:ns:xmpp-stanzas' })\n .up();\n break;\n }\n this.connection.send(ack);\n\n return true;\n }\n\n /**\n * Creates new JingleSessionPC meant to be used in a direct P2P\n * connection, configured as 'initiator'.\n * @param {string} me our JID\n * @param {string} peer remote participant's JID\n * @return {JingleSessionPC}\n */\n newP2PJingleSession(me, peer) {\n const sess\n = new JingleSessionPC(\n RandomUtil.randomHexString(12),\n me,\n peer,\n this.connection,\n this.mediaConstraints,\n this.p2pIceConfig,\n /* P2P */ true,\n /* initiator */ true);\n\n this.sessions[sess.sid] = sess;\n\n return sess;\n }\n\n /**\n *\n * @param sid\n * @param reasonCondition\n * @param reasonText\n */\n terminate(sid, reasonCondition, reasonText) {\n if (this.sessions.hasOwnProperty(sid)) {\n if (this.sessions[sid].state !== 'ended') {\n this.sessions[sid].onTerminated(reasonCondition, reasonText);\n }\n delete this.sessions[sid];\n }\n }\n\n /**\n *\n */\n getStunAndTurnCredentials() {\n // get stun and turn configuration from server via xep-0215\n // uses time-limited credentials as described in\n // http://tools.ietf.org/html/draft-uberti-behave-turn-rest-00\n //\n // See https://modules.prosody.im/mod_turncredentials.html\n // for a prosody module which implements this.\n // Or the new implementation https://modules.prosody.im/mod_external_services which will be in prosody 0.12\n //\n // Currently, this doesn't work with updateIce and therefore credentials\n // with a long validity have to be fetched before creating the\n // peerconnection.\n // TODO: implement refresh via updateIce as described in\n // https://code.google.com/p/webrtc/issues/detail?id=1650\n this.connection.sendIQ(\n $iq({ type: 'get',\n to: this.xmpp.options.hosts.domain })\n .c('services', { xmlns: 'urn:xmpp:extdisco:2' }),\n v2Res => this.onReceiveStunAndTurnCredentials(v2Res),\n () => {\n logger.warn('getting turn credentials with extdisco:2 failed, trying extdisco:1');\n this.connection.sendIQ(\n $iq({ type: 'get',\n to: this.xmpp.options.hosts.domain })\n .c('services', { xmlns: 'urn:xmpp:extdisco:1' }),\n v1Res => this.onReceiveStunAndTurnCredentials(v1Res),\n () => {\n logger.warn('getting turn credentials failed');\n logger.warn('is mod_turncredentials or similar installed and configured?');\n }\n );\n });\n }\n\n /**\n * Parses response when querying for services using urn:xmpp:extdisco:1 or urn:xmpp:extdisco:2.\n * Stores results in jvbIceConfig and p2pIceConfig.\n * @param res The response iq.\n * @return {boolean} Whether something was processed from the supplied message.\n */\n onReceiveStunAndTurnCredentials(res) {\n const iceservers = [];\n\n $(res).find('>services>service').each((idx, el) => {\n // eslint-disable-next-line no-param-reassign\n el = $(el);\n const dict = {};\n const type = el.attr('type');\n\n switch (type) {\n case 'stun':\n dict.urls = `stun:${el.attr('host')}`;\n if (el.attr('port')) {\n dict.urls += `:${el.attr('port')}`;\n }\n iceservers.push(dict);\n break;\n case 'turn':\n case 'turns': {\n dict.urls = `${type}:`;\n dict.username = el.attr('username');\n dict.urls += el.attr('host');\n const port = el.attr('port');\n\n if (port) {\n dict.urls += `:${el.attr('port')}`;\n }\n const transport = el.attr('transport');\n\n if (transport && transport !== 'udp') {\n dict.urls += `?transport=${transport}`;\n }\n\n dict.credential = el.attr('password')\n || dict.credential;\n iceservers.push(dict);\n break;\n }\n }\n });\n\n const options = this.xmpp.options;\n\n // Shuffle ICEServers for loadbalancing\n for (let i = iceservers.length - 1; i > 0; i--) {\n const j = Math.floor(Math.random() * (i + 1));\n const temp = iceservers[i];\n\n iceservers[i] = iceservers[j];\n iceservers[j] = temp;\n }\n\n let filter;\n\n if (options.useTurnUdp) {\n filter = s => s.urls.startsWith('turn');\n } else {\n // By default we filter out STUN and TURN/UDP and leave only TURN/TCP.\n filter = s => s.urls.startsWith('turn') && (s.urls.indexOf('transport=tcp') >= 0);\n }\n\n this.jvbIceConfig.iceServers = iceservers.filter(filter);\n this.p2pIceConfig.iceServers = iceservers;\n\n return iceservers.length > 0;\n }\n\n /**\n * Returns the data saved in 'updateLog' in a format to be logged.\n */\n getLog() {\n const data = {};\n\n Object.keys(this.sessions).forEach(sid => {\n const session = this.sessions[sid];\n const pc = session.peerconnection;\n\n if (pc && pc.updateLog) {\n // FIXME: should probably be a .dump call\n data[`jingle_${sid}`] = {\n updateLog: pc.updateLog,\n stats: pc.stats,\n url: window.location.href\n };\n }\n });\n\n return data;\n }\n}\n\n/* eslint-enable newline-per-chained-call */\n","import { Strophe } from 'strophe.js';\n\nimport ConnectionPlugin from './ConnectionPlugin';\n\n/**\n * Logs raw stanzas and makes them available for download as JSON\n */\nclass StropheLogger extends ConnectionPlugin {\n /**\n *\n */\n constructor() {\n super();\n this.log = [];\n }\n\n /**\n *\n * @param connection\n */\n init(connection) {\n super.init(connection);\n this.connection.rawInput = this.logIncoming.bind(this);\n this.connection.rawOutput = this.logOutgoing.bind(this);\n }\n\n /**\n *\n * @param stanza\n */\n logIncoming(stanza) {\n this.log.push([ new Date().getTime(), 'incoming', stanza ]);\n }\n\n /**\n *\n * @param stanza\n */\n logOutgoing(stanza) {\n this.log.push([ new Date().getTime(), 'outgoing', stanza ]);\n }\n}\n\n/**\n *\n */\nexport default function() {\n Strophe.addConnectionPlugin('logger', new StropheLogger());\n}\n","/* global $ */\n\nimport { getLogger } from '@jitsi/logger';\nimport { $iq } from 'strophe.js';\n\nimport ConnectionPlugin from './ConnectionPlugin';\n\nconst logger = getLogger(__filename);\n\nconst RAYO_XMLNS = 'urn:xmpp:rayo:1';\n\n/**\n *\n */\nexport default class RayoConnectionPlugin extends ConnectionPlugin {\n /**\n *\n * @param connection\n */\n init(connection) {\n super.init(connection);\n\n this.connection.addHandler(\n this.onRayo.bind(this), RAYO_XMLNS, 'iq', 'set', null, null);\n }\n\n /**\n *\n * @param iq\n */\n onRayo(iq) {\n logger.info('Rayo IQ', iq);\n }\n\n /* eslint-disable max-params */\n\n /**\n *\n * @param to\n * @param from\n * @param roomName\n * @param roomPass\n * @param focusMucJid\n */\n dial(to, from, roomName, roomPass, focusMucJid) {\n return new Promise((resolve, reject) => {\n if (!focusMucJid) {\n reject(new Error('Internal error!'));\n\n return;\n }\n const req = $iq({\n type: 'set',\n to: focusMucJid\n });\n\n req.c('dial', {\n xmlns: RAYO_XMLNS,\n to,\n from\n });\n req.c('header', {\n name: 'JvbRoomName',\n value: roomName\n }).up();\n\n if (roomPass && roomPass.length) {\n req.c('header', {\n name: 'JvbRoomPassword',\n value: roomPass\n }).up();\n }\n\n this.connection.sendIQ(\n req,\n result => {\n logger.info('Dial result ', result);\n\n // eslint-disable-next-line newline-per-chained-call\n const resource = $(result).find('ref').attr('uri');\n\n this.callResource = resource.substr('xmpp:'.length);\n logger.info(`Received call resource: ${this.callResource}`);\n resolve();\n },\n error => {\n logger.info('Dial error ', error);\n reject(error);\n });\n });\n }\n\n /* eslint-enable max-params */\n\n /**\n *\n */\n hangup() {\n return new Promise((resolve, reject) => {\n if (!this.callResource) {\n reject(new Error('No call in progress'));\n logger.warn('No call in progress');\n\n return;\n }\n\n const req = $iq({\n type: 'set',\n to: this.callResource\n });\n\n req.c('hangup', {\n xmlns: RAYO_XMLNS\n });\n\n this.connection.sendIQ(req, result => {\n logger.info('Hangup result ', result);\n this.callResource = null;\n resolve();\n }, error => {\n logger.info('Hangup error ', error);\n this.callResource = null;\n reject(new Error('Hangup error '));\n });\n });\n }\n}\n","/**\n * Strophe logger implementation. Logs from level WARN and above.\n */\nimport { getLogger } from '@jitsi/logger';\nimport { Strophe } from 'strophe.js';\n\nimport GlobalOnErrorHandler from '../util/GlobalOnErrorHandler';\n\nconst logger = getLogger(__filename);\n\n/**\n * This is the last HTTP error status captured from Strophe debug logs.\n * The purpose of storing it is to distinguish between the network and\n * infrastructure reason for connection being dropped (see connectionHandler in\n * xmpp.js). The value will be cleared (-1) if the subsequent request succeeds\n * which means that the failure could be transient.\n *\n * FIXME in the latest Strophe (not released on npm) there is API to handle\n * particular HTTP errors, but there is no way to learn if the subsequent\n * request succeeded in order to tell if the error was one time incident or if\n * it was the reason for dropping the connection by Strophe (the connection is\n * dropped after 5 subsequent failures). Ideally Strophe should provide more\n * details about the reason on why the connection stopped.\n *\n * @type {number}\n */\nlet lastErrorStatus = -1;\n\n/**\n * A regular expression used to catch Strophe's log message indicating that the\n * last BOSH request was successful. When there is such message seen the\n * {@link lastErrorStatus} will be set back to '-1'.\n * @type {RegExp}\n */\nconst resetLastErrorStatusRegExpr = /request id \\d+.\\d+ got 200/;\n\n/**\n * A regular expression used to capture the current value of the BOSH request\n * error status (HTTP error code or '0' or something else).\n * @type {RegExp}\n */\nconst lastErrorStatusRegExpr\n = /request errored, status: (\\d+), number of errors: \\d+/;\n\n/**\n *\n */\nexport default function() {\n\n Strophe.log = function(level, msg) {\n // Our global handler reports uncaught errors to the stats which may\n // interpret those as partial call failure.\n // Strophe log entry about secondary request timeout does not mean that\n // it's a final failure(the request will be restarted), so we lower it's\n // level here to a warning.\n logger.trace('Strophe', level, msg);\n if (typeof msg === 'string'\n && msg.indexOf('Request ') !== -1\n && msg.indexOf('timed out (secondary), restarting') !== -1) {\n // eslint-disable-next-line no-param-reassign\n level = Strophe.LogLevel.WARN;\n }\n\n /* eslint-disable no-case-declarations */\n switch (level) {\n case Strophe.LogLevel.DEBUG:\n // The log message which reports successful status is logged on\n // Strophe's DEBUG level.\n if (lastErrorStatus !== -1\n && resetLastErrorStatusRegExpr.test(msg)) {\n logger.debug('Reset lastErrorStatus');\n lastErrorStatus = -1;\n }\n break;\n case Strophe.LogLevel.WARN:\n logger.warn(`Strophe: ${msg}`);\n const errStatusCapture = lastErrorStatusRegExpr.exec(msg);\n\n if (errStatusCapture && errStatusCapture.length === 2) {\n lastErrorStatus = parseInt(errStatusCapture[1], 10);\n logger.debug(`lastErrorStatus set to: ${lastErrorStatus}`);\n }\n break;\n case Strophe.LogLevel.ERROR:\n case Strophe.LogLevel.FATAL:\n // eslint-disable-next-line no-param-reassign\n msg = `Strophe: ${msg}`;\n GlobalOnErrorHandler.callErrorHandler(new Error(msg));\n logger.error(msg);\n break;\n }\n\n /* eslint-enable no-case-declarations */\n };\n\n /**\n * Returns error status (HTTP error code) of the last BOSH request.\n *\n * @return {number} HTTP error code, '0' for unknown or \"god knows what\"\n * (this is a hack).\n */\n Strophe.getLastErrorStatus = function() {\n return lastErrorStatus;\n };\n\n Strophe.getStatusString = function(status) {\n switch (status) {\n case Strophe.Status.BINDREQUIRED:\n return 'BINDREQUIRED';\n case Strophe.Status.ERROR:\n return 'ERROR';\n case Strophe.Status.CONNECTING:\n return 'CONNECTING';\n case Strophe.Status.CONNFAIL:\n return 'CONNFAIL';\n case Strophe.Status.AUTHENTICATING:\n return 'AUTHENTICATING';\n case Strophe.Status.AUTHFAIL:\n return 'AUTHFAIL';\n case Strophe.Status.CONNECTED:\n return 'CONNECTED';\n case Strophe.Status.DISCONNECTED:\n return 'DISCONNECTED';\n case Strophe.Status.DISCONNECTING:\n return 'DISCONNECTING';\n case Strophe.Status.ATTACHED:\n return 'ATTACHED';\n default:\n return 'unknown';\n }\n };\n}\n","/* global $ */\n\nimport { getLogger } from '@jitsi/logger';\nimport { $msg, Strophe } from 'strophe.js';\nimport 'strophejs-plugin-disco';\n\nimport * as JitsiConnectionErrors from '../../JitsiConnectionErrors';\nimport * as JitsiConnectionEvents from '../../JitsiConnectionEvents';\nimport XMPPEvents from '../../service/xmpp/XMPPEvents';\nimport browser from '../browser';\nimport { E2EEncryption } from '../e2ee/E2EEncryption';\nimport Statistics from '../statistics/statistics';\nimport GlobalOnErrorHandler from '../util/GlobalOnErrorHandler';\nimport Listenable from '../util/Listenable';\nimport RandomUtil from '../util/RandomUtil';\n\nimport Caps, { parseDiscoInfo } from './Caps';\nimport XmppConnection from './XmppConnection';\nimport MucConnectionPlugin from './strophe.emuc';\nimport JingleConnectionPlugin from './strophe.jingle';\nimport initStropheLogger from './strophe.logger';\nimport RayoConnectionPlugin from './strophe.rayo';\nimport initStropheUtil from './strophe.util';\n\nconst logger = getLogger(__filename);\n\n/**\n* Regex to extract exact error message on jwt error.\n*/\nconst FAILURE_REGEX = /(.*)<\\/text><\\/failure>/gi;\n\n/**\n * Creates XMPP connection.\n *\n * @param {Object} options\n * @param {string} [options.token] - JWT token used for authentication(JWT authentication module must be enabled in\n * Prosody).\n * @param {string} options.serviceUrl - The service URL for XMPP connection.\n * @param {string} options.shard - The shard where XMPP connection initially landed.\n * @param {string} options.enableWebsocketResume - True to enable stream resumption.\n * @param {number} [options.websocketKeepAlive] - See {@link XmppConnection} constructor.\n * @param {number} [options.websocketKeepAliveUrl] - See {@link XmppConnection} constructor.\n * @param {Object} [options.xmppPing] - See {@link XmppConnection} constructor.\n * @returns {XmppConnection}\n */\nfunction createConnection({\n enableWebsocketResume,\n serviceUrl = '/http-bind',\n shard,\n token,\n websocketKeepAlive,\n websocketKeepAliveUrl,\n xmppPing }) {\n\n // Append token as URL param\n if (token) {\n // eslint-disable-next-line no-param-reassign\n serviceUrl += `${serviceUrl.indexOf('?') === -1 ? '?' : '&'}token=${token}`;\n }\n\n return new XmppConnection({\n enableWebsocketResume,\n serviceUrl,\n websocketKeepAlive,\n websocketKeepAliveUrl,\n xmppPing,\n shard\n });\n}\n\n/**\n * Initializes Strophe plugins that need to work with Strophe.Connection directly rather than the lib-jitsi-meet's\n * {@link XmppConnection} wrapper.\n *\n * @returns {void}\n */\nfunction initStropheNativePlugins() {\n initStropheUtil();\n initStropheLogger();\n}\n\n// FIXME: remove once we have a default config template. -saghul\n/**\n * A list of ice servers to use by default for P2P.\n */\nexport const DEFAULT_STUN_SERVERS = [\n { urls: 'stun:meet-jit-si-turnrelay.jitsi.net:443' }\n];\n\n/**\n * The name of the field used to recognize a chat message as carrying a JSON\n * payload from another endpoint.\n * If the json-message of a chat message contains a valid JSON object, and\n * the JSON has this key, then it is a valid json-message to be sent.\n */\nexport const JITSI_MEET_MUC_TYPE = 'type';\n\n/**\n * The feature used by jigasi participants.\n * @type {string}\n */\nexport const FEATURE_JIGASI = 'http://jitsi.org/protocol/jigasi';\n\n/**\n * The feature used by the lib to mark support for e2ee. We use the feature by putting it in the presence\n * to avoid additional signaling (disco-info).\n * @type {string}\n */\nexport const FEATURE_E2EE = 'https://jitsi.org/meet/e2ee';\n\n/**\n *\n */\nexport default class XMPP extends Listenable {\n /**\n * FIXME describe all options\n * @param {Object} options\n * @param {String} options.serviceUrl - URL passed to the XMPP client which will be used to establish XMPP\n * connection with the server.\n * @param {String} options.bosh - Deprecated, use {@code serviceUrl}.\n * @param {boolean} options.enableWebsocketResume - Enables XEP-0198 stream management which will make the XMPP\n * module try to resume the session in case the Websocket connection breaks.\n * @param {number} [options.websocketKeepAlive] - The websocket keep alive interval. See {@link XmppConnection}\n * constructor for more details.\n * @param {number} [options.websocketKeepAliveUrl] - The websocket keep alive url. See {@link XmppConnection}\n * constructor for more details.\n * @param {Object} [options.xmppPing] - The xmpp ping settings.\n * @param {Array} options.p2pStunServers see {@link JingleConnectionPlugin} for more details.\n * @param token\n */\n constructor(options, token) {\n super();\n this.connection = null;\n this.disconnectInProgress = false;\n this.connectionTimes = {};\n this.options = options;\n this.token = token;\n this.authenticatedUser = false;\n\n initStropheNativePlugins();\n\n const xmppPing = options.xmppPing || {};\n\n // let's ping the main domain (in case a guest one is used for the connection)\n xmppPing.domain = options.hosts.domain;\n\n this.connection = createConnection({\n enableWebsocketResume: options.enableWebsocketResume,\n\n // FIXME remove deprecated bosh option at some point\n serviceUrl: options.serviceUrl || options.bosh,\n token,\n websocketKeepAlive: options.websocketKeepAlive,\n websocketKeepAliveUrl: options.websocketKeepAliveUrl,\n xmppPing,\n shard: options.deploymentInfo?.shard\n });\n\n // forwards the shard changed event\n this.connection.on(XmppConnection.Events.CONN_SHARD_CHANGED, () => {\n /* eslint-disable camelcase */\n const details = {\n shard_changed: true,\n suspend_time: this.connection.ping.getPingSuspendTime(),\n time_since_last_success: this.connection.getTimeSinceLastSuccess()\n };\n /* eslint-enable camelcase */\n\n this.eventEmitter.emit(\n JitsiConnectionEvents.CONNECTION_FAILED,\n JitsiConnectionErrors.OTHER_ERROR,\n undefined,\n undefined,\n details);\n });\n\n this._initStrophePlugins();\n\n this.caps = new Caps(this.connection, /* clientNode */ 'https://jitsi.org/jitsi-meet');\n\n // Initialize features advertised in disco-info\n this.initFeaturesList();\n\n // Setup a disconnect on unload as a way to facilitate API consumers. It\n // sounds like they would want that. A problem for them though may be if\n // they wanted to utilize the connected connection in an unload handler\n // of their own. However, it should be fairly easy for them to do that\n // by registering their unload handler before us.\n $(window).on(`${this.options.disableBeforeUnloadHandlers ? '' : 'beforeunload '}unload`, ev => {\n this.disconnect(ev).catch(() => {\n // ignore errors in order to not brake the unload.\n });\n });\n }\n\n /**\n * Initializes the list of feature advertised through the disco-info\n * mechanism.\n */\n initFeaturesList() {\n // http://xmpp.org/extensions/xep-0167.html#support\n // http://xmpp.org/extensions/xep-0176.html#support\n this.caps.addFeature('urn:xmpp:jingle:1');\n this.caps.addFeature('urn:xmpp:jingle:apps:rtp:1');\n this.caps.addFeature('urn:xmpp:jingle:transports:ice-udp:1');\n this.caps.addFeature('urn:xmpp:jingle:apps:dtls:0');\n this.caps.addFeature('urn:xmpp:jingle:transports:dtls-sctp:1');\n this.caps.addFeature('urn:xmpp:jingle:apps:rtp:audio');\n this.caps.addFeature('urn:xmpp:jingle:apps:rtp:video');\n this.caps.addFeature('http://jitsi.org/json-encoded-sources');\n\n if (!(this.options.disableRtx || !browser.supportsRTX())) {\n this.caps.addFeature('urn:ietf:rfc:4588');\n }\n if (this.options.enableOpusRed === true && browser.supportsAudioRed()) {\n this.caps.addFeature('http://jitsi.org/opus-red');\n }\n\n if (typeof this.options.enableRemb === 'undefined' || this.options.enableRemb) {\n this.caps.addFeature('http://jitsi.org/remb');\n }\n\n // Disable TCC on Firefox because of a known issue where BWE is halved on every renegotiation.\n if (!browser.isFirefox() && (typeof this.options.enableTcc === 'undefined' || this.options.enableTcc)) {\n this.caps.addFeature('http://jitsi.org/tcc');\n }\n\n // this is dealt with by SDP O/A so we don't need to announce this\n // XEP-0293\n // this.caps.addFeature('urn:xmpp:jingle:apps:rtp:rtcp-fb:0');\n // XEP-0294\n // this.caps.addFeature('urn:xmpp:jingle:apps:rtp:rtp-hdrext:0');\n\n this.caps.addFeature('urn:ietf:rfc:5761'); // rtcp-mux\n this.caps.addFeature('urn:ietf:rfc:5888'); // a=group, e.g. bundle\n\n // this.caps.addFeature('urn:ietf:rfc:5576'); // a=ssrc\n\n // Enable Lipsync ?\n if (browser.isChromiumBased() && this.options.enableLipSync === true) {\n logger.info('Lip-sync enabled !');\n this.caps.addFeature('http://jitsi.org/meet/lipsync');\n }\n\n if (this.connection.rayo) {\n this.caps.addFeature('urn:xmpp:rayo:client:1');\n }\n\n if (E2EEncryption.isSupported(this.options)) {\n this.caps.addFeature(FEATURE_E2EE, false, true);\n }\n }\n\n /**\n *\n */\n getConnection() {\n return this.connection;\n }\n\n /**\n * Receive connection status changes and handles them.\n *\n * @param {Object} credentials\n * @param {string} credentials.jid - The user's XMPP ID passed to the\n * connect method. For example, 'user@xmpp.com'.\n * @param {string} credentials.password - The password passed to the connect\n * method.\n * @param {string} status - One of Strophe's connection status strings.\n * @param {string} [msg] - The connection error message provided by Strophe.\n */\n connectionHandler(credentials = {}, status, msg) {\n const now = window.performance.now();\n const statusStr = Strophe.getStatusString(status).toLowerCase();\n\n this.connectionTimes[statusStr] = now;\n logger.log(\n `(TIME) Strophe ${statusStr}${msg ? `[${msg}]` : ''}:\\t`,\n now);\n\n this.eventEmitter.emit(XMPPEvents.CONNECTION_STATUS_CHANGED, credentials, status, msg);\n this._maybeSendDeploymentInfoStat();\n if (status === Strophe.Status.CONNECTED || status === Strophe.Status.ATTACHED) {\n // once connected or attached we no longer need this handle, drop it if it exist\n if (this._sysMessageHandler) {\n this.connection._stropheConn.deleteHandler(this._sysMessageHandler);\n this._sysMessageHandler = null;\n }\n\n this.sendDiscoInfo && this.connection.jingle.getStunAndTurnCredentials();\n\n logger.info(`My Jabber ID: ${this.connection.jid}`);\n\n // XmppConnection emits CONNECTED again on reconnect - a good opportunity to clear any \"last error\" flags\n this._resetState();\n\n // make sure we will send the info after the features request succeeds or fails\n this.sendDeploymentInfo = false;\n this.sendDiscoInfo && this.caps.getFeaturesAndIdentities(this.options.hosts.domain)\n .then(({ features, identities }) => {\n if (!features.has(Strophe.NS.PING)) {\n logger.error(`Ping NOT supported by ${\n this.options.hosts.domain} - please enable ping in your XMPP server config`);\n }\n\n this._processDiscoInfoIdentities(\n identities, undefined /* when querying we will query for features */);\n })\n .catch(error => {\n const errmsg = 'Feature discovery error';\n\n GlobalOnErrorHandler.callErrorHandler(\n new Error(`${errmsg}: ${error}`));\n logger.error(errmsg, error);\n\n this._maybeSendDeploymentInfoStat(true);\n });\n\n // make sure we don't query again\n this.sendDiscoInfo = false;\n\n if (credentials.password) {\n this.authenticatedUser = true;\n }\n if (this.connection && this.connection.connected\n && Strophe.getResourceFromJid(this.connection.jid)) {\n // .connected is true while connecting?\n // this.connection.send($pres());\n this.eventEmitter.emit(\n JitsiConnectionEvents.CONNECTION_ESTABLISHED,\n Strophe.getResourceFromJid(this.connection.jid));\n }\n } else if (status === Strophe.Status.CONNFAIL) {\n if (msg === 'x-strophe-bad-non-anon-jid') {\n this.anonymousConnectionFailed = true;\n } else {\n this.connectionFailed = true;\n }\n this.lastErrorMsg = msg;\n if (msg === 'giving-up') {\n this.eventEmitter.emit(\n JitsiConnectionEvents.CONNECTION_FAILED,\n JitsiConnectionErrors.OTHER_ERROR, msg);\n }\n } else if (status === Strophe.Status.ERROR) {\n this.lastErrorMsg = msg;\n } else if (status === Strophe.Status.DISCONNECTED) {\n // Stop ping interval\n this.connection.ping.stopInterval();\n const wasIntentionalDisconnect = Boolean(this.disconnectInProgress);\n const errMsg = msg || this.lastErrorMsg;\n\n if (this.anonymousConnectionFailed) {\n // prompt user for username and password\n this.eventEmitter.emit(\n JitsiConnectionEvents.CONNECTION_FAILED,\n JitsiConnectionErrors.PASSWORD_REQUIRED);\n } else if (this.connectionFailed) {\n this.eventEmitter.emit(\n JitsiConnectionEvents.CONNECTION_FAILED,\n JitsiConnectionErrors.OTHER_ERROR,\n errMsg,\n undefined, /* credentials */\n this._getConnectionFailedReasonDetails());\n } else if (wasIntentionalDisconnect) {\n this.eventEmitter.emit(\n JitsiConnectionEvents.CONNECTION_DISCONNECTED, errMsg);\n } else {\n // XXX if Strophe drops the connection while not being asked to,\n // it means that most likely some serious error has occurred.\n // One currently known case is when a BOSH request fails for\n // more than 4 times. The connection is dropped without\n // supplying a reason(error message/event) through the API.\n logger.error('XMPP connection dropped!');\n\n // XXX if the last request error is within 5xx range it means it\n // was a server failure\n const lastErrorStatus = Strophe.getLastErrorStatus();\n\n if (lastErrorStatus >= 500 && lastErrorStatus < 600) {\n this.eventEmitter.emit(\n JitsiConnectionEvents.CONNECTION_FAILED,\n JitsiConnectionErrors.SERVER_ERROR,\n errMsg || 'server-error',\n /* credentials */ undefined,\n this._getConnectionFailedReasonDetails());\n } else {\n this.eventEmitter.emit(\n JitsiConnectionEvents.CONNECTION_FAILED,\n JitsiConnectionErrors.CONNECTION_DROPPED_ERROR,\n errMsg || 'connection-dropped-error',\n /* credentials */ undefined,\n this._getConnectionFailedReasonDetails());\n }\n }\n } else if (status === Strophe.Status.AUTHFAIL) {\n const lastFailedRawMessage = this.getConnection().getLastFailedMessage();\n\n // wrong password or username, prompt user\n this.eventEmitter.emit(\n JitsiConnectionEvents.CONNECTION_FAILED,\n JitsiConnectionErrors.PASSWORD_REQUIRED,\n msg || this._parseConnectionFailedMessage(lastFailedRawMessage),\n credentials);\n }\n }\n\n /**\n * Process received identities.\n * @param {Set} identities The identities to process.\n * @param {Set} features The features to process, optional. If missing lobby component will be queried\n * for more features.\n * @private\n */\n _processDiscoInfoIdentities(identities, features) {\n // check for speakerstats\n identities.forEach(identity => {\n if (identity.type === 'av_moderation') {\n this.avModerationComponentAddress = identity.name;\n }\n\n if (identity.type === 'speakerstats') {\n this.speakerStatsComponentAddress = identity.name;\n }\n\n if (identity.type === 'conference_duration') {\n this.conferenceDurationComponentAddress = identity.name;\n }\n\n if (identity.type === 'lobbyrooms') {\n this.lobbySupported = true;\n const processLobbyFeatures = f => {\n f.forEach(fr => {\n if (fr.endsWith('#displayname_required')) {\n this.eventEmitter.emit(JitsiConnectionEvents.DISPLAY_NAME_REQUIRED);\n }\n });\n };\n\n if (features) {\n processLobbyFeatures(features);\n } else {\n identity.name && this.caps.getFeaturesAndIdentities(identity.name, identity.type)\n .then(({ features: f }) => processLobbyFeatures(f))\n .catch(e => logger.warn('Error getting features from lobby.', e && e.message));\n }\n }\n\n if (identity.type === 'shard') {\n this.options.deploymentInfo.shard = this.connection.shard = identity.name;\n }\n\n if (identity.type === 'region') {\n this.options.deploymentInfo.region = this.connection.region = identity.name;\n }\n\n if (identity.type === 'breakout_rooms') {\n this.breakoutRoomsComponentAddress = identity.name;\n }\n });\n\n this._maybeSendDeploymentInfoStat(true);\n\n if (this.avModerationComponentAddress\n || this.speakerStatsComponentAddress\n || this.conferenceDurationComponentAddress\n || this.breakoutRoomsComponentAddress) {\n this.connection.addHandler(this._onPrivateMessage.bind(this), null, 'message', null, null);\n }\n }\n\n /**\n * Parses a raw failure xmpp xml message received on auth failed.\n *\n * @param {string} msg - The raw failure message from xmpp.\n * @returns {string|null} - The parsed message from the raw xmpp message.\n */\n _parseConnectionFailedMessage(msg) {\n if (!msg) {\n return null;\n }\n\n const matches = FAILURE_REGEX.exec(msg);\n\n return matches ? matches[1] : null;\n }\n\n /**\n *\n * @param jid\n * @param password\n */\n _connect(jid, password) {\n // connection.connect() starts the connection process.\n //\n // As the connection process proceeds, the user supplied callback will\n // be triggered multiple times with status updates. The callback should\n // take two arguments - the status code and the error condition.\n //\n // The status code will be one of the values in the Strophe.Status\n // constants. The error condition will be one of the conditions defined\n // in RFC 3920 or the condition ‘strophe-parsererror’.\n //\n // The Parameters wait, hold and route are optional and only relevant\n // for BOSH connections. Please see XEP 124 for a more detailed\n // explanation of the optional parameters.\n //\n // Connection status constants for use by the connection handler\n // callback.\n //\n // Status.ERROR - An error has occurred (websockets specific)\n // Status.CONNECTING - The connection is currently being made\n // Status.CONNFAIL - The connection attempt failed\n // Status.AUTHENTICATING - The connection is authenticating\n // Status.AUTHFAIL - The authentication attempt failed\n // Status.CONNECTED - The connection has succeeded\n // Status.DISCONNECTED - The connection has been terminated\n // Status.DISCONNECTING - The connection is currently being terminated\n // Status.ATTACHED - The connection has been attached\n\n this._resetState();\n\n // we want to send this only on the initial connect\n this.sendDiscoInfo = true;\n this.sendDeploymentInfo = true;\n\n if (this.connection._stropheConn && this.connection._stropheConn._addSysHandler) {\n this._sysMessageHandler = this.connection._stropheConn._addSysHandler(\n this._onSystemMessage.bind(this),\n null,\n 'message'\n );\n } else {\n logger.warn('Cannot attach strophe system handler, jiconop cannot operate');\n }\n\n this.connection.connect(\n jid,\n password,\n this.connectionHandler.bind(this, {\n jid,\n password\n }));\n }\n\n /**\n * Receives system messages during the connect/login process and checks for services or\n * @param msg The received message.\n * @returns {void}\n * @private\n */\n _onSystemMessage(msg) {\n // proceed only if the message has any of the expected information\n if ($(msg).find('>services').length === 0 && $(msg).find('>query').length === 0) {\n return;\n }\n\n this.sendDiscoInfo = false;\n\n const foundIceServers = this.connection.jingle.onReceiveStunAndTurnCredentials(msg);\n\n const { features, identities } = parseDiscoInfo(msg);\n\n this._processDiscoInfoIdentities(identities, features);\n\n if (foundIceServers || identities.size > 0 || features.size > 0) {\n this.connection._stropheConn.deleteHandler(this._sysMessageHandler);\n this._sysMessageHandler = null;\n }\n }\n\n /**\n * Attach to existing connection. Can be used for optimizations. For\n * example: if the connection is created on the server we can attach to it\n * and start using it.\n *\n * @param options {object} connecting options - rid, sid, jid and password.\n */\n attach(options) {\n this._resetState();\n\n // we want to send this only on the initial connect\n this.sendDiscoInfo = true;\n\n const now = this.connectionTimes.attaching = window.performance.now();\n\n logger.log('(TIME) Strophe Attaching:\\t', now);\n this.connection.attach(options.jid, options.sid,\n parseInt(options.rid, 10) + 1,\n this.connectionHandler.bind(this, {\n jid: options.jid,\n password: options.password\n }));\n }\n\n /**\n * Resets any state/flag before starting a new connection.\n * @private\n */\n _resetState() {\n this.anonymousConnectionFailed = false;\n this.connectionFailed = false;\n this.lastErrorMsg = undefined;\n this.disconnectInProgress = undefined;\n }\n\n /**\n *\n * @param jid\n * @param password\n */\n connect(jid, password) {\n if (!jid) {\n const { anonymousdomain, domain } = this.options.hosts;\n let configDomain = anonymousdomain || domain;\n\n // Force authenticated domain if room is appended with '?login=true'\n // or if we're joining with the token\n\n // FIXME Do not rely on window.location because (1) React Native\n // does not have a window.location by default and (2) here we cannot\n // know for sure that query/search has not be stripped from\n // window.location by the time the following executes.\n const { location } = window;\n\n if (anonymousdomain) {\n const search = location && location.search;\n\n if ((search && search.indexOf('login=true') !== -1)\n || this.token) {\n configDomain = domain;\n }\n }\n\n // eslint-disable-next-line no-param-reassign\n jid = configDomain || (location && location.hostname);\n }\n\n return this._connect(jid, password);\n }\n\n /**\n * Joins or creates a muc with the provided jid, created from the passed\n * in room name and muc host and onCreateResource result.\n *\n * @param {string} roomName - The name of the muc to join.\n * @param {Object} options - Configuration for how to join the muc.\n * @param {Function} [onCreateResource] - Callback to invoke when a resource\n * is to be added to the jid.\n * @returns {Promise} Resolves with an instance of a strophe muc.\n */\n createRoom(roomName, options, onCreateResource) {\n // Support passing the domain in a String object as part of the room name.\n const domain = roomName.domain || options.customDomain;\n\n // There are cases (when using subdomain) where muc can hold an uppercase part\n let roomjid = `${this.getRoomJid(roomName, domain)}/`;\n const mucNickname = onCreateResource\n ? onCreateResource(this.connection.jid, this.authenticatedUser)\n : RandomUtil.randomHexString(8).toLowerCase();\n\n logger.info(`JID ${this.connection.jid} using MUC nickname ${mucNickname}`);\n roomjid += mucNickname;\n\n return this.connection.emuc.createRoom(roomjid, null, options);\n }\n\n /**\n * Returns the room JID based on the passed room name and domain.\n *\n * @param {string} roomName - The room name.\n * @param {string} domain - The domain.\n * @returns {string} - The room JID.\n */\n getRoomJid(roomName, domain) {\n return `${roomName}@${domain ? domain : this.options.hosts.muc.toLowerCase()}`;\n }\n\n /**\n * Check if a room with the passed JID is already created.\n *\n * @param {string} roomJid - The JID of the room.\n * @returns {boolean}\n */\n isRoomCreated(roomName, domain) {\n return this.connection.emuc.isRoomCreated(this.getRoomJid(roomName, domain));\n }\n\n /**\n * Returns the jid of the participant associated with the Strophe connection.\n *\n * @returns {string} The jid of the participant.\n */\n getJid() {\n return this.connection.jid;\n }\n\n /**\n * Returns the logs from strophe.jingle.\n * @returns {Object}\n */\n getJingleLog() {\n const jingle = this.connection.jingle;\n\n\n return jingle ? jingle.getLog() : {};\n }\n\n /**\n * Returns the logs from strophe.\n */\n getXmppLog() {\n return (this.connection.logger || {}).log || null;\n }\n\n /**\n *\n */\n dial(...args) {\n this.connection.rayo.dial(...args);\n }\n\n /**\n * Pings the server.\n * @param timeout how many ms before a timeout should occur.\n * @returns {Promise} resolved on ping success and reject on an error or\n * a timeout.\n */\n ping(timeout) {\n return new Promise((resolve, reject) => {\n this.connection.ping.ping(this.connection.pingDomain, resolve, reject, timeout);\n });\n }\n\n /**\n *\n */\n getSessions() {\n return this.connection.jingle.sessions;\n }\n\n /**\n * Disconnects this from the XMPP server (if this is connected).\n *\n * @param {Object} ev - Optionally, the event which triggered the necessity to\n * disconnect from the XMPP server (e.g. beforeunload, unload).\n * @returns {Promise} - Resolves when the disconnect process is finished or rejects with an error.\n */\n disconnect(ev) {\n if (this.disconnectInProgress) {\n return this.disconnectInProgress;\n } else if (!this.connection) {\n return Promise.resolve();\n }\n\n this.disconnectInProgress = new Promise(resolve => {\n const disconnectListener = (credentials, status) => {\n if (status === Strophe.Status.DISCONNECTED) {\n resolve();\n this.eventEmitter.removeListener(XMPPEvents.CONNECTION_STATUS_CHANGED, disconnectListener);\n }\n };\n\n this.eventEmitter.on(XMPPEvents.CONNECTION_STATUS_CHANGED, disconnectListener);\n });\n\n this._cleanupXmppConnection(ev);\n\n return this.disconnectInProgress;\n }\n\n /**\n * The method is supposed to gracefully close the XMPP connection and the main goal is to make sure that the current\n * participant will be removed from the conference XMPP MUC, so that it doesn't leave a \"ghost\" participant behind.\n *\n * @param {Object} ev - Optionally, the event which triggered the necessity to disconnect from the XMPP server\n * (e.g. beforeunload, unload).\n * @private\n * @returns {void}\n */\n _cleanupXmppConnection(ev) {\n // XXX Strophe is asynchronously sending by default. Unfortunately, that means that there may not be enough time\n // to send an unavailable presence or disconnect at all. Switching Strophe to synchronous sending is not much of\n // an option because it may lead to a noticeable delay in navigating away from the current location. As\n // a compromise, we will try to increase the chances of sending an unavailable presence and/or disconnecting\n // within the short time span that we have upon unloading by invoking flush() on the connection. We flush() once\n // before disconnect() in order to attempt to have its unavailable presence at the top of the send queue. We\n // flush() once more after disconnect() in order to attempt to have its unavailable presence sent as soon as\n // possible.\n !this.connection.isUsingWebSocket && this.connection.flush();\n\n if (!this.connection.isUsingWebSocket && ev !== null && typeof ev !== 'undefined') {\n const evType = ev.type;\n\n if (evType === 'beforeunload' || evType === 'unload') {\n // XXX Whatever we said above, synchronous sending is the best (known) way to properly disconnect from\n // the XMPP server. Consequently, it may be fine to have the source code and comment it in or out\n // depending on whether we want to run with it for some time.\n this.connection.options.sync = true;\n\n // This is needed in some browsers where sync xhr sending is disabled by default on unload.\n if (this.connection.sendUnavailableBeacon()) {\n\n return;\n }\n }\n }\n\n this.connection.disconnect();\n\n if (this.connection.options.sync !== true) {\n this.connection.flush();\n }\n }\n\n /**\n *\n */\n _initStrophePlugins() {\n const iceConfig = {\n jvb: { iceServers: [ ] },\n p2p: { iceServers: [ ] }\n };\n\n const p2pStunServers = (this.options.p2p\n && this.options.p2p.stunServers) || DEFAULT_STUN_SERVERS;\n\n if (Array.isArray(p2pStunServers)) {\n logger.info('P2P STUN servers: ', p2pStunServers);\n iceConfig.p2p.iceServers = p2pStunServers;\n }\n\n if (this.options.p2p && this.options.p2p.iceTransportPolicy) {\n logger.info('P2P ICE transport policy: ',\n this.options.p2p.iceTransportPolicy);\n\n iceConfig.p2p.iceTransportPolicy\n = this.options.p2p.iceTransportPolicy;\n }\n\n this.connection.addConnectionPlugin('emuc', new MucConnectionPlugin(this));\n this.connection.addConnectionPlugin('jingle', new JingleConnectionPlugin(this, this.eventEmitter, iceConfig));\n this.connection.addConnectionPlugin('rayo', new RayoConnectionPlugin());\n }\n\n /**\n * Returns details about connection failure. Shard change or is it after\n * suspend.\n * @returns {object} contains details about a connection failure.\n * @private\n */\n _getConnectionFailedReasonDetails() {\n const details = {};\n\n // check for moving between shard if information is available\n if (this.options.deploymentInfo\n && this.options.deploymentInfo.shard\n && this.connection.lastResponseHeaders) {\n\n // split headers by line\n const headersArr = this.connection.lastResponseHeaders\n .trim().split(/[\\r\\n]+/);\n const headers = {};\n\n headersArr.forEach(line => {\n const parts = line.split(': ');\n const header = parts.shift();\n const value = parts.join(': ');\n\n headers[header] = value;\n });\n\n /* eslint-disable camelcase */\n details.shard_changed\n = this.options.deploymentInfo.shard\n !== headers['x-jitsi-shard'];\n /* eslint-enable camelcase */\n }\n\n /* eslint-disable camelcase */\n // check for possible suspend\n details.suspend_time = this.connection.ping.getPingSuspendTime();\n details.time_since_last_success = this.connection.getTimeSinceLastSuccess();\n /* eslint-enable camelcase */\n\n return details;\n }\n\n /**\n * Notifies speaker stats component if available that we are the new\n * dominant speaker in the conference.\n * @param {String} roomJid - The room jid where the speaker event occurred.\n */\n sendDominantSpeakerEvent(roomJid) {\n // no speaker stats component advertised\n if (!this.speakerStatsComponentAddress || !roomJid) {\n return;\n }\n\n const msg = $msg({ to: this.speakerStatsComponentAddress });\n\n msg.c('speakerstats', {\n xmlns: 'http://jitsi.org/jitmeet',\n room: roomJid })\n .up();\n\n this.connection.send(msg);\n }\n\n /**\n * Sends facial expression to speaker stats component.\n * @param {String} roomJid - The room jid where the speaker event occurred.\n * @param {Object} payload - The expression to be sent to the speaker stats.\n */\n sendFacialExpressionEvent(roomJid, payload) {\n // no speaker stats component advertised\n if (!this.speakerStatsComponentAddress || !roomJid) {\n return;\n }\n\n const msg = $msg({ to: this.speakerStatsComponentAddress });\n\n msg.c('facialExpression', {\n xmlns: 'http://jitsi.org/jitmeet',\n room: roomJid,\n expression: payload.facialExpression,\n duration: payload.duration\n }).up();\n\n this.connection.send(msg);\n }\n\n /**\n * Check if the given argument is a valid JSON ENDPOINT_MESSAGE string by\n * parsing it and checking if it has a field called 'type'.\n *\n * @param {string} jsonString check if this string is a valid json string\n * and contains the special structure.\n * @returns {boolean, object} if given object is a valid JSON string, return\n * the json object. Otherwise, returns false.\n */\n tryParseJSONAndVerify(jsonString) {\n // ignore empty strings, like message errors\n if (!jsonString) {\n return false;\n }\n\n try {\n const json = JSON.parse(jsonString);\n\n // Handle non-exception-throwing cases:\n // Neither JSON.parse(false) or JSON.parse(1234) throw errors,\n // hence the type-checking,\n // but... JSON.parse(null) returns null, and\n // typeof null === \"object\",\n // so we must check for that, too.\n // Thankfully, null is falsey, so this suffices:\n if (json && typeof json === 'object') {\n const type = json[JITSI_MEET_MUC_TYPE];\n\n if (typeof type !== 'undefined') {\n return json;\n }\n\n logger.debug('parsing valid json but does not have correct '\n + 'structure', 'topic: ', type);\n }\n } catch (e) {\n logger.error(`Error parsing json ${jsonString}`, e);\n\n return false;\n }\n\n return false;\n }\n\n /**\n * A private message is received, message that is not addressed to the muc.\n * We expect private message coming from plugins component if it is\n * enabled and running.\n *\n * @param {string} msg - The message.\n */\n _onPrivateMessage(msg) {\n const from = msg.getAttribute('from');\n\n if (!(from === this.speakerStatsComponentAddress\n || from === this.conferenceDurationComponentAddress\n || from === this.avModerationComponentAddress\n || from === this.breakoutRoomsComponentAddress)) {\n return true;\n }\n\n const jsonMessage = $(msg).find('>json-message')\n .text();\n const parsedJson = this.tryParseJSONAndVerify(jsonMessage);\n\n if (!parsedJson) {\n return true;\n }\n\n if (parsedJson[JITSI_MEET_MUC_TYPE] === 'speakerstats' && parsedJson.users) {\n this.eventEmitter.emit(XMPPEvents.SPEAKER_STATS_RECEIVED, parsedJson.users);\n } else if (parsedJson[JITSI_MEET_MUC_TYPE] === 'conference_duration' && parsedJson.created_timestamp) {\n this.eventEmitter.emit(XMPPEvents.CONFERENCE_TIMESTAMP_RECEIVED, parsedJson.created_timestamp);\n } else if (parsedJson[JITSI_MEET_MUC_TYPE] === 'av_moderation') {\n this.eventEmitter.emit(XMPPEvents.AV_MODERATION_RECEIVED, parsedJson);\n } else if (parsedJson[JITSI_MEET_MUC_TYPE] === 'breakout_rooms') {\n this.eventEmitter.emit(XMPPEvents.BREAKOUT_ROOMS_EVENT, parsedJson);\n }\n\n return true;\n }\n\n /**\n * Sends deployment info to stats if not sent already.\n * We want to try sending it on failure to connect\n * or when we get a sys message(from jiconop2)\n * or after success or failure of disco-info\n * @param force Whether to force sending without checking anything.\n * @private\n */\n _maybeSendDeploymentInfoStat(force) {\n const acceptedStatuses = [\n Strophe.Status.ERROR,\n Strophe.Status.CONNFAIL,\n Strophe.Status.AUTHFAIL,\n Strophe.Status.DISCONNECTED,\n Strophe.Status.CONNTIMEOUT\n ];\n\n if (!force && !(acceptedStatuses.includes(this.connection.status) && this.sendDeploymentInfo)) {\n return;\n }\n\n // Log deployment-specific information, if available. Defined outside\n // the application by individual deployments\n const aprops = this.options.deploymentInfo;\n\n if (aprops && Object.keys(aprops).length > 0) {\n const logObject = {};\n\n logObject.id = 'deployment_info';\n for (const attr in aprops) {\n if (aprops.hasOwnProperty(attr)) {\n logObject[attr] = aprops[attr];\n }\n }\n\n Statistics.sendLog(JSON.stringify(logObject));\n }\n\n this.sendDeploymentInfo = false;\n }\n}\n","import {\n CONNECTION_DISCONNECTED,\n CONNECTION_ESTABLISHED,\n CONNECTION_FAILED\n} from './JitsiConnectionEvents';\nimport XMPP from './modules/xmpp/xmpp';\n\n/**\n * @typedef {Object} UpgradeRoleError\n *\n * @property {JitsiConnectionErrors} [connectionError] - One of\n * {@link JitsiConnectionErrors} which occurred when trying to connect to the\n * XMPP server.\n * @property {String} [authenticationError] - One of XMPP error conditions\n * returned by Jicofo on authentication attempt. See\n * {@link https://xmpp.org/rfcs/rfc3920.html#streams-error}.\n * @property {String} [message] - More details about the error.\n * @property {Object} [credentials] - The credentials that failed the\n * authentication.\n * @property {String} [credentials.jid] - The XMPP ID part of the credentials\n * that failed the authentication.\n * @property {string} [credentials.password] - The password part of the\n * credentials that failed the authentication.\n *\n * NOTE If neither one of the errors is present, then the operation has been\n * canceled.\n */\n\n/* eslint-disable no-invalid-this */\n\n/**\n * Connects to the XMPP server using the specified credentials and contacts\n * Jicofo in order to obtain a session ID (which is then stored in the local\n * storage). The user's role of the parent conference will be upgraded to\n * moderator (by Jicofo). It's also used to join the conference when starting\n * from anonymous domain and only authenticated users are allowed to create new\n * rooms.\n *\n * @param {Object} options\n * @param {string} options.id - XMPP user's ID to log in. For example,\n * user@xmpp-server.com.\n * @param {string} options.password - XMPP user's password to log in with.\n * @param {string} [options.roomPassword] - The password to join the MUC with.\n * @param {Function} [options.onLoginSuccessful] - Callback called when logging\n * into the XMPP server was successful. The next step will be to obtain a new\n * session ID from Jicofo and join the MUC using it which will effectively\n * upgrade the user's role to moderator.\n * @returns {Object} A thenable which (1) settles when the process of\n * authenticating and upgrading the role of the specified XMPP user finishes and\n * (2) has a cancel method that allows the caller to interrupt the\n * process. If the process finishes successfully, the session ID has been stored\n * in the settings and the thenable is resolved. If the process\n * finishes with failure, the thenable is rejected with reason of type\n * {@link UpgradeRoleError} which will have either connectionError or\n * authenticationError property set depending on which of the steps has\n * failed. If cancel is called before the process finishes, then the\n * thenable will be rejected with an empty object (i.e. no error property will\n * be set on the rejection reason).\n */\nexport default function authenticateAndUpgradeRole({\n // 1. Log the specified XMPP user in.\n id,\n password,\n onCreateResource,\n\n // 2. Let the API client/consumer know as soon as the XMPP user has been\n // successfully logged in.\n onLoginSuccessful,\n\n // 3. Join the MUC.\n roomPassword\n}) {\n let canceled = false;\n let rejectPromise;\n let xmpp = new XMPP(this.connection.options);\n\n const process = new Promise((resolve, reject) => {\n // The process is represented by a Thenable with a cancel method. The\n // Thenable is implemented using Promise and the cancel using the\n // Promise's reject function.\n rejectPromise = reject;\n\n\n xmpp.addListener(\n CONNECTION_DISCONNECTED,\n () => {\n xmpp = undefined;\n });\n xmpp.addListener(\n CONNECTION_ESTABLISHED,\n () => {\n if (canceled) {\n return;\n }\n\n // Let the caller know that the XMPP login was successful.\n onLoginSuccessful && onLoginSuccessful();\n\n // Now authenticate with Jicofo and get a new session ID.\n const room = xmpp.createRoom(\n this.options.name,\n this.options.config,\n onCreateResource\n );\n\n room.moderator.authenticate()\n .then(() => {\n xmpp && xmpp.disconnect();\n\n if (canceled) {\n return;\n }\n\n // At this point we should have the new session ID\n // stored in the settings. Jicofo will allow to join the\n // room.\n this.join(roomPassword);\n\n resolve();\n })\n .catch(({ error, message }) => {\n xmpp.disconnect();\n\n reject({\n authenticationError: error,\n message\n });\n });\n });\n xmpp.addListener(\n CONNECTION_FAILED,\n (connectionError, message, credentials) => {\n reject({\n connectionError,\n credentials,\n message\n });\n xmpp = undefined;\n });\n\n canceled || xmpp.connect(id, password);\n });\n\n /**\n * Cancels the process, if it's in progress, of authenticating and upgrading\n * the role of the local participant/user.\n *\n * @public\n * @returns {void}\n */\n process.cancel = () => {\n canceled = true;\n rejectPromise({});\n xmpp && xmpp.disconnect();\n };\n\n return process;\n}\n\n/* eslint-enable no-invalid-this */\n","\nimport { getLogger } from '@jitsi/logger';\n\nimport * as JitsiConferenceEvents from '../../JitsiConferenceEvents';\nimport CodecMimeType from '../../service/RTC/CodecMimeType';\nimport * as MediaType from '../../service/RTC/MediaType';\nimport browser from '../browser';\n\nconst logger = getLogger(__filename);\n\n/**\n * This class handles the codec selection mechanism for the conference based on the config.js settings.\n * The preferred codec is selected based on the settings and the list of codecs supported by the browser.\n * The preferred codec is published in presence which is then used by the other endpoints in the\n * conference to pick a supported codec at join time and when the call transitions between p2p and jvb\n * connections.\n */\nexport class CodecSelection {\n /**\n * Creates a new instance for a given conference.\n *\n * @param {JitsiConference} conference the conference instance\n * @param {*} options\n * @param {string} options.disabledCodec the codec that needs to be disabled.\n * @param {boolean} options.enforcePreferredCodec whether codec preference has to be\n * enforced even when an endpoints that doesn't support the preferred codec joins the call.\n * Falling back to the standard codec will be skipped when this option is true, endpoints\n * that do not support the preferred codec may not be able to encode/decode video when this happens.\n * @param {string} options.jvbCodec the codec that is preferred on jvb connection.\n * @param {string} options.p2pCodec the codec that is preferred on p2p connection.\n */\n constructor(conference, options) {\n this.conference = conference;\n this.options = options;\n\n // VP8 cannot be disabled and it will be the default codec when no preference is set.\n this.disabledCodec = options.disabledCodec === CodecMimeType.VP8\n ? undefined\n : this._getCodecMimeType(options.disabledCodec);\n\n // Check if the codec values passed are valid.\n const jvbCodec = this._getCodecMimeType(options.jvbCodec);\n const p2pCodec = this._getCodecMimeType(options.p2pCodec);\n\n this.jvbPreferredCodec = jvbCodec && this._isCodecSupported(jvbCodec) ? jvbCodec : CodecMimeType.VP8;\n this.p2pPreferredCodec = p2pCodec && this._isCodecSupported(p2pCodec) ? p2pCodec : CodecMimeType.VP8;\n logger.debug(`Codec preferences for the conference are JVB: ${this.jvbPreferredCodec},\n P2P: ${this.p2pPreferredCodec}`);\n\n if (this.jvbPreferredCodec === CodecMimeType.VP9 && !browser.supportsVP9()) {\n this.jvbPreferredCodec = CodecMimeType.VP8;\n }\n\n this.conference.on(\n JitsiConferenceEvents.USER_JOINED,\n () => this._selectPreferredCodec());\n this.conference.on(\n JitsiConferenceEvents.USER_LEFT,\n () => this._selectPreferredCodec());\n this.conference.on(\n JitsiConferenceEvents._MEDIA_SESSION_STARTED,\n session => this._onMediaSessionStarted(session));\n }\n\n /**\n * Checks if a given string is a valid video codec mime type.\n *\n * @param {string} codec the codec string that needs to be validated.\n * @returns {CodecMimeType|null} mime type if valid, null otherwise.\n * @private\n */\n _getCodecMimeType(codec) {\n if (typeof codec === 'string') {\n return Object.values(CodecMimeType).find(value => value === codec.toLowerCase());\n }\n\n return null;\n }\n\n /**\n * Checks if the given codec is supported by the browser.\n *\n * @param {CodecMimeType} preferredCodec codec to be checked.\n * @returns {boolean} true if the given codec is supported, false otherwise.\n * @private\n */\n _isCodecSupported(preferredCodec) {\n // Skip the check on FF and RN because they do not support the getCapabilities API.\n // It is safe to assume both of them support all the codecs supported by Chrome.\n if (browser.isFirefox() || browser.isReactNative()) {\n return true;\n }\n\n return window.RTCRtpReceiver\n && window.RTCRtpReceiver.getCapabilities\n && window.RTCRtpReceiver.getCapabilities('video').codecs\n .some(codec => codec.mimeType.toLowerCase() === `video/${preferredCodec}`);\n }\n\n /**\n * Handles the {@link JitsiConferenceEvents._MEDIA_SESSION_STARTED} event. Codecs need to be\n * configured on the media session that is newly created.\n *\n * @param {JingleSessionPC} mediaSession media session that started.\n * @returns {void}\n * @private\n */\n _onMediaSessionStarted(mediaSession) {\n const preferredCodec = mediaSession.isP2P ? this.p2pPreferredCodec : this.jvbPreferredCodec;\n const disabledCodec = this.disabledCodec && this._isCodecSupported(this.disabledCodec)\n ? this.disabledCodec\n : null;\n\n this._selectPreferredCodec(mediaSession, preferredCodec, disabledCodec);\n }\n\n /**\n * Sets the codec on the media session based on the preferred codec setting and the supported codecs\n * published by the remote participants in their presence.\n *\n * @param {JingleSessionPC} mediaSession session for which the codec selection has to be made.\n * @param {CodecMimeType} preferredCodec preferred codec.\n * @param {CodecMimeType} disabledCodec codec that needs to be disabled.\n */\n _selectPreferredCodec(mediaSession = null, preferredCodec = null, disabledCodec = null) {\n const session = mediaSession ? mediaSession : this.conference.jvbJingleSession;\n const currentCodec = preferredCodec ? preferredCodec : this.jvbPreferredCodec;\n let selectedCodec = currentCodec;\n\n if (session && !session.isP2P && !this.options.enforcePreferredCodec) {\n const remoteParticipants = this.conference.getParticipants().map(participant => participant.getId());\n\n for (const remote of remoteParticipants) {\n const peerMediaInfo = session._signalingLayer.getPeerMediaInfo(remote, MediaType.VIDEO);\n const peerCodec = peerMediaInfo?.codecType;\n\n if (peerCodec\n && peerCodec !== currentCodec\n && (peerCodec !== CodecMimeType.VP9 || browser.supportsVP9())) {\n selectedCodec = peerCodec;\n }\n }\n }\n session && session.setVideoCodecs(selectedCodec, disabledCodec);\n }\n\n /**\n * Returns the preferred codec for the conference. The preferred codec for the JVB media session\n * is the one that gets published in presence and a comparision is made whenever a participant joins\n * or leaves the call.\n *\n * @returns {CodecMimeType} preferred codec.\n */\n getPreferredCodec() {\n return this.jvbPreferredCodec;\n }\n}\n","import { getLogger } from '@jitsi/logger';\n\nimport RTCEvents from '../../service/RTC/RTCEvents';\nimport { createBridgeChannelClosedEvent } from '../../service/statistics/AnalyticsEvents';\nimport FeatureFlags from '../flags/FeatureFlags';\nimport Statistics from '../statistics/statistics';\nimport GlobalOnErrorHandler from '../util/GlobalOnErrorHandler';\n\nconst logger = getLogger(__filename);\n\n/**\n * Handles a WebRTC RTCPeerConnection or a WebSocket instance to communicate\n * with the videobridge.\n */\nexport default class BridgeChannel {\n /**\n * Binds \"ondatachannel\" event listener on the given RTCPeerConnection\n * instance, or creates a WebSocket connection with the videobridge.\n * At least one of both, peerconnection or wsUrl parameters, must be\n * given.\n * @param {RTCPeerConnection} [peerconnection] WebRTC peer connection\n * instance.\n * @param {string} [wsUrl] WebSocket URL.\n * @param {EventEmitter} emitter the EventEmitter instance to use for event emission.\n */\n constructor(peerconnection, wsUrl, emitter) {\n if (!peerconnection && !wsUrl) {\n throw new TypeError('At least peerconnection or wsUrl must be given');\n } else if (peerconnection && wsUrl) {\n throw new TypeError('Just one of peerconnection or wsUrl must be given');\n }\n\n if (peerconnection) {\n logger.debug('constructor() with peerconnection');\n } else {\n logger.debug(`constructor() with wsUrl:\"${wsUrl}\"`);\n }\n\n // The underlying WebRTC RTCDataChannel or WebSocket instance.\n // @type {RTCDataChannel|WebSocket}\n this._channel = null;\n\n // @type {EventEmitter}\n this._eventEmitter = emitter;\n\n // Whether a RTCDataChannel or WebSocket is internally used.\n // @type {string} \"datachannel\" / \"websocket\"\n this._mode = null;\n\n // Indicates whether the connection retries are enabled or not.\n this._areRetriesEnabled = false;\n\n // Indicates whether the connection was closed from the client or not.\n this._closedFromClient = false;\n\n // If a RTCPeerConnection is given, listen for new RTCDataChannel\n // event.\n if (peerconnection) {\n const datachannel\n = peerconnection.createDataChannel(\n 'JVB data channel', {\n protocol: 'http://jitsi.org/protocols/colibri'\n });\n\n // Handle the RTCDataChannel.\n this._handleChannel(datachannel);\n this._mode = 'datachannel';\n\n // Otherwise create a WebSocket connection.\n } else if (wsUrl) {\n this._areRetriesEnabled = true;\n this._wsUrl = wsUrl;\n this._initWebSocket();\n }\n }\n\n /**\n * Initializes the web socket channel.\n *\n * @returns {void}\n */\n _initWebSocket() {\n // Create a WebSocket instance.\n const ws = new WebSocket(this._wsUrl);\n\n // Handle the WebSocket.\n this._handleChannel(ws);\n this._mode = 'websocket';\n }\n\n /**\n * Starts the websocket connection retries.\n *\n * @returns {void}\n */\n _startConnectionRetries() {\n let timeoutS = 1;\n\n const reload = () => {\n if (this.isOpen()) {\n return;\n }\n this._initWebSocket(this._wsUrl);\n timeoutS = Math.min(timeoutS * 2, 60);\n this._retryTimeout = setTimeout(reload, timeoutS * 1000);\n };\n\n this._retryTimeout = setTimeout(reload, timeoutS * 1000);\n }\n\n /**\n * Stops the websocket connection retries.\n *\n * @returns {void}\n */\n _stopConnectionRetries() {\n if (this._retryTimeout) {\n clearTimeout(this._retryTimeout);\n this._retryTimeout = undefined;\n }\n }\n\n /**\n * Retries to establish the websocket connection after the connection was closed by the server.\n *\n * @param {CloseEvent} closeEvent - The close event that triggered the retries.\n * @returns {void}\n */\n _retryWebSocketConnection(closeEvent) {\n if (!this._areRetriesEnabled) {\n return;\n }\n const { code, reason } = closeEvent;\n\n Statistics.sendAnalytics(createBridgeChannelClosedEvent(code, reason));\n this._areRetriesEnabled = false;\n this._eventEmitter.once(RTCEvents.DATA_CHANNEL_OPEN, () => {\n this._stopConnectionRetries();\n this._areRetriesEnabled = true;\n });\n this._startConnectionRetries();\n }\n\n /**\n * The channel mode.\n * @return {string} \"datachannel\" or \"websocket\" (or null if not yet set).\n */\n get mode() {\n return this._mode;\n }\n\n /**\n * Closes the currently opened channel.\n */\n close() {\n this._closedFromClient = true;\n this._stopConnectionRetries();\n this._areRetriesEnabled = false;\n if (this._channel) {\n try {\n this._channel.close();\n } catch (error) {} // eslint-disable-line no-empty\n\n this._channel = null;\n }\n }\n\n /**\n * Whether there is an underlying RTCDataChannel or WebSocket and it's\n * open.\n * @return {boolean}\n */\n isOpen() {\n return this._channel && (this._channel.readyState === 'open'\n || this._channel.readyState === WebSocket.OPEN);\n }\n\n /**\n * Sends local stats via the bridge channel.\n * @param {Object} payload The payload of the message.\n * @throws NetworkError/InvalidStateError/Error if the operation fails or if there is no data channel created.\n */\n sendEndpointStatsMessage(payload) {\n this._send({\n colibriClass: 'EndpointStats',\n ...payload\n });\n }\n\n /**\n * Sends message via the channel.\n * @param {string} to The id of the endpoint that should receive the\n * message. If \"\" the message will be sent to all participants.\n * @param {object} payload The payload of the message.\n * @throws NetworkError or InvalidStateError from RTCDataChannel#send (@see\n * {@link https://developer.mozilla.org/docs/Web/API/RTCDataChannel/send})\n * or from WebSocket#send or Error with \"No opened channel\" message.\n */\n sendMessage(to, payload) {\n this._send({\n colibriClass: 'EndpointMessage',\n msgPayload: payload,\n to\n });\n }\n\n /**\n * Sends a \"lastN value changed\" message via the channel.\n * @param {number} value The new value for lastN. -1 means unlimited.\n */\n sendSetLastNMessage(value) {\n logger.log(`Sending lastN=${value}.`);\n\n this._send({\n colibriClass: 'LastNChangedEvent',\n lastN: value\n });\n }\n\n /**\n * Sends a \"selected endpoints changed\" message via the channel.\n *\n * @param {Array} endpointIds - The ids of the selected endpoints.\n * @throws NetworkError or InvalidStateError from RTCDataChannel#send (@see\n * {@link https://developer.mozilla.org/docs/Web/API/RTCDataChannel/send})\n * or from WebSocket#send or Error with \"No opened channel\" message.\n */\n sendSelectedEndpointsMessage(endpointIds) {\n logger.log(`Sending selected endpoints: ${endpointIds}.`);\n\n this._send({\n colibriClass: 'SelectedEndpointsChangedEvent',\n selectedEndpoints: endpointIds\n });\n }\n\n /**\n * Sends a \"receiver video constraint\" message via the channel.\n * @param {Number} maxFrameHeightPixels the maximum frame height,\n * in pixels, this receiver is willing to receive\n */\n sendReceiverVideoConstraintMessage(maxFrameHeightPixels) {\n logger.log(`Sending ReceiverVideoConstraint with maxFrameHeight=${maxFrameHeightPixels}px`);\n this._send({\n colibriClass: 'ReceiverVideoConstraint',\n maxFrameHeight: maxFrameHeightPixels\n });\n }\n\n /**\n * Sends a 'ReceiverVideoConstraints' message via the bridge channel.\n *\n * @param {ReceiverVideoConstraints} constraints video constraints.\n */\n sendNewReceiverVideoConstraintsMessage(constraints) {\n logger.log(`Sending ReceiverVideoConstraints with ${JSON.stringify(constraints)}`);\n this._send({\n colibriClass: 'ReceiverVideoConstraints',\n ...constraints\n });\n }\n\n /**\n * Sends a 'VideoTypeMessage' message via the bridge channel.\n *\n * @param {string} videoType 'camera', 'desktop' or 'none'.\n * @deprecated to be replaced with sendSourceVideoTypeMessage\n */\n sendVideoTypeMessage(videoType) {\n logger.debug(`Sending VideoTypeMessage with video type as ${videoType}`);\n this._send({\n colibriClass: 'VideoTypeMessage',\n videoType\n });\n }\n\n /**\n * Sends a 'VideoTypeMessage' message via the bridge channel.\n *\n * @param {BridgeVideoType} videoType - the video type.\n * @param {SourceName} sourceName - the source name of the video track.\n * @returns {void}\n */\n sendSourceVideoTypeMessage(sourceName, videoType) {\n logger.info(`Sending SourceVideoTypeMessage with video type ${sourceName}: ${videoType}`);\n this._send({\n colibriClass: 'SourceVideoTypeMessage',\n sourceName,\n videoType\n });\n }\n\n /**\n * Set events on the given RTCDataChannel or WebSocket instance.\n */\n _handleChannel(channel) {\n const emitter = this._eventEmitter;\n\n channel.onopen = () => {\n logger.info(`${this._mode} channel opened`);\n\n // Code sample for sending string and/or binary data.\n // Sends string message to the bridge:\n // channel.send(\"Hello bridge!\");\n // Sends 12 bytes binary message to the bridge:\n // channel.send(new ArrayBuffer(12));\n\n emitter.emit(RTCEvents.DATA_CHANNEL_OPEN);\n };\n\n channel.onerror = event => {\n // WS error events contain no information about the failure (this is available in the onclose event) and\n // the event references the WS object itself, which causes hangs on mobile.\n if (this._mode !== 'websocket') {\n logger.error(`Channel error: ${event.message}`);\n }\n };\n\n channel.onmessage = ({ data }) => {\n // JSON object.\n let obj;\n\n try {\n obj = JSON.parse(data);\n } catch (error) {\n GlobalOnErrorHandler.callErrorHandler(error);\n logger.error('Failed to parse channel message as JSON: ', data, error);\n\n return;\n }\n\n const colibriClass = obj.colibriClass;\n\n switch (colibriClass) {\n case 'DominantSpeakerEndpointChangeEvent': {\n const { dominantSpeakerEndpoint, previousSpeakers = [] } = obj;\n\n logger.debug(`Dominant speaker: ${dominantSpeakerEndpoint}, previous speakers: ${previousSpeakers}`);\n emitter.emit(RTCEvents.DOMINANT_SPEAKER_CHANGED, dominantSpeakerEndpoint, previousSpeakers);\n break;\n }\n case 'EndpointConnectivityStatusChangeEvent': {\n const endpoint = obj.endpoint;\n const isActive = obj.active === 'true';\n\n logger.info(`Endpoint connection status changed: ${endpoint} active=${isActive}`);\n emitter.emit(RTCEvents.ENDPOINT_CONN_STATUS_CHANGED, endpoint, isActive);\n\n break;\n }\n case 'EndpointMessage': {\n emitter.emit(RTCEvents.ENDPOINT_MESSAGE_RECEIVED, obj.from, obj.msgPayload);\n\n break;\n }\n case 'EndpointStats': {\n emitter.emit(RTCEvents.ENDPOINT_STATS_RECEIVED, obj.from, obj);\n\n break;\n }\n case 'LastNEndpointsChangeEvent': {\n if (!FeatureFlags.isSourceNameSignalingEnabled()) {\n // The new/latest list of last-n endpoint IDs (i.e. endpoints for which the bridge is sending\n // video).\n const lastNEndpoints = obj.lastNEndpoints;\n\n logger.info(`New forwarded endpoints: ${lastNEndpoints}`);\n emitter.emit(RTCEvents.LASTN_ENDPOINT_CHANGED, lastNEndpoints);\n }\n\n break;\n }\n case 'ForwardedSources': {\n if (FeatureFlags.isSourceNameSignalingEnabled()) {\n // The new/latest list of forwarded sources\n const forwardedSources = obj.forwardedSources;\n\n logger.info(`New forwarded sources: ${forwardedSources}`);\n emitter.emit(RTCEvents.FORWARDED_SOURCES_CHANGED, forwardedSources);\n }\n\n break;\n }\n case 'SenderVideoConstraints': {\n const videoConstraints = obj.videoConstraints;\n\n if (videoConstraints) {\n logger.info(`SenderVideoConstraints: ${JSON.stringify(videoConstraints)}`);\n emitter.emit(RTCEvents.SENDER_VIDEO_CONSTRAINTS_CHANGED, videoConstraints);\n }\n break;\n }\n case 'SenderSourceConstraints': {\n if (FeatureFlags.isSourceNameSignalingEnabled()) {\n const { sourceName, maxHeight } = obj;\n\n if (typeof sourceName === 'string' && typeof maxHeight === 'number') {\n // eslint-disable-next-line object-property-newline\n logger.info(`SenderSourceConstraints: ${JSON.stringify({ sourceName, maxHeight })}`);\n emitter.emit(\n RTCEvents.SENDER_VIDEO_CONSTRAINTS_CHANGED, {\n sourceName,\n maxHeight\n }\n );\n } else {\n logger.error(`Invalid SenderSourceConstraints: ${JSON.stringify(obj)}`);\n }\n }\n break;\n }\n case 'ServerHello': {\n logger.info(`Received ServerHello, version=${obj.version}.`);\n break;\n }\n default: {\n logger.debug('Channel JSON-formatted message: ', obj);\n\n // The received message appears to be appropriately formatted\n // (i.e. is a JSON object which assigns a value to the\n // mandatory property colibriClass) so don't just swallow it,\n // expose it to public consumption.\n emitter.emit(`rtc.datachannel.${colibriClass}`, obj);\n }\n }\n };\n\n channel.onclose = event => {\n logger.info(`Channel closed by ${this._closedFromClient ? 'client' : 'server'}`);\n\n if (this._mode === 'websocket') {\n if (!this._closedFromClient) {\n logger.error(`Channel closed: ${event.code} ${event.reason}`);\n this._retryWebSocketConnection(event);\n }\n }\n\n // Remove the channel.\n this._channel = null;\n };\n\n // Store the channel.\n this._channel = channel;\n }\n\n /**\n * Sends passed object via the channel.\n * @param {object} jsonObject The object that will be sent.\n * @throws NetworkError or InvalidStateError from RTCDataChannel#send (@see\n * {@link https://developer.mozilla.org/docs/Web/API/RTCDataChannel/send})\n * or from WebSocket#send or Error with \"No opened channel\" message.\n */\n _send(jsonObject) {\n const channel = this._channel;\n\n if (!this.isOpen()) {\n logger.error('Bridge Channel send: no opened channel.');\n throw new Error('No opened channel');\n }\n\n channel.send(JSON.stringify(jsonObject));\n }\n}\n","import { getLogger } from '@jitsi/logger';\nimport EventEmitter from 'events';\nimport clonedeep from 'lodash.clonedeep';\n\nimport JitsiTrackError from '../../JitsiTrackError';\nimport * as JitsiTrackErrors from '../../JitsiTrackErrors';\nimport CameraFacingMode from '../../service/RTC/CameraFacingMode';\nimport RTCEvents from '../../service/RTC/RTCEvents';\nimport Resolutions from '../../service/RTC/Resolutions';\nimport VideoType from '../../service/RTC/VideoType';\nimport { AVAILABLE_DEVICE } from '../../service/statistics/AnalyticsEvents';\nimport browser from '../browser';\nimport SDPUtil from '../sdp/SDPUtil';\nimport Statistics from '../statistics/statistics';\nimport GlobalOnErrorHandler from '../util/GlobalOnErrorHandler';\nimport Listenable from '../util/Listenable';\n\nimport screenObtainer from './ScreenObtainer';\n\nconst logger = getLogger(__filename);\n\n// Require adapter only for certain browsers. This is being done for\n// react-native, which has its own shims, and while browsers are being migrated\n// over to use adapter's shims.\nif (browser.usesAdapter()) {\n require('webrtc-adapter');\n}\n\nconst eventEmitter = new EventEmitter();\n\nconst AVAILABLE_DEVICES_POLL_INTERVAL_TIME = 3000; // ms\n\n/**\n * Default MediaStreamConstraints to use for calls to getUserMedia.\n *\n * @private\n */\nconst DEFAULT_CONSTRAINTS = {\n video: {\n height: {\n ideal: 720,\n max: 720,\n min: 180\n },\n width: {\n ideal: 1280,\n max: 1280,\n min: 320\n }\n }\n};\n\n// Currently audio output device change is supported only in Chrome and\n// default output always has 'default' device ID\nlet audioOutputDeviceId = 'default'; // default device\n// whether user has explicitly set a device to use\nlet audioOutputChanged = false;\n\n// Disables all audio processing\nlet disableAP = false;\n\n// Disables Acoustic Echo Cancellation\nlet disableAEC = false;\n\n// Disables Noise Suppression\nlet disableNS = false;\n\n// Disables Automatic Gain Control\nlet disableAGC = false;\n\n// Enables stereo.\nlet stereo = null;\n\nconst featureDetectionAudioEl = document.createElement('audio');\nconst isAudioOutputDeviceChangeAvailable\n = typeof featureDetectionAudioEl.setSinkId !== 'undefined';\n\nlet availableDevices = [];\nlet availableDevicesPollTimer;\n\n/**\n * An empty function.\n */\nfunction emptyFuncton() {\n // no-op\n}\n\n/**\n * Creates a constraints object to be passed into a call to getUserMedia.\n *\n * @param {Array} um - An array of user media types to get. The accepted types are \"video\", \"audio\", and \"desktop.\"\n * @param {Object} options - Various values to be added to the constraints.\n * @param {string} options.cameraDeviceId - The device id for the video capture device to get video from.\n * @param {Object} options.constraints - Default constraints object to use as a base for the returned constraints.\n * @param {Object} options.desktopStream - The desktop source id from which to capture a desktop sharing video.\n * @param {string} options.facingMode - Which direction the camera is pointing to (applicable on mobile)\n * @param {string} options.micDeviceId - The device id for the audio capture device to get audio from.\n * @private\n * @returns {Object}\n */\nfunction getConstraints(um = [], options = {}) {\n // Create a deep copy of the constraints to avoid any modification of\n // the passed in constraints object.\n const constraints = clonedeep(options.constraints || DEFAULT_CONSTRAINTS);\n\n if (um.indexOf('video') >= 0) {\n // The \"resolution\" option is a shortcut and takes precendence.\n if (Resolutions[options.resolution]) {\n const r = Resolutions[options.resolution];\n\n constraints.video.height = { ideal: r.height };\n constraints.video.width = { ideal: r.width };\n }\n\n if (!constraints.video) {\n constraints.video = {};\n }\n\n // Override the constraints on Safari because of the following webkit bug.\n // https://bugs.webkit.org/show_bug.cgi?id=210932\n // Camera doesn't start on older macOS versions if min/max constraints are specified.\n // TODO: remove this hack when the bug fix is available on Mojave, Sierra and High Sierra.\n if (browser.isWebKitBased()) {\n if (constraints.video.height && constraints.video.height.ideal) {\n constraints.video.height = { ideal: constraints.video.height.ideal };\n } else {\n logger.warn('Ideal camera height missing, camera may not start properly');\n }\n if (constraints.video.width && constraints.video.width.ideal) {\n constraints.video.width = { ideal: constraints.video.width.ideal };\n } else {\n logger.warn('Ideal camera width missing, camera may not start properly');\n }\n }\n if (options.cameraDeviceId) {\n constraints.video.deviceId = options.cameraDeviceId;\n } else {\n const facingMode = options.facingMode || CameraFacingMode.USER;\n\n constraints.video.facingMode = facingMode;\n }\n } else {\n constraints.video = false;\n }\n\n if (um.indexOf('audio') >= 0) {\n if (!constraints.audio || typeof constraints.audio === 'boolean') {\n constraints.audio = {};\n }\n\n constraints.audio = {\n autoGainControl: !disableAGC && !disableAP,\n deviceId: options.micDeviceId,\n echoCancellation: !disableAEC && !disableAP,\n noiseSuppression: !disableNS && !disableAP\n };\n\n if (stereo) {\n Object.assign(constraints.audio, { channelCount: 2 });\n }\n } else {\n constraints.audio = false;\n }\n\n return constraints;\n}\n\n/**\n * Updates the granted permissions based on the options we requested and the\n * streams we received.\n * @param um the options we requested to getUserMedia.\n * @param stream the stream we received from calling getUserMedia.\n */\nfunction updateGrantedPermissions(um, stream) {\n const audioTracksReceived\n = Boolean(stream) && stream.getAudioTracks().length > 0;\n const videoTracksReceived\n = Boolean(stream) && stream.getVideoTracks().length > 0;\n const grantedPermissions = {};\n\n if (um.indexOf('video') !== -1) {\n grantedPermissions.video = videoTracksReceived;\n }\n if (um.indexOf('audio') !== -1) {\n grantedPermissions.audio = audioTracksReceived;\n }\n\n eventEmitter.emit(RTCEvents.PERMISSIONS_CHANGED, grantedPermissions);\n}\n\n/**\n * Checks if new list of available media devices differs from previous one.\n * @param {MediaDeviceInfo[]} newDevices - list of new devices.\n * @returns {boolean} - true if list is different, false otherwise.\n */\nfunction compareAvailableMediaDevices(newDevices) {\n if (newDevices.length !== availableDevices.length) {\n return true;\n }\n\n /* eslint-disable newline-per-chained-call */\n\n return (\n newDevices.map(mediaDeviceInfoToJSON).sort().join('')\n !== availableDevices\n .map(mediaDeviceInfoToJSON).sort().join(''));\n\n /* eslint-enable newline-per-chained-call */\n\n /**\n *\n * @param info\n */\n function mediaDeviceInfoToJSON(info) {\n return JSON.stringify({\n kind: info.kind,\n deviceId: info.deviceId,\n groupId: info.groupId,\n label: info.label,\n facing: info.facing\n });\n }\n}\n\n/**\n * Sends analytics event with the passed device list.\n *\n * @param {Array} deviceList - List with info about the\n * available devices.\n * @returns {void}\n */\nfunction sendDeviceListToAnalytics(deviceList) {\n const audioInputDeviceCount\n = deviceList.filter(d => d.kind === 'audioinput').length;\n const audioOutputDeviceCount\n = deviceList.filter(d => d.kind === 'audiooutput').length;\n const videoInputDeviceCount\n = deviceList.filter(d => d.kind === 'videoinput').length;\n const videoOutputDeviceCount\n = deviceList.filter(d => d.kind === 'videooutput').length;\n\n deviceList.forEach(device => {\n const attributes = {\n 'audio_input_device_count': audioInputDeviceCount,\n 'audio_output_device_count': audioOutputDeviceCount,\n 'video_input_device_count': videoInputDeviceCount,\n 'video_output_device_count': videoOutputDeviceCount,\n 'device_id': device.deviceId,\n 'device_group_id': device.groupId,\n 'device_kind': device.kind,\n 'device_label': device.label\n };\n\n Statistics.sendAnalytics(AVAILABLE_DEVICE, attributes);\n });\n}\n\n\n/**\n * Update known devices.\n *\n * @param {Array} pds - The new devices.\n * @returns {void}\n *\n * NOTE: Use this function as a shared callback to handle both the devicechange event and the polling implementations.\n * This prevents duplication and works around a chrome bug (verified to occur on 68) where devicechange fires twice in\n * a row, which can cause async post devicechange processing to collide.\n */\nfunction updateKnownDevices(pds) {\n if (compareAvailableMediaDevices(pds)) {\n onMediaDevicesListChanged(pds);\n }\n}\n\n/**\n * Event handler for the 'devicechange' event.\n *\n * @param {MediaDeviceInfo[]} devices - list of media devices.\n * @emits RTCEvents.DEVICE_LIST_CHANGED\n */\nfunction onMediaDevicesListChanged(devicesReceived) {\n availableDevices = devicesReceived.slice(0);\n logger.info('list of media devices has changed:', availableDevices);\n\n sendDeviceListToAnalytics(availableDevices);\n\n // Used by tracks to update the real device id before the consumer of lib-jitsi-meet receives the new device list.\n eventEmitter.emit(RTCEvents.DEVICE_LIST_WILL_CHANGE, availableDevices);\n\n eventEmitter.emit(RTCEvents.DEVICE_LIST_CHANGED, availableDevices);\n}\n\n/**\n *\n */\nclass RTCUtils extends Listenable {\n /**\n *\n */\n constructor() {\n super(eventEmitter);\n }\n\n /**\n * Depending on the browser, sets difference instance methods for\n * interacting with user media and adds methods to native WebRTC-related\n * objects. Also creates an instance variable for peer connection\n * constraints.\n *\n * @param {Object} options\n * @returns {void}\n */\n init(options = {}) {\n if (typeof options.disableAEC === 'boolean') {\n disableAEC = options.disableAEC;\n logger.info(`Disable AEC: ${disableAEC}`);\n }\n if (typeof options.disableNS === 'boolean') {\n disableNS = options.disableNS;\n logger.info(`Disable NS: ${disableNS}`);\n }\n if (typeof options.disableAP === 'boolean') {\n disableAP = options.disableAP;\n logger.info(`Disable AP: ${disableAP}`);\n }\n if (typeof options.disableAGC === 'boolean') {\n disableAGC = options.disableAGC;\n logger.info(`Disable AGC: ${disableAGC}`);\n }\n if (typeof options.audioQuality?.stereo === 'boolean') {\n stereo = options.audioQuality.stereo;\n logger.info(`Stereo: ${stereo}`);\n }\n\n window.clearInterval(availableDevicesPollTimer);\n availableDevicesPollTimer = undefined;\n\n if (browser.isReactNative()) {\n this.RTCPeerConnectionType = RTCPeerConnection;\n\n this.attachMediaStream = undefined; // Unused on React Native.\n\n this.getStreamID = function({ id }) {\n // The react-native-webrtc implementation that we use at the\n // time of this writing returns a number for the id of\n // MediaStream. Let's just say that a number contains no special\n // characters.\n return (\n typeof id === 'number'\n ? id\n : SDPUtil.filterSpecialChars(id));\n };\n this.getTrackID = ({ id }) => id;\n } else {\n this.RTCPeerConnectionType = RTCPeerConnection;\n\n this.attachMediaStream\n = wrapAttachMediaStream((element, stream) => {\n if (element) {\n element.srcObject = stream;\n }\n });\n\n this.getStreamID = ({ id }) => id;\n this.getTrackID = ({ id }) => id;\n }\n\n this.pcConstraints = browser.isChromiumBased() || browser.isReactNative()\n ? { optional: [\n { googScreencastMinBitrate: 100 },\n { googCpuOveruseDetection: true }\n ] }\n : {};\n\n screenObtainer.init(options);\n\n if (this.isDeviceListAvailable()) {\n this.enumerateDevices(ds => {\n availableDevices = ds.slice(0);\n\n logger.debug('Available devices: ', availableDevices);\n sendDeviceListToAnalytics(availableDevices);\n\n eventEmitter.emit(\n RTCEvents.DEVICE_LIST_AVAILABLE,\n availableDevices);\n\n if (browser.supportsDeviceChangeEvent()) {\n navigator.mediaDevices.addEventListener(\n 'devicechange',\n () => this.enumerateDevices(emptyFuncton));\n } else {\n // Periodically poll enumerateDevices() method to check if\n // list of media devices has changed.\n availableDevicesPollTimer = window.setInterval(\n () => this.enumerateDevices(emptyFuncton),\n AVAILABLE_DEVICES_POLL_INTERVAL_TIME);\n }\n });\n }\n }\n\n /**\n *\n * @param {Function} callback\n */\n enumerateDevices(callback) {\n navigator.mediaDevices.enumerateDevices()\n .then(devices => {\n updateKnownDevices(devices);\n callback(devices);\n })\n .catch(error => {\n logger.warn(`Failed to enumerate devices. ${error}`);\n updateKnownDevices([]);\n callback([]);\n });\n }\n\n /**\n * Acquires a media stream via getUserMedia that\n * matches the given constraints\n *\n * @param {array} umDevices which devices to acquire (e.g. audio, video)\n * @param {Object} constraints - Stream specifications to use.\n * @param {number} timeout - The timeout in ms for GUM.\n * @returns {Promise}\n */\n _getUserMedia(umDevices, constraints = {}, timeout = 0) {\n return new Promise((resolve, reject) => {\n let gumTimeout, timeoutExpired = false;\n\n if (typeof timeout === 'number' && !isNaN(timeout) && timeout > 0) {\n gumTimeout = setTimeout(() => {\n timeoutExpired = true;\n gumTimeout = undefined;\n reject(new JitsiTrackError(JitsiTrackErrors.TIMEOUT));\n }, timeout);\n }\n\n navigator.mediaDevices.getUserMedia(constraints)\n .then(stream => {\n logger.log('onUserMediaSuccess');\n updateGrantedPermissions(umDevices, stream);\n if (!timeoutExpired) {\n if (typeof gumTimeout !== 'undefined') {\n clearTimeout(gumTimeout);\n }\n resolve(stream);\n }\n })\n .catch(error => {\n logger.warn(`Failed to get access to local media. ${error} ${JSON.stringify(constraints)}`);\n const jitsiError = new JitsiTrackError(error, constraints, umDevices);\n\n if (!timeoutExpired) {\n if (typeof gumTimeout !== 'undefined') {\n clearTimeout(gumTimeout);\n }\n reject(error);\n }\n\n if (jitsiError.name === JitsiTrackErrors.PERMISSION_DENIED) {\n updateGrantedPermissions(umDevices, undefined);\n }\n\n // else {\n // Probably the error is not caused by the lack of permissions and we don't need to update them.\n // }\n });\n });\n }\n\n /**\n * Acquire a display stream via the screenObtainer. This requires extra\n * logic compared to use screenObtainer versus normal device capture logic\n * in RTCUtils#_getUserMedia.\n *\n * @returns {Promise} A promise which will be resolved with an object which\n * contains the acquired display stream. If desktop sharing is not supported\n * then a rejected promise will be returned.\n */\n _getDesktopMedia() {\n if (!screenObtainer.isSupported()) {\n return Promise.reject(new Error('Desktop sharing is not supported!'));\n }\n\n return new Promise((resolve, reject) => {\n screenObtainer.obtainStream(\n stream => {\n resolve(stream);\n },\n error => {\n reject(error);\n });\n });\n }\n\n /**\n * Private utility for determining if the passed in MediaStream contains\n * tracks of the type(s) specified in the requested devices.\n *\n * @param {string[]} requestedDevices - The track types that are expected to\n * be includes in the stream.\n * @param {MediaStream} stream - The MediaStream to check if it has the\n * expected track types.\n * @returns {string[]} An array of string with the missing track types. The\n * array will be empty if all requestedDevices are found in the stream.\n */\n _getMissingTracks(requestedDevices = [], stream) {\n const missingDevices = [];\n\n const audioDeviceRequested = requestedDevices.includes('audio');\n const audioTracksReceived\n = stream && stream.getAudioTracks().length > 0;\n\n if (audioDeviceRequested && !audioTracksReceived) {\n missingDevices.push('audio');\n }\n\n const videoDeviceRequested = requestedDevices.includes('video');\n const videoTracksReceived\n = stream && stream.getVideoTracks().length > 0;\n\n if (videoDeviceRequested && !videoTracksReceived) {\n missingDevices.push('video');\n }\n\n return missingDevices;\n }\n\n /**\n * Gets streams from specified device types. This function intentionally\n * ignores errors for upstream to catch and handle instead.\n *\n * @param {Object} options - A hash describing what devices to get and\n * relevant constraints.\n * @param {string[]} options.devices - The types of media to capture. Valid\n * values are \"desktop\", \"audio\", and \"video\".\n * @param {Object} options.desktopSharingFrameRate\n * @param {Object} options.desktopSharingFrameRate.min - Minimum fps\n * @param {Object} options.desktopSharingFrameRate.max - Maximum fps\n * @param {String} options.desktopSharingSourceDevice - The device id or\n * label for a video input source that should be used for screensharing.\n * @returns {Promise} The promise, when successful, will return an array of\n * meta data for the requested device type, which includes the stream and\n * track. If an error occurs, it will be deferred to the caller for\n * handling.\n */\n obtainAudioAndVideoPermissions(options) {\n const {\n timeout,\n ...otherOptions\n } = options;\n\n const mediaStreamsMetaData = [];\n\n // Declare private functions to be used in the promise chain below.\n // These functions are declared in the scope of this function because\n // they are not being used anywhere else, so only this function needs to\n // know about them.\n\n /**\n * Executes a request for desktop media if specified in options.\n *\n * @returns {Promise}\n */\n const maybeRequestDesktopDevice = function() {\n const umDevices = otherOptions.devices || [];\n const isDesktopDeviceRequested\n = umDevices.indexOf('desktop') !== -1;\n\n if (!isDesktopDeviceRequested) {\n return Promise.resolve();\n }\n\n const {\n desktopSharingSourceDevice\n } = otherOptions;\n\n // Attempt to use a video input device as a screenshare source if\n // the option is defined.\n if (desktopSharingSourceDevice) {\n const matchingDevice\n = availableDevices && availableDevices.find(device =>\n device.kind === 'videoinput'\n && (device.deviceId === desktopSharingSourceDevice\n || device.label === desktopSharingSourceDevice));\n\n if (!matchingDevice) {\n return Promise.reject(new JitsiTrackError(\n { name: 'ConstraintNotSatisfiedError' },\n {},\n [ desktopSharingSourceDevice ]\n ));\n }\n\n const requestedDevices = [ 'video' ];\n const constraints = {\n video: {\n deviceId: matchingDevice.deviceId\n\n // frameRate is omited here on purpose since this is a device that we'll pretend is a screen.\n }\n };\n\n return this._getUserMedia(requestedDevices, constraints, timeout)\n .then(stream => {\n return {\n sourceType: 'device',\n stream\n };\n });\n }\n\n return this._getDesktopMedia();\n }.bind(this);\n\n /**\n * Creates a meta data object about the passed in desktopStream and\n * pushes the meta data to the internal array mediaStreamsMetaData to be\n * returned later.\n *\n * @param {MediaStreamTrack} desktopStream - A track for a desktop\n * capture.\n * @returns {void}\n */\n const maybeCreateAndAddDesktopTrack = function(desktopStream) {\n if (!desktopStream) {\n return;\n }\n\n const { stream, sourceId, sourceType } = desktopStream;\n\n const desktopAudioTracks = stream.getAudioTracks();\n\n if (desktopAudioTracks.length) {\n const desktopAudioStream = new MediaStream(desktopAudioTracks);\n\n mediaStreamsMetaData.push({\n stream: desktopAudioStream,\n sourceId,\n sourceType,\n track: desktopAudioStream.getAudioTracks()[0]\n });\n }\n\n const desktopVideoTracks = stream.getVideoTracks();\n\n if (desktopVideoTracks.length) {\n const desktopVideoStream = new MediaStream(desktopVideoTracks);\n\n mediaStreamsMetaData.push({\n stream: desktopVideoStream,\n sourceId,\n sourceType,\n track: desktopVideoStream.getVideoTracks()[0],\n videoType: VideoType.DESKTOP\n });\n }\n };\n\n /**\n * Executes a request for audio and/or video, as specified in options.\n * By default both audio and video will be captured if options.devices\n * is not defined.\n *\n * @returns {Promise}\n */\n const maybeRequestCaptureDevices = function() {\n const umDevices = otherOptions.devices || [ 'audio', 'video' ];\n const requestedCaptureDevices = umDevices.filter(device => device === 'audio' || device === 'video');\n\n if (!requestedCaptureDevices.length) {\n return Promise.resolve();\n }\n\n const constraints = getConstraints(requestedCaptureDevices, otherOptions);\n\n logger.info('Got media constraints: ', JSON.stringify(constraints));\n\n return this._getUserMedia(requestedCaptureDevices, constraints, timeout);\n }.bind(this);\n\n /**\n * Splits the passed in media stream into separate audio and video\n * streams and creates meta data objects for each and pushes them to the\n * internal array mediaStreamsMetaData to be returned later.\n *\n * @param {MediaStreamTrack} avStream - A track for with audio and/or\n * video track.\n * @returns {void}\n */\n const maybeCreateAndAddAVTracks = function(avStream) {\n if (!avStream) {\n return;\n }\n\n const audioTracks = avStream.getAudioTracks();\n\n if (audioTracks.length) {\n const audioStream = new MediaStream(audioTracks);\n\n mediaStreamsMetaData.push({\n stream: audioStream,\n track: audioStream.getAudioTracks()[0],\n effects: otherOptions.effects\n });\n }\n\n const videoTracks = avStream.getVideoTracks();\n\n if (videoTracks.length) {\n const videoStream = new MediaStream(videoTracks);\n\n mediaStreamsMetaData.push({\n stream: videoStream,\n track: videoStream.getVideoTracks()[0],\n videoType: VideoType.CAMERA,\n effects: otherOptions.effects\n });\n }\n };\n\n return maybeRequestDesktopDevice()\n .then(maybeCreateAndAddDesktopTrack)\n .then(maybeRequestCaptureDevices)\n .then(maybeCreateAndAddAVTracks)\n .then(() => mediaStreamsMetaData)\n .catch(error => {\n mediaStreamsMetaData.forEach(({ stream }) => {\n this.stopMediaStream(stream);\n });\n\n return Promise.reject(error);\n });\n }\n\n /**\n * Checks whether it is possible to enumerate available cameras/microphones.\n *\n * @returns {boolean} {@code true} if the device listing is available;\n * {@code false}, otherwise.\n */\n isDeviceListAvailable() {\n return Boolean(\n navigator.mediaDevices\n && navigator.mediaDevices.enumerateDevices);\n }\n\n /**\n * Returns true if changing the input (camera / microphone) or output\n * (audio) device is supported and false if not.\n * @params {string} [deviceType] - type of device to change. Default is\n * undefined or 'input', 'output' - for audio output device change.\n * @returns {boolean} true if available, false otherwise.\n */\n isDeviceChangeAvailable(deviceType) {\n if (deviceType === 'output' || deviceType === 'audiooutput') {\n return isAudioOutputDeviceChangeAvailable;\n }\n\n // Calling getUserMedia again (for preview) kills the track returned by the first getUserMedia call because of\n // https://bugs.webkit.org/show_bug.cgi?id=179363. Therefore, do not show microphone/camera options on mobile\n // Safari.\n if ((deviceType === 'audioinput' || deviceType === 'input') && browser.isIosBrowser()) {\n return false;\n }\n\n return true;\n }\n\n /**\n * A method to handle stopping of the stream.\n * One point to handle the differences in various implementations.\n * @param mediaStream MediaStream object to stop.\n */\n stopMediaStream(mediaStream) {\n if (!mediaStream) {\n return;\n }\n\n mediaStream.getTracks().forEach(track => {\n if (track.stop) {\n track.stop();\n }\n });\n\n // leave stop for implementation still using it\n if (mediaStream.stop) {\n mediaStream.stop();\n }\n\n // The MediaStream implementation of the react-native-webrtc project has\n // an explicit release method that is to be invoked in order to release\n // used resources such as memory.\n if (mediaStream.release) {\n mediaStream.release();\n }\n }\n\n /**\n * Returns whether the desktop sharing is enabled or not.\n * @returns {boolean}\n */\n isDesktopSharingEnabled() {\n return screenObtainer.isSupported();\n }\n\n /**\n * Sets current audio output device.\n * @param {string} deviceId - id of 'audiooutput' device from\n * navigator.mediaDevices.enumerateDevices(), 'default' for default\n * device\n * @returns {Promise} - resolves when audio output is changed, is rejected\n * otherwise\n */\n setAudioOutputDevice(deviceId) {\n if (!this.isDeviceChangeAvailable('output')) {\n return Promise.reject(\n new Error('Audio output device change is not supported'));\n }\n\n return featureDetectionAudioEl.setSinkId(deviceId)\n .then(() => {\n audioOutputDeviceId = deviceId;\n audioOutputChanged = true;\n\n logger.log(`Audio output device set to ${deviceId}`);\n\n eventEmitter.emit(RTCEvents.AUDIO_OUTPUT_DEVICE_CHANGED,\n deviceId);\n });\n }\n\n /**\n * Sets the capture frame rate for desktop tracks.\n *\n * @param {number} maxFps - max fps to be used as the capture frame rate.\n * @returns {void}\n */\n setDesktopSharingFrameRate(maxFps) {\n screenObtainer.setDesktopSharingFrameRate(maxFps);\n }\n\n /**\n * Returns currently used audio output device id, '' stands for default\n * device\n * @returns {string}\n */\n getAudioOutputDevice() {\n return audioOutputDeviceId;\n }\n\n /**\n * Returns list of available media devices if its obtained, otherwise an\n * empty array is returned/\n * @returns {Array} list of available media devices.\n */\n getCurrentlyAvailableMediaDevices() {\n return availableDevices;\n }\n\n /**\n * Returns whether available devices have permissions granted\n * @returns {Boolean}\n */\n arePermissionsGrantedForAvailableDevices() {\n return availableDevices.some(device => Boolean(device.label));\n }\n\n /**\n * Returns event data for device to be reported to stats.\n * @returns {MediaDeviceInfo} device.\n */\n getEventDataForActiveDevice(device) {\n const deviceList = [];\n const deviceData = {\n 'deviceId': device.deviceId,\n 'kind': device.kind,\n 'label': device.label,\n 'groupId': device.groupId\n };\n\n deviceList.push(deviceData);\n\n return { deviceList };\n }\n\n /**\n * Configures the given PeerConnection constraints to either enable or\n * disable (according to the value of the 'enable' parameter) the\n * 'googSuspendBelowMinBitrate' option.\n * @param constraints the constraints on which to operate.\n * @param enable {boolean} whether to enable or disable the suspend video\n * option.\n */\n setSuspendVideo(constraints, enable) {\n if (!constraints.optional) {\n constraints.optional = [];\n }\n\n // Get rid of all \"googSuspendBelowMinBitrate\" constraints (we assume\n // that the elements of constraints.optional contain a single property).\n constraints.optional\n = constraints.optional.filter(\n c => !c.hasOwnProperty('googSuspendBelowMinBitrate'));\n\n if (enable) {\n constraints.optional.push({ googSuspendBelowMinBitrate: 'true' });\n }\n }\n}\n\nconst rtcUtils = new RTCUtils();\n\n/**\n * Wraps original attachMediaStream function to set current audio output device\n * if this is supported.\n * @param {Function} origAttachMediaStream\n * @returns {Function}\n */\nfunction wrapAttachMediaStream(origAttachMediaStream) {\n return function(element, stream) {\n // eslint-disable-next-line prefer-rest-params\n const res = origAttachMediaStream.apply(rtcUtils, arguments);\n\n if (stream\n && rtcUtils.isDeviceChangeAvailable('output')\n && stream.getAudioTracks\n && stream.getAudioTracks().length\n\n // we skip setting audio output if there was no explicit change\n && audioOutputChanged) {\n element.setSinkId(rtcUtils.getAudioOutputDevice())\n .catch(function(ex) {\n const err\n = new JitsiTrackError(ex, null, [ 'audiooutput' ]);\n\n GlobalOnErrorHandler.callUnhandledRejectionHandler({\n promise: this, // eslint-disable-line no-invalid-this\n reason: err\n });\n\n logger.warn(\n 'Failed to set audio output device for the element.'\n + ' Default audio output device will be used'\n + ' instead',\n element,\n err);\n });\n }\n\n return res;\n };\n}\n\nexport default rtcUtils;\n","import { getLogger } from '@jitsi/logger';\nimport EventEmitter from 'events';\n\nimport * as JitsiTrackEvents from '../../JitsiTrackEvents';\nimport * as MediaType from '../../service/RTC/MediaType';\nimport browser from '../browser';\n\nimport RTCUtils from './RTCUtils';\n\nconst logger = getLogger(__filename);\n\n/**\n * Maps our handler types to MediaStreamTrack properties.\n */\nconst trackHandler2Prop = {\n 'track_mute': 'onmute', // Not supported on FF\n 'track_unmute': 'onunmute',\n 'track_ended': 'onended'\n};\n\n/**\n * Represents a single media track (either audio or video).\n */\nexport default class JitsiTrack extends EventEmitter {\n /* eslint-disable max-params */\n /**\n * Represents a single media track (either audio or video).\n * @constructor\n * @param conference the rtc instance\n * @param stream the WebRTC MediaStream instance\n * @param track the WebRTC MediaStreamTrack instance, must be part of\n * the given stream.\n * @param streamInactiveHandler the function that will handle\n * onended/oninactive events of the stream.\n * @param trackMediaType the media type of the JitsiTrack\n * @param videoType the VideoType for this track if any\n */\n constructor(\n conference,\n stream,\n track,\n streamInactiveHandler,\n trackMediaType,\n videoType) {\n super();\n\n // aliases for addListener/removeListener\n this.addEventListener = this.addListener;\n this.removeEventListener = this.off = this.removeListener;\n\n /**\n * Array with the HTML elements that are displaying the streams.\n * @type {Array}\n */\n this.containers = [];\n this.conference = conference;\n this.audioLevel = -1;\n this.type = trackMediaType;\n this.track = track;\n this.videoType = videoType;\n this.handlers = new Map();\n\n /**\n * Indicates whether this JitsiTrack has been disposed. If true, this\n * JitsiTrack is to be considered unusable and operations involving it\n * are to fail (e.g. {@link JitsiConference#addTrack(JitsiTrack)},\n * {@link JitsiConference#removeTrack(JitsiTrack)}).\n * @type {boolean}\n */\n this.disposed = false;\n\n /**\n * The inactive handler which will be triggered when the underlying\n * MediaStream ends.\n *\n * @private\n * @type {Function}\n */\n this._streamInactiveHandler = streamInactiveHandler;\n\n this._setStream(stream);\n }\n\n /* eslint-enable max-params */\n\n /**\n * Adds onended/oninactive handler to a MediaStream or a MediaStreamTrack.\n * Firefox doesn't fire a inactive event on the MediaStream, instead it fires\n * a onended event on the MediaStreamTrack.\n * @param {Function} handler the handler\n */\n _addMediaStreamInactiveHandler(handler) {\n if (browser.isFirefox()) {\n this.track.onended = handler;\n } else {\n this.stream.oninactive = handler;\n }\n }\n\n /**\n * Sets handler to the WebRTC MediaStream or MediaStreamTrack object\n * depending on the passed type.\n * @param {string} type the type of the handler that is going to be set\n * @param {Function} handler the handler.\n */\n _setHandler(type, handler) {\n if (!trackHandler2Prop.hasOwnProperty(type)) {\n logger.error(`Invalid handler type ${type}`);\n\n return;\n }\n if (handler) {\n this.handlers.set(type, handler);\n } else {\n this.handlers.delete(type);\n }\n\n if (this.stream) {\n for (const track of this.stream.getTracks()) {\n track[trackHandler2Prop[type]] = handler;\n }\n }\n }\n\n /**\n * Unregisters all event handlers bound to the underlying media stream/track\n * @private\n */\n _unregisterHandlers() {\n if (!this.stream) {\n logger.warn(\n `${this}: unable to unregister handlers - no stream object`);\n\n return;\n }\n\n for (const type of this.handlers.keys()) {\n // FIXME Why only video tracks?\n for (const videoTrack of this.stream.getVideoTracks()) {\n videoTrack[trackHandler2Prop[type]] = undefined;\n }\n }\n if (this._streamInactiveHandler) {\n this._addMediaStreamInactiveHandler(undefined);\n }\n }\n\n /**\n * Sets the stream property of JitsiTrack object and sets all stored\n * handlers to it.\n *\n * @param {MediaStream} stream the new stream.\n * @protected\n */\n _setStream(stream) {\n if (this.stream === stream) {\n return;\n }\n\n this.stream = stream;\n\n // TODO Practically, that's like the opposite of _unregisterHandlers\n // i.e. may be abstracted into a function/method called\n // _registerHandlers for clarity and easing the maintenance of the two\n // pieces of source code.\n if (this.stream) {\n for (const type of this.handlers.keys()) {\n this._setHandler(type, this.handlers.get(type));\n }\n if (this._streamInactiveHandler) {\n this._addMediaStreamInactiveHandler(this._streamInactiveHandler);\n }\n }\n }\n\n /**\n * Returns the video type (camera or desktop) of this track.\n */\n getVideoType() {\n return this.videoType;\n }\n\n /**\n * Returns the type (audio or video) of this track.\n */\n getType() {\n return this.type;\n }\n\n /**\n * Check if this is an audio track.\n */\n isAudioTrack() {\n return this.getType() === MediaType.AUDIO;\n }\n\n /**\n * Checks whether the underlying WebRTC MediaStreamTrack is muted\n * according to it's 'muted' field status.\n * @return {boolean} true if the underlying\n * MediaStreamTrack is muted or false otherwise.\n */\n isWebRTCTrackMuted() {\n return this.track && this.track.muted;\n }\n\n /**\n * Check if this is a video track.\n */\n isVideoTrack() {\n return this.getType() === MediaType.VIDEO;\n }\n\n /**\n * Checks whether this is a local track.\n * @abstract\n * @return {boolean} 'true' if it's a local track or 'false' otherwise.\n */\n isLocal() {\n throw new Error('Not implemented by subclass');\n }\n\n /**\n * Check whether this is a local audio track.\n *\n * @return {boolean} - true if track represents a local audio track, false otherwise.\n */\n isLocalAudioTrack() {\n return this.isAudioTrack() && this.isLocal();\n }\n\n /**\n * Returns the WebRTC MediaStream instance.\n */\n getOriginalStream() {\n return this.stream;\n }\n\n /**\n * Returns the ID of the underlying WebRTC Media Stream(if any)\n * @returns {String|null}\n */\n getStreamId() {\n return this.stream ? this.stream.id : null;\n }\n\n /**\n * Return the underlying WebRTC MediaStreamTrack\n * @returns {MediaStreamTrack}\n */\n getTrack() {\n return this.track;\n }\n\n /**\n * Return the underlying WebRTC MediaStreamTrack label\n * @returns {string}\n */\n getTrackLabel() {\n return this.track.label;\n }\n\n /**\n * Returns the ID of the underlying WebRTC MediaStreamTrack(if any)\n * @returns {String|null}\n */\n getTrackId() {\n return this.track ? this.track.id : null;\n }\n\n /**\n * Return meaningful usage label for this track depending on it's media and\n * eventual video type.\n * @returns {string}\n */\n getUsageLabel() {\n if (this.isAudioTrack()) {\n return 'mic';\n }\n\n return this.videoType ? this.videoType : 'default';\n }\n\n /**\n * Eventually will trigger RTCEvents.TRACK_ATTACHED event.\n * @param container the video/audio container to which this stream is\n * attached and for which event will be fired.\n * @private\n */\n _maybeFireTrackAttached(container) {\n if (this.conference && container) {\n this.conference._onTrackAttach(this, container);\n }\n }\n\n /**\n * Attaches the MediaStream of this track to an HTML container.\n * Adds the container to the list of containers that are displaying the\n * track.\n *\n * @param container the HTML container which can be 'video' or 'audio'\n * element.\n *\n * @returns {void}\n */\n attach(container) {\n if (this.stream) {\n this._onTrackAttach(container);\n RTCUtils.attachMediaStream(container, this.stream);\n }\n this.containers.push(container);\n this._maybeFireTrackAttached(container);\n this._attachTTFMTracker(container);\n }\n\n /**\n * Removes this JitsiTrack from the passed HTML container.\n *\n * @param container the HTML container to detach from this JitsiTrack. If\n * null or undefined, all containers are removed. A\n * container can be a 'video', 'audio' or 'object' HTML element instance to\n * which this JitsiTrack is currently attached.\n */\n detach(container) {\n for (let cs = this.containers, i = cs.length - 1; i >= 0; --i) {\n const c = cs[i];\n\n if (!container) {\n this._onTrackDetach(c);\n RTCUtils.attachMediaStream(c, null);\n }\n if (!container || c === container) {\n cs.splice(i, 1);\n }\n }\n\n if (container) {\n this._onTrackDetach(container);\n RTCUtils.attachMediaStream(container, null);\n }\n }\n\n /**\n * Called when the track has been attached to a new container.\n *\n * @param {HTMLElement} container the HTML container which can be 'video' or\n * 'audio' element.\n * @private\n */\n _onTrackAttach(container) { // eslint-disable-line no-unused-vars\n // Should be defined by the classes that are extending JitsiTrack\n }\n\n /**\n * Called when the track has been detached from a container.\n *\n * @param {HTMLElement} container the HTML container which can be 'video' or\n * 'audio' element.\n * @private\n */\n _onTrackDetach(container) { // eslint-disable-line no-unused-vars\n // Should be defined by the classes that are extending JitsiTrack\n }\n\n /**\n * Attach time to first media tracker only if there is conference and only\n * for the first element.\n *\n * @param {HTMLElement} container the HTML container which can be 'video' or\n * 'audio' element.\n * @private\n */\n _attachTTFMTracker(container) { // eslint-disable-line no-unused-vars\n // Should be defined by the classes that are extending JitsiTrack\n }\n\n /**\n * Removes attached event listeners.\n *\n * @returns {Promise}\n */\n dispose() {\n this.removeAllListeners();\n\n this.disposed = true;\n\n return Promise.resolve();\n }\n\n /**\n * Returns true if this is a video track and the source of the video is a\n * screen capture as opposed to a camera.\n */\n isScreenSharing() {\n // FIXME: Should be fixed or removed.\n }\n\n /**\n * Returns id of the track.\n * @returns {string|null} id of the track or null if this is fake track.\n */\n getId() {\n if (this.stream) {\n return RTCUtils.getStreamID(this.stream);\n }\n\n return null;\n }\n\n /**\n * Checks whether the MediaStream is active/not ended.\n * When there is no check for active we don't have information and so\n * will return that stream is active (in case of FF).\n * @returns {boolean} whether MediaStream is active.\n */\n isActive() {\n if (typeof this.stream.active !== 'undefined') {\n return this.stream.active;\n }\n\n return true;\n }\n\n /**\n * Sets the audio level for the stream\n * @param {number} audioLevel value between 0 and 1\n * @param {TraceablePeerConnection} [tpc] the peerconnection instance which\n * is source for the audio level. It can be undefined for\n * a local track if the audio level was measured outside of the\n * peerconnection (see /modules/statistics/LocalStatsCollector.js).\n */\n setAudioLevel(audioLevel, tpc) {\n let newAudioLevel = audioLevel;\n\n // When using getSynchornizationSources on the audio receiver to gather audio levels for\n // remote tracks, browser reports last known audio levels even when the remote user is\n // audio muted, we need to reset the value to zero here so that the audio levels are cleared.\n // Remote tracks have the tpc info present while local tracks do not.\n if (browser.supportsReceiverStats() && typeof tpc !== 'undefined' && this.isMuted()) {\n newAudioLevel = 0;\n }\n\n if (this.audioLevel !== newAudioLevel) {\n this.audioLevel = newAudioLevel;\n this.emit(\n JitsiTrackEvents.TRACK_AUDIO_LEVEL_CHANGED,\n newAudioLevel,\n tpc);\n\n // LocalStatsCollector reports a value of 0.008 for muted mics\n // and a value of 0 when there is no audio input.\n } else if (this.audioLevel === 0\n && newAudioLevel === 0\n && this.isLocal()\n && !this.isWebRTCTrackMuted()) {\n this.emit(\n JitsiTrackEvents.NO_AUDIO_INPUT,\n newAudioLevel);\n }\n }\n\n /**\n * Returns the msid of the stream attached to the JitsiTrack object or null\n * if no stream is attached.\n */\n getMSID() {\n const streamId = this.getStreamId();\n const trackId = this.getTrackId();\n\n return streamId && trackId ? `${streamId} ${trackId}` : null;\n }\n\n /**\n * Sets new audio output device for track's DOM elements. Video tracks are\n * ignored.\n * @param {string} audioOutputDeviceId - id of 'audiooutput' device from\n * navigator.mediaDevices.enumerateDevices(), '' for default device\n * @emits JitsiTrackEvents.TRACK_AUDIO_OUTPUT_CHANGED\n * @returns {Promise}\n */\n setAudioOutput(audioOutputDeviceId) {\n if (!RTCUtils.isDeviceChangeAvailable('output')) {\n return Promise.reject(\n new Error('Audio output device change is not supported'));\n }\n\n // All audio communication is done through audio tracks, so ignore\n // changing audio output for video tracks at all.\n if (this.isVideoTrack()) {\n return Promise.resolve();\n }\n\n return (\n Promise.all(\n this.containers.map(\n element =>\n element.setSinkId(audioOutputDeviceId)\n .catch(error => {\n logger.warn(\n 'Failed to change audio output device on'\n + ' element. Default or previously set'\n + ' audio output device will be used.',\n element,\n error);\n throw error;\n }))\n )\n .then(() => {\n this.emit(\n JitsiTrackEvents.TRACK_AUDIO_OUTPUT_CHANGED,\n audioOutputDeviceId);\n }));\n }\n}\n","import { getLogger } from '@jitsi/logger';\n\nimport JitsiTrackError from '../../JitsiTrackError';\nimport {\n TRACK_IS_DISPOSED,\n TRACK_NO_STREAM_FOUND\n} from '../../JitsiTrackErrors';\nimport {\n LOCAL_TRACK_STOPPED,\n NO_DATA_FROM_SOURCE,\n TRACK_MUTE_CHANGED\n} from '../../JitsiTrackEvents';\nimport CameraFacingMode from '../../service/RTC/CameraFacingMode';\nimport * as MediaType from '../../service/RTC/MediaType';\nimport RTCEvents from '../../service/RTC/RTCEvents';\nimport VideoType from '../../service/RTC/VideoType';\nimport {\n NO_BYTES_SENT,\n TRACK_UNMUTED,\n createNoDataFromSourceEvent\n} from '../../service/statistics/AnalyticsEvents';\nimport browser from '../browser';\nimport Statistics from '../statistics/statistics';\n\nimport JitsiTrack from './JitsiTrack';\nimport RTCUtils from './RTCUtils';\n\nconst logger = getLogger(__filename);\n\n/**\n * Represents a single media track(either audio or video).\n * One JitsiLocalTrack corresponds to one WebRTC MediaStreamTrack.\n */\nexport default class JitsiLocalTrack extends JitsiTrack {\n /**\n * Constructs a new JitsiLocalTrack instance.\n *\n * @constructor\n * @param {Object} trackInfo\n * @param {number} trackInfo.rtcId - The ID assigned by the RTC module.\n * @param {Object} trackInfo.stream - The WebRTC MediaStream, parent of the track.\n * @param {Object} trackInfo.track - The underlying WebRTC MediaStreamTrack for new JitsiLocalTrack.\n * @param {string} trackInfo.mediaType - The MediaType of the JitsiLocalTrack.\n * @param {string} trackInfo.videoType - The VideoType of the JitsiLocalTrack.\n * @param {Array} trackInfo.effects - The effects to be applied to the JitsiLocalTrack.\n * @param {number} trackInfo.resolution - The the video resolution if it's a video track\n * @param {string} trackInfo.deviceId - The ID of the local device for this track.\n * @param {string} trackInfo.facingMode - Thehe camera facing mode used in getUserMedia call (for mobile only).\n * @param {sourceId} trackInfo.sourceId - The id of the desktop sharing source. NOTE: defined for desktop sharing\n * tracks only.\n */\n constructor({\n deviceId,\n facingMode,\n mediaType,\n resolution,\n rtcId,\n sourceId,\n sourceType,\n stream,\n track,\n videoType,\n effects = []\n }) {\n super(\n /* conference */ null,\n stream,\n track,\n /* streamInactiveHandler */ () => this.emit(LOCAL_TRACK_STOPPED),\n mediaType,\n videoType);\n\n this._setEffectInProgress = false;\n const effect = effects.find(e => e.isEnabled(this));\n\n if (effect) {\n this._startStreamEffect(effect);\n }\n\n const displaySurface = videoType === VideoType.DESKTOP\n ? track.getSettings().displaySurface\n : null;\n\n /**\n * Track metadata.\n */\n this.metadata = {\n timestamp: Date.now(),\n ...displaySurface ? { displaySurface } : {}\n };\n\n\n /**\n * The ID assigned by the RTC module on instance creation.\n *\n * @type {number}\n */\n this.rtcId = rtcId;\n this.sourceId = sourceId;\n this.sourceType = sourceType;\n\n // Get the resolution from the track itself because it cannot be\n // certain which resolution webrtc has fallen back to using.\n this.resolution = track.getSettings().height;\n this.maxEnabledResolution = resolution;\n\n // Cache the constraints of the track in case of any this track\n // model needs to call getUserMedia again, such as when unmuting.\n this._constraints = track.getConstraints();\n\n // Safari returns an empty constraints object, construct the constraints using getSettings.\n if (!Object.keys(this._constraints).length && videoType === VideoType.CAMERA) {\n this._constraints = {\n height: track.getSettings().height,\n width: track.getSettings().width\n };\n }\n\n this.deviceId = deviceId;\n\n /**\n * The Promise which represents the progress of a previously\n * queued/scheduled {@link _setMuted} (from the point of view of\n * {@link _queueSetMuted}).\n *\n * @private\n * @type {Promise}\n */\n this._prevSetMuted = Promise.resolve();\n\n /**\n * The facing mode of the camera from which this JitsiLocalTrack\n * instance was obtained.\n *\n * @private\n * @type {CameraFacingMode|undefined}\n */\n this._facingMode = facingMode;\n\n // Currently there is no way to know the MediaStreamTrack ended due to\n // to device disconnect in Firefox through e.g. \"readyState\" property.\n // Instead we will compare current track's label with device labels from\n // enumerateDevices() list.\n this._trackEnded = false;\n\n /**\n * Indicates whether data has been sent or not.\n */\n this._hasSentData = false;\n\n /**\n * Used only for detection of audio problems. We want to check only once\n * whether the track is sending data ot not. This flag is set to false\n * after the check.\n */\n this._testDataSent = true;\n\n // Currently there is no way to determine with what device track was\n // created (until getConstraints() support), however we can associate\n // tracks with real devices obtained from enumerateDevices() call as\n // soon as it's called.\n // NOTE: this.deviceId corresponds to the device id specified in GUM constraints and this._realDeviceId seems to\n // correspond to the id of a matching device from the available device list.\n this._realDeviceId = this.deviceId === '' ? undefined : this.deviceId;\n\n // The source name that will be signaled for this track.\n this._sourceName = null;\n\n this._trackMutedTS = 0;\n\n this._onDeviceListWillChange = devices => {\n const oldRealDeviceId = this._realDeviceId;\n\n this._setRealDeviceIdFromDeviceList(devices);\n\n if (\n // Mark track as ended for those browsers that do not support\n // \"readyState\" property. We do not touch tracks created with\n // default device ID \"\".\n (typeof this.getTrack().readyState === 'undefined'\n && typeof this._realDeviceId !== 'undefined'\n && !devices.find(d => d.deviceId === this._realDeviceId))\n\n // If there was an associated realDeviceID and after the device change the realDeviceId is undefined\n // then the associated device has been disconnected and the _trackEnded flag needs to be set. In\n // addition on some Chrome versions the readyState property is set after the device change event is\n // triggered which causes issues in jitsi-meet with the selection of a new device because we don't\n // detect that the old one was removed.\n || (typeof oldRealDeviceId !== 'undefined' && typeof this._realDeviceId === 'undefined')\n ) {\n this._trackEnded = true;\n }\n };\n\n // Subscribe each created local audio track to\n // RTCEvents.AUDIO_OUTPUT_DEVICE_CHANGED event. This is different from\n // handling this event for remote tracks (which are handled in RTC.js),\n // because there might be local tracks not attached to a conference.\n if (this.isAudioTrack() && RTCUtils.isDeviceChangeAvailable('output')) {\n this._onAudioOutputDeviceChanged = this.setAudioOutput.bind(this);\n RTCUtils.addListener(\n RTCEvents.AUDIO_OUTPUT_DEVICE_CHANGED,\n this._onAudioOutputDeviceChanged);\n }\n\n RTCUtils.addListener(RTCEvents.DEVICE_LIST_WILL_CHANGE, this._onDeviceListWillChange);\n\n this._initNoDataFromSourceHandlers();\n }\n\n /**\n * Adds stream to conference and marks it as \"unmute\" operation.\n *\n * @private\n * @returns {Promise}\n */\n _addStreamToConferenceAsUnmute() {\n if (!this.conference) {\n return Promise.resolve();\n }\n\n // FIXME it would be good to not included conference as part of this process. Only TraceablePeerConnections to\n // which the track is attached should care about this action. The TPCs to which the track is not attached can\n // sync up when track is re-attached. A problem with that is that the \"modify sources\" queue is part of the\n // JingleSessionPC and it would be excluded from the process. One solution would be to extract class between\n // TPC and JingleSessionPC which would contain the queue and would notify the signaling layer when local SSRCs\n // are changed. This would help to separate XMPP from the RTC module.\n return new Promise((resolve, reject) => {\n this.conference._addLocalTrackAsUnmute(this)\n .then(resolve, error => reject(new Error(error)));\n });\n }\n\n /**\n * Fires NO_DATA_FROM_SOURCE event and logs it to analytics and callstats.\n *\n * @private\n * @returns {void}\n */\n _fireNoDataFromSourceEvent() {\n const value = !this.isReceivingData();\n\n this.emit(NO_DATA_FROM_SOURCE, value);\n\n // FIXME: Should we report all of those events\n Statistics.sendAnalytics(createNoDataFromSourceEvent(this.getType(), value));\n Statistics.sendLog(JSON.stringify({\n name: NO_DATA_FROM_SOURCE,\n log: value\n }));\n }\n\n /**\n * Sets handlers to the MediaStreamTrack object that will detect camera issues.\n *\n * @private\n * @returns {void}\n */\n _initNoDataFromSourceHandlers() {\n if (!this._isNoDataFromSourceEventsEnabled()) {\n return;\n }\n\n this._setHandler('track_mute', () => {\n this._trackMutedTS = window.performance.now();\n this._fireNoDataFromSourceEvent();\n });\n\n this._setHandler('track_unmute', () => {\n this._fireNoDataFromSourceEvent();\n Statistics.sendAnalyticsAndLog(\n TRACK_UNMUTED,\n {\n 'media_type': this.getType(),\n 'track_type': 'local',\n value: window.performance.now() - this._trackMutedTS\n });\n });\n\n if (this.isVideoTrack() && this.videoType === VideoType.CAMERA) {\n this._setHandler('track_ended', () => {\n if (!this.isReceivingData()) {\n this._fireNoDataFromSourceEvent();\n }\n });\n }\n }\n\n /**\n * Returns true if no data from source events are enabled for this JitsiLocalTrack and false otherwise.\n *\n * @private\n * @returns {boolean} - True if no data from source events are enabled for this JitsiLocalTrack and false otherwise.\n */\n _isNoDataFromSourceEventsEnabled() {\n // Disable the events for screen sharing.\n return !this.isVideoTrack() || this.videoType !== VideoType.DESKTOP;\n }\n\n /**\n * Initializes a new Promise to execute {@link #_setMuted}. May be called multiple times in a row and the\n * invocations of {@link #_setMuted} and, consequently, {@link #mute} and/or {@link #unmute} will be resolved in a\n * serialized fashion.\n *\n * @param {boolean} muted - The value to invoke _setMuted with.\n * @private\n * @returns {Promise}\n */\n _queueSetMuted(muted) {\n const setMuted = this._setMuted.bind(this, muted);\n\n this._prevSetMuted = this._prevSetMuted.then(setMuted, setMuted);\n\n return this._prevSetMuted;\n }\n\n /**\n * Removes stream from conference and marks it as \"mute\" operation.\n *\n * @param {Function} successCallback - Callback that will be called when the operation is successful.\n * @param {Function} errorCallback - Callback that will be called when the operation fails.\n * @private\n * @returns {Promise}\n */\n _removeStreamFromConferenceAsMute(successCallback, errorCallback) {\n if (!this.conference) {\n successCallback();\n\n return;\n }\n this.conference._removeLocalTrackAsMute(this).then(\n successCallback,\n error => errorCallback(new Error(error)));\n }\n\n /**\n * Sends mute status for a track to conference if any.\n *\n * @param {boolean} mute - If track is muted.\n * @private\n * @returns {void}\n */\n _sendMuteStatus(mute) {\n if (this.conference) {\n this.conference._setTrackMuteStatus(this.getType(), this, mute) && this.conference.room.sendPresence();\n }\n }\n\n /**\n * Mutes / unmutes this track.\n *\n * @param {boolean} muted - If true, this track will be muted; otherwise, this track will be unmuted.\n * @private\n * @returns {Promise}\n */\n _setMuted(muted) {\n if (this.isMuted() === muted) {\n return Promise.resolve();\n }\n\n if (this.disposed) {\n return Promise.reject(new JitsiTrackError(TRACK_IS_DISPOSED));\n }\n\n let promise = Promise.resolve();\n\n // A function that will print info about muted status transition\n const logMuteInfo = () => logger.info(`Mute ${this}: ${muted}`);\n\n if (this.isAudioTrack()\n || this.videoType === VideoType.DESKTOP\n || !browser.doesVideoMuteByStreamRemove()) {\n logMuteInfo();\n\n // If we have a stream effect that implements its own mute functionality, prioritize it before\n // normal mute e.g. the stream effect that implements system audio sharing has a custom\n // mute state in which if the user mutes, system audio still has to go through.\n if (this._streamEffect && this._streamEffect.setMuted) {\n this._streamEffect.setMuted(muted);\n } else if (this.track) {\n this.track.enabled = !muted;\n }\n } else if (muted) {\n promise = new Promise((resolve, reject) => {\n logMuteInfo();\n this._removeStreamFromConferenceAsMute(\n () => {\n if (this._streamEffect) {\n this._stopStreamEffect();\n }\n\n // FIXME: Maybe here we should set the SRC for the\n // containers to something\n // We don't want any events to be fired on this stream\n this._unregisterHandlers();\n this.stopStream();\n this._setStream(null);\n\n resolve();\n },\n reject);\n });\n } else {\n logMuteInfo();\n\n // This path is only for camera.\n const streamOptions = {\n cameraDeviceId: this.getDeviceId(),\n devices: [ MediaType.VIDEO ],\n effects: this._streamEffect ? [ this._streamEffect ] : [],\n facingMode: this.getCameraFacingMode()\n };\n\n promise\n = RTCUtils.obtainAudioAndVideoPermissions(Object.assign(\n {},\n streamOptions,\n { constraints: { video: this._constraints } }));\n\n promise = promise.then(streamsInfo => {\n // The track kind for presenter track is video as well.\n const mediaType = this.getType() === MediaType.PRESENTER ? MediaType.VIDEO : this.getType();\n const streamInfo = streamsInfo.find(info => info.track.kind === mediaType);\n\n if (streamInfo) {\n this._setStream(streamInfo.stream);\n this.track = streamInfo.track;\n\n // This is not good when video type changes after\n // unmute, but let's not crash here\n if (this.videoType !== streamInfo.videoType) {\n logger.warn(\n `${this}: video type has changed after unmute!`,\n this.videoType, streamInfo.videoType);\n this.videoType = streamInfo.videoType;\n }\n } else {\n throw new JitsiTrackError(TRACK_NO_STREAM_FOUND);\n }\n\n if (this._streamEffect) {\n this._startStreamEffect(this._streamEffect);\n }\n\n this.containers.map(\n cont => RTCUtils.attachMediaStream(cont, this.stream));\n\n return this._addStreamToConferenceAsUnmute();\n });\n }\n\n return promise\n .then(() => {\n this._sendMuteStatus(muted);\n\n // Send the videoType message to the bridge.\n this.isVideoTrack() && this.conference && this.conference._sendBridgeVideoTypeMessage(this);\n this.emit(TRACK_MUTE_CHANGED, this);\n });\n }\n\n /**\n * Sets real device ID by comparing track information with device information. This is temporary solution until\n * getConstraints() method will be implemented in browsers.\n *\n * @param {MediaDeviceInfo[]} devices - The list of devices obtained from enumerateDevices() call.\n * @private\n * @returns {void}\n */\n _setRealDeviceIdFromDeviceList(devices) {\n const track = this.getTrack();\n const kind = `${track.kind}input`;\n\n // We need to match by deviceId as well, in case of multiple devices with the same label.\n let device = devices.find(d => d.kind === kind && d.label === track.label && d.deviceId === this.deviceId);\n\n if (!device && this._realDeviceId === 'default') { // the default device has been changed.\n // If the default device was 'A' and the default device is changed to 'B' the label for the track will\n // remain 'Default - A' but the label for the device in the device list will be updated to 'A'. That's\n // why in order to match it we need to remove the 'Default - ' part.\n const label = (track.label || '').replace('Default - ', '');\n\n device = devices.find(d => d.kind === kind && d.label === label);\n }\n\n if (device) {\n this._realDeviceId = device.deviceId;\n } else {\n this._realDeviceId = undefined;\n }\n }\n\n /**\n * Sets the stream property of JitsiLocalTrack object and sets all stored handlers to it.\n *\n * @param {MediaStream} stream - The new MediaStream.\n * @private\n * @returns {void}\n */\n _setStream(stream) {\n super._setStream(stream);\n\n if (stream) {\n // Store the MSID for video mute/unmute purposes.\n this.storedMSID = this.getMSID();\n logger.debug(`Setting new MSID: ${this.storedMSID} on ${this}`);\n } else {\n logger.debug(`Setting 'null' stream on ${this}`);\n }\n }\n\n /**\n * Starts the effect process and returns the modified stream.\n *\n * @param {Object} effect - Represents effect instance\n * @private\n * @returns {void}\n */\n _startStreamEffect(effect) {\n this._streamEffect = effect;\n this._originalStream = this.stream;\n this._setStream(this._streamEffect.startEffect(this._originalStream));\n this.track = this.stream.getTracks()[0];\n }\n\n /**\n * Stops the effect process and returns the original stream.\n *\n * @private\n * @returns {void}\n */\n _stopStreamEffect() {\n if (this._streamEffect) {\n this._streamEffect.stopEffect();\n this._setStream(this._originalStream);\n this._originalStream = null;\n this.track = this.stream ? this.stream.getTracks()[0] : null;\n }\n }\n\n /**\n * Switches the camera facing mode if the WebRTC implementation supports the custom MediaStreamTrack._switchCamera\n * method. Currently, the method in question is implemented in react-native-webrtc only. When such a WebRTC\n * implementation is executing, the method is the preferred way to switch between the front/user-facing and the\n * back/environment-facing cameras because it will likely be (as is the case of react-native-webrtc) noticeably\n * faster that creating a new MediaStreamTrack via a new getUserMedia call with the switched facingMode constraint\n * value. Moreover, the approach with a new getUserMedia call may not even work: WebRTC on Android and iOS is\n * either very slow to open the camera a second time or plainly freezes attempting to do that.\n *\n * @returns {void}\n */\n _switchCamera() {\n if (this.isVideoTrack()\n && this.videoType === VideoType.CAMERA\n && typeof this.track._switchCamera === 'function') {\n this.track._switchCamera();\n\n this._facingMode\n = this._facingMode === CameraFacingMode.ENVIRONMENT\n ? CameraFacingMode.USER\n : CameraFacingMode.ENVIRONMENT;\n }\n }\n\n /**\n * Stops the currently used effect (if there is one) and starts the passed effect (if there is one).\n *\n * @param {Object|undefined} effect - The new effect to be set.\n * @private\n * @returns {void}\n */\n _switchStreamEffect(effect) {\n if (this._streamEffect) {\n this._stopStreamEffect();\n this._streamEffect = undefined;\n }\n if (effect) {\n this._startStreamEffect(effect);\n }\n }\n\n /**\n * @inheritdoc\n *\n * Stops sending the media track. And removes it from the HTML. NOTE: Works for local tracks only.\n *\n * @extends JitsiTrack#dispose\n * @returns {Promise}\n */\n dispose() {\n let promise = Promise.resolve();\n\n // Remove the effect instead of stopping it so that the original stream is restored\n // on both the local track and on the peerconnection.\n if (this._streamEffect) {\n promise = this.setEffect();\n }\n\n if (this.conference) {\n promise = promise.then(() => this.conference.removeTrack(this));\n }\n\n if (this.stream) {\n this.stopStream();\n this.detach();\n }\n\n RTCUtils.removeListener(RTCEvents.DEVICE_LIST_WILL_CHANGE, this._onDeviceListWillChange);\n\n if (this._onAudioOutputDeviceChanged) {\n RTCUtils.removeListener(RTCEvents.AUDIO_OUTPUT_DEVICE_CHANGED,\n this._onAudioOutputDeviceChanged);\n }\n\n return promise.then(() => super.dispose());\n }\n\n /**\n * Returns facing mode for video track from camera. For other cases (e.g. audio track or 'desktop' video track)\n * returns undefined.\n *\n * @returns {CameraFacingMode|undefined}\n */\n getCameraFacingMode() {\n if (this.isVideoTrack() && this.videoType === VideoType.CAMERA) {\n // MediaStreamTrack#getSettings() is not implemented in many\n // browsers, so we need feature checking here. Progress on the\n // respective browser's implementation can be tracked at\n // https://bugs.chromium.org/p/webrtc/issues/detail?id=2481 for\n // Chromium and https://bugzilla.mozilla.org/show_bug.cgi?id=1213517\n // for Firefox. Even if a browser implements getSettings() already,\n // it might still not return anything for 'facingMode'.\n const trackSettings = this.track.getSettings?.();\n\n if (trackSettings && 'facingMode' in trackSettings) {\n return trackSettings.facingMode;\n }\n\n if (typeof this._facingMode !== 'undefined') {\n return this._facingMode;\n }\n\n // In most cases we are showing a webcam. So if we've gotten here,\n // it should be relatively safe to assume that we are probably\n // showing the user-facing camera.\n return CameraFacingMode.USER;\n }\n\n return undefined;\n }\n\n /**\n * Returns device id associated with track.\n *\n * @returns {string}\n */\n getDeviceId() {\n return this._realDeviceId || this.deviceId;\n }\n\n /**\n * Get the duration of the track.\n *\n * @returns {Number} the duration of the track in seconds\n */\n getDuration() {\n return (Date.now() / 1000) - (this.metadata.timestamp / 1000);\n }\n\n /**\n * Returns the participant id which owns the track.\n *\n * @returns {string} the id of the participants. It corresponds to the\n * Colibri endpoint id/MUC nickname in case of Jitsi-meet.\n */\n getParticipantId() {\n return this.conference && this.conference.myUserId();\n }\n\n /**\n * Returns the source name associated with the jitsi track.\n *\n * @returns {string | null} source name\n */\n getSourceName() {\n return this._sourceName;\n }\n\n /**\n * Returns if associated MediaStreamTrack is in the 'ended' state\n *\n * @returns {boolean}\n */\n isEnded() {\n if (this.isVideoTrack() && this.isMuted()) {\n // If a video track is muted the readyState will be ended, that's why we need to rely only on the\n // _trackEnded flag.\n return this._trackEnded;\n }\n\n return this.getTrack().readyState === 'ended' || this._trackEnded;\n }\n\n /**\n * Returns true.\n *\n * @returns {boolean} true\n */\n isLocal() {\n return true;\n }\n\n /**\n * Returns true - if the stream is muted and false otherwise.\n *\n * @returns {boolean} true - if the stream is muted and false otherwise.\n */\n isMuted() {\n // this.stream will be null when we mute local video on Chrome\n if (!this.stream) {\n return true;\n }\n if (this.isVideoTrack() && !this.isActive()) {\n return true;\n }\n\n // If currently used stream effect has its own muted state, use that.\n if (this._streamEffect && this._streamEffect.isMuted) {\n return this._streamEffect.isMuted();\n }\n\n return !this.track || !this.track.enabled;\n }\n\n /**\n * Checks whether the attached MediaStream is receiving data from source or not. If the stream property is null\n * (because of mute or another reason) this method will return false.\n * NOTE: This method doesn't indicate problem with the streams directly. For example in case of video mute the\n * method will return false or if the user has disposed the track.\n *\n * @returns {boolean} true if the stream is receiving data and false this otherwise.\n */\n isReceivingData() {\n if (this.isVideoTrack()\n && (this.isMuted() || this._stopStreamInProgress || this.videoType === VideoType.DESKTOP)) {\n return true;\n }\n\n if (!this.stream) {\n return false;\n }\n\n // In older version of the spec there is no muted property and readyState can have value muted. In the latest\n // versions readyState can have values \"live\" and \"ended\" and there is muted boolean property. If the stream is\n // muted that means that we aren't receiving any data from the source. We want to notify the users for error if\n // the stream is muted or ended on it's creation.\n\n // For video blur enabled use the original video stream\n const stream = this._effectEnabled ? this._originalStream : this.stream;\n\n return stream.getTracks().some(track =>\n (!('readyState' in track) || track.readyState === 'live')\n && (!('muted' in track) || track.muted !== true));\n }\n\n /**\n * Asynchronously mutes this track.\n *\n * @returns {Promise}\n */\n mute() {\n return this._queueSetMuted(true);\n }\n\n /**\n * Handles bytes sent statistics. NOTE: used only for audio tracks to detect audio issues.\n *\n * @param {TraceablePeerConnection} tpc - The peerconnection that is reporting the bytes sent stat.\n * @param {number} bytesSent - The new value.\n * @returns {void}\n */\n onByteSentStatsReceived(tpc, bytesSent) {\n if (bytesSent > 0) {\n this._hasSentData = true;\n }\n const iceConnectionState = tpc.getConnectionState();\n\n if (this._testDataSent && iceConnectionState === 'connected') {\n setTimeout(() => {\n if (!this._hasSentData) {\n logger.warn(`${this} 'bytes sent' <= 0: \\\n ${bytesSent}`);\n\n Statistics.analytics.sendEvent(NO_BYTES_SENT, { 'media_type': this.getType() });\n }\n }, 3000);\n this._testDataSent = false;\n }\n }\n\n /**\n * Sets the JitsiConference object associated with the track. This is temp solution.\n *\n * @param conference - JitsiConference object.\n * @returns {void}\n */\n setConference(conference) {\n this.conference = conference;\n\n // We want to keep up with postponed events which should have been fired\n // on \"attach\" call, but for local track we not always have the\n // conference before attaching. However this may result in duplicated\n // events if they have been triggered on \"attach\" already.\n for (let i = 0; i < this.containers.length; i++) {\n this._maybeFireTrackAttached(this.containers[i]);\n }\n }\n\n /**\n * Sets the effect and switches between the modified stream and original one.\n *\n * @param {Object} effect - Represents the effect instance to be used.\n * @returns {Promise}\n */\n setEffect(effect) {\n if (typeof this._streamEffect === 'undefined' && typeof effect === 'undefined') {\n return Promise.resolve();\n }\n\n if (typeof effect !== 'undefined' && !effect.isEnabled(this)) {\n return Promise.reject(new Error('Incompatible effect instance!'));\n }\n\n if (this._setEffectInProgress === true) {\n return Promise.reject(new Error('setEffect already in progress!'));\n }\n\n // In case we have an audio track that is being enhanced with an effect, we still want it to be applied,\n // even if the track is muted. Where as for video the actual track doesn't exists if it's muted.\n if (this.isMuted() && !this.isAudioTrack()) {\n this._streamEffect = effect;\n\n return Promise.resolve();\n }\n\n const conference = this.conference;\n\n if (!conference) {\n this._switchStreamEffect(effect);\n if (this.isVideoTrack()) {\n this.containers.forEach(cont => RTCUtils.attachMediaStream(cont, this.stream));\n }\n\n return Promise.resolve();\n }\n\n this._setEffectInProgress = true;\n\n // TODO: Create new JingleSessionPC method for replacing a stream in JitsiLocalTrack without offer answer.\n return conference.removeTrack(this)\n .then(() => {\n this._switchStreamEffect(effect);\n if (this.isVideoTrack()) {\n this.containers.forEach(cont => RTCUtils.attachMediaStream(cont, this.stream));\n }\n\n return conference.addTrack(this);\n })\n .then(() => {\n this._setEffectInProgress = false;\n })\n .catch(error => {\n // Any error will be not recovarable and will trigger CONFERENCE_FAILED event. But let's try to cleanup\n // everyhting related to the effect functionality.\n this._setEffectInProgress = false;\n this._switchStreamEffect();\n logger.error('Failed to switch to the new stream!', error);\n throw error;\n });\n }\n\n /**\n * Sets the source name to be used for signaling the jitsi track.\n *\n * @param {string} name The source name.\n */\n setSourceName(name) {\n this._sourceName = name;\n }\n\n /**\n * Stops the associated MediaStream.\n *\n * @returns {void}\n */\n stopStream() {\n /**\n * Indicates that we are executing {@link #stopStream} i.e.\n * {@link RTCUtils#stopMediaStream} for the MediaStream\n * associated with this JitsiTrack instance.\n *\n * @private\n * @type {boolean}\n */\n this._stopStreamInProgress = true;\n\n try {\n RTCUtils.stopMediaStream(this.stream);\n } finally {\n this._stopStreamInProgress = false;\n }\n }\n\n /**\n * Creates a text representation of this local track instance.\n *\n * @return {string}\n */\n toString() {\n return `LocalTrack[${this.rtcId},${this.getType()}]`;\n }\n\n /**\n * Asynchronously unmutes this track.\n *\n * @returns {Promise}\n */\n unmute() {\n return this._queueSetMuted(false);\n }\n}\n","/* Copyright @ 2015 - Present, 8x8 Inc\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport transform from 'sdp-transform';\n\n/**\n * Rewrites the source information in the way sdp-transform expects.\n * Source information is split into multiple ssrc objects each containing\n * an id, attribute and value.\n * @param {Object} media - media description to be modified.\n * @returns {void}\n */\nconst write = function(session, opts) {\n if (typeof session !== 'undefined' && typeof session.media !== 'undefined' && Array.isArray(session.media)) {\n session.media.forEach(mLine => {\n if (mLine.sources && mLine.sources.length) {\n mLine.ssrcs = [];\n mLine.sources.forEach(source => {\n Object.keys(source).forEach(attribute => {\n if (attribute === 'id') {\n return;\n }\n mLine.ssrcs.push({\n id: source.id,\n attribute,\n value: source[attribute]\n });\n });\n });\n delete mLine.sources;\n }\n\n // join ssrcs in ssrc groups\n if (mLine.ssrcGroups && mLine.ssrcGroups.length) {\n mLine.ssrcGroups.forEach(ssrcGroup => {\n if (typeof ssrcGroup.ssrcs !== 'undefined'\n && Array.isArray(ssrcGroup.ssrcs)) {\n ssrcGroup.ssrcs = ssrcGroup.ssrcs.join(' ');\n }\n });\n }\n });\n }\n\n return transform.write(session, opts);\n};\n\n/**\n * Rewrites the source information that we get from sdp-transform.\n * All the ssrc lines with different attributes that belong to the\n * same ssrc are grouped into a single soure object with multiple key value pairs.\n * @param {Object} media - media description to be modified.\n * @returns {void}\n */\nconst parse = function(sdp) {\n const session = transform.parse(sdp);\n\n if (typeof session !== 'undefined' && typeof session.media !== 'undefined' && Array.isArray(session.media)) {\n session.media.forEach(mLine => {\n // group sources attributes by ssrc\n if (typeof mLine.ssrcs !== 'undefined' && Array.isArray(mLine.ssrcs)) {\n mLine.sources = [];\n mLine.ssrcs.forEach(ssrc => {\n const found = mLine.sources.findIndex(source => source.id === ssrc.id);\n\n if (found > -1) {\n mLine.sources[found][ssrc.attribute] = ssrc.value;\n } else {\n const src = { id: ssrc.id };\n\n src[ssrc.attribute] = ssrc.value;\n mLine.sources.push(src);\n }\n });\n delete mLine.ssrcs;\n }\n\n // split ssrcs in ssrc groups\n if (typeof mLine.ssrcGroups !== 'undefined' && Array.isArray(mLine.ssrcGroups)) {\n mLine.ssrcGroups.forEach(ssrcGroup => {\n if (typeof ssrcGroup.ssrcs === 'string') {\n ssrcGroup.ssrcs = ssrcGroup.ssrcs.split(' ');\n }\n });\n }\n });\n }\n\n return session;\n};\n\nexport default {\n write,\n parse\n};\n","/* Copyright @ 2015 - Present, 8x8 Inc\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport clonedeep from 'lodash.clonedeep';\n\nimport transform from './transform.js';\n\nconst PLAN_B_MIDS = [ 'audio', 'video', 'data' ];\nconst findSimGroup = ssrcGroup => ssrcGroup.find(grp => grp.semantics === 'SIM');\nconst findFidGroup = ssrcGroup => ssrcGroup.find(grp => grp.semantics === 'FID');\n\n/**\n * Add the ssrcs of the SIM group and their corresponding FID group ssrcs\n * to the m-line.\n * @param {Object} mLine - The m-line to which ssrcs have to be added.\n * @param {Object} simGroup - The SIM group whose ssrcs have to be added to\n * the m-line.\n * @param {Object} sourceGroups - inverted source-group map.\n * @param {Array} sourceList - array containing all the sources.\n */\nfunction addSimGroupSources(mLine, simGroup, sourceGroups, sourceList) {\n if (!mLine || !simGroup) {\n return;\n }\n const findSourcebyId = src => sourceList.find(source => source.id.toString() === src);\n\n simGroup.ssrcs.forEach(src => {\n mLine.sources.push(findSourcebyId(src));\n\n // find the related FID group member for this ssrc.\n const relatedFidGroup = sourceGroups[parseInt(src, 10)].find(grp => grp.semantics === 'FID');\n\n if (relatedFidGroup) {\n const relatedSsrc = relatedFidGroup.ssrcs.find(s => s !== src);\n\n mLine.sources.push(findSourcebyId(relatedSsrc));\n mLine.ssrcGroups.push(relatedFidGroup);\n }\n });\n\n // Add the SIM group last.\n mLine.ssrcGroups.push(simGroup);\n}\n\n/**\n * Add ssrcs and ssrc-groups to the m-line. When a primary ssrc, i.e., the\n * first ssrc in a SIM group is passed, all the other ssrcs from the SIM\n * group and the other ssrcs from the related FID groups are added to the same\n * m-line since they all belong to the same remote source. Since the ssrcs are\n * not guaranteed to be in the correct order, try to find if a SIM group exists,\n * if not, just add the FID group.\n * @param {Object} mLine - The m-line to which ssrcs have to be added.\n * @param {Object} ssrc - the primary ssrc.\n * @param {Object} sourceGroups - inverted source-group map.\n * @param {Array} sourceList - array containing all the sources.\n * @returns {void}\n */\nfunction addSourcesToMline(mLine, ssrc, sourceGroups, sourceList) {\n if (!mLine || !ssrc) {\n return;\n }\n mLine.sources = [];\n mLine.ssrcGroups = [];\n\n // If there are no associated ssrc-groups, just add the ssrc and msid.\n if (!sourceGroups[ssrc.id]) {\n mLine.sources.push(ssrc);\n mLine.msid = ssrc.msid;\n\n return;\n }\n const findSourcebyId = src => sourceList.find(source => source.id.toString() === src);\n\n // Find the SIM and FID groups that this ssrc belongs to.\n const simGroup = findSimGroup(sourceGroups[ssrc.id]);\n const fidGroup = findFidGroup(sourceGroups[ssrc.id]);\n\n // Add the ssrcs for the SIM group and their corresponding FID groups.\n if (simGroup) {\n addSimGroupSources(mLine, simGroup, sourceGroups, sourceList);\n } else if (fidGroup) {\n // check if the other ssrc from this FID group is part of a SIM group\n const otherSsrc = fidGroup.ssrcs.find(s => s !== ssrc);\n const simGroup2 = findSimGroup(sourceGroups[otherSsrc]);\n\n if (simGroup2) {\n addSimGroupSources(mLine, simGroup2, sourceGroups, sourceList);\n } else {\n // Add the FID group ssrcs.\n fidGroup.ssrcs.forEach(src => {\n mLine.sources.push(findSourcebyId(src));\n });\n mLine.ssrcGroups.push(fidGroup);\n }\n }\n\n // Set the msid for the media description using the msid attribute of the ssrcs.\n mLine.msid = mLine.sources[0].msid;\n}\n\n/**\n * Checks if there is a mline for the given ssrc or its related primary ssrc.\n * We always implode the SIM group to the first ssrc in the SIM group before sRD,\n * so we also check if mline for that ssrc exists.\n * For example:\n * If the following ssrcs are in a SIM group,\n * \n * \n * \n * \n * \n * This method returns true for any one of the 3 ssrcs if there is a mline for 1806330949.\n * @param {Object} ssrc - ssrc to check.\n * @param {Object} sourceGroups - inverted source-group map.\n * @param {Array} mlines - mlines in the description\n\n * @returns {Boolean} - Returns true if mline for the given ssrc or the related primary ssrc\n * exists, returns false otherwise.\n */\nfunction checkIfMlineForSsrcExists(ssrc, sourceGroups, mlines) {\n const findMatchingMline = mline => {\n if (mline.sources) {\n return mline.sources.some(source => source.id === ssrc.id);\n }\n\n return false;\n };\n\n if (!mlines.find(findMatchingMline)) {\n // check if this ssrc is member of a SIM group. If so, check if there\n // is a matching m-line for the primary ssrc of the SIM group.\n if (!sourceGroups[ssrc.id]) {\n return false;\n }\n const simGroup = findSimGroup(sourceGroups[ssrc.id]);\n const fidGroup = findFidGroup(sourceGroups[ssrc.id]);\n\n if (simGroup) {\n return mlines.some(mline => mline.sources\n && mline.sources.some(src => src.id.toString() === simGroup.ssrcs[0]));\n } else if (fidGroup && ssrc.id.toString() !== fidGroup.ssrcs[0]) {\n const otherSsrc = { id: fidGroup.ssrcs[0] };\n\n return checkIfMlineForSsrcExists(otherSsrc, sourceGroups, mlines);\n\n }\n\n return false;\n }\n\n return true;\n}\n\n/**\n * Create an inverted sourceGroup map to put all the grouped ssrcs\n * in the same m-line.\n * @param {Array} sourceGroups\n * @returns {Object} - An inverted sourceGroup map.\n */\nfunction createSourceGroupMap(sourceGroups) {\n const ssrc2group = {};\n\n if (!sourceGroups || !Array.isArray(sourceGroups)) {\n return ssrc2group;\n }\n sourceGroups.forEach(group => {\n if (group.ssrcs && Array.isArray(group.ssrcs)) {\n group.ssrcs.forEach(ssrc => {\n if (typeof ssrc2group[ssrc] === 'undefined') {\n ssrc2group[ssrc] = [];\n }\n ssrc2group[ssrc].push(group);\n });\n }\n });\n\n return ssrc2group;\n}\n\n/**\n * Check if a new SDP requests an ICE restart.\n * @param {Object} - the parsed new SDP\n * @param {Object} - the parsed previous SDP\n * @returns {Boolean} - Returns true if an ICE restart is requested otherwise false.\n */\nfunction checkForIceRestart(newDesc, oldDesc) {\n if (!newDesc || !oldDesc || newDesc.media.length === 0 || oldDesc.media.length === 0) {\n return false;\n }\n\n const newMLine = newDesc.media[0];\n const oldMLine = oldDesc.media[0];\n\n return newMLine.iceUfrag !== oldMLine.iceUfrag || newMLine.icePwd !== oldMLine.icePwd;\n}\n\n/**\n * Interop provides an API for tranforming a Plan B SDP to a Unified Plan SDP and\n * vice versa.\n */\nexport class Interop {\n /**\n * This method transforms a Unified Plan SDP to an equivalent Plan B SDP.\n * @param {RTCSessionDescription} description - The description in Unified plan format.\n * @returns RTCSessionDescription - The transformed session description.\n */\n toPlanB(description) {\n if (!description || typeof description.sdp !== 'string') {\n console.warn('An empty description was passed as an argument.');\n\n return description;\n }\n\n // Objectify the SDP for easier manipulation.\n const session = transform.parse(description.sdp);\n\n // If the SDP contains no media, there's nothing to transform.\n if (!session.media || !session.media.length) {\n console.warn('The description has no media.');\n\n return description;\n }\n\n // Make sure this is a unified plan sdp\n if (session.media.every(m => PLAN_B_MIDS.indexOf(m.mid) !== -1)) {\n console.warn('The description does not look like unified plan sdp');\n\n return description;\n }\n\n const media = {};\n const sessionMedia = session.media;\n\n session.media = [];\n sessionMedia.forEach(mLine => {\n const type = mLine.type;\n\n if (type === 'application') {\n mLine.mid = 'data';\n media[mLine.mid] = mLine;\n\n return;\n }\n if (typeof media[type] === 'undefined') {\n const bLine = clonedeep(mLine);\n\n // Copy the msid attribute to all the ssrcs if they belong to the same source group\n if (bLine.sources && Array.isArray(bLine.sources)) {\n bLine.sources.forEach(source => {\n mLine.msid ? source.msid = mLine.msid : delete source.msid;\n });\n }\n\n // Do not signal the FID groups if there is no msid attribute present\n // on the sources as sesison-accept with this source info will fail strophe\n // validation and the session will not be established. This behavior is seen\n // on Firefox (with RTX enabled) when no video source is added at the join time.\n // FF generates two recvonly ssrcs with no msid and a corresponding FID group in\n // this case.\n if (!bLine.ssrcGroups || !mLine.msid) {\n bLine.ssrcGroups = [];\n }\n delete bLine.msid;\n bLine.mid = type;\n media[type] = bLine;\n } else if (mLine.msid) {\n // Add sources and source-groups to the existing m-line of the same media type.\n if (mLine.sources && Array.isArray(mLine.sources)) {\n media[type].sources = (media[type].sources || []).concat(mLine.sources);\n }\n if (typeof mLine.ssrcGroups !== 'undefined' && Array.isArray(mLine.ssrcGroups)) {\n media[type].ssrcGroups = media[type].ssrcGroups.concat(mLine.ssrcGroups);\n }\n }\n });\n session.media = Object.values(media);\n\n // Bundle the media only if it is active.\n const bundle = [];\n\n Object.values(media).forEach(mline => {\n if (mline.direction !== 'inactive') {\n bundle.push(mline.mid);\n }\n });\n\n // We regenerate the BUNDLE group with the new mids.\n session.groups.forEach(group => {\n if (group.type === 'BUNDLE') {\n group.mids = bundle.join(' ');\n }\n });\n\n // msid semantic\n session.msidSemantic = {\n semantic: 'WMS',\n token: '*'\n };\n const resStr = transform.write(session);\n\n return new RTCSessionDescription({\n type: description.type,\n sdp: resStr\n });\n }\n\n /**\n * This method transforms a Plan B SDP to an equivalent Unified Plan SDP.\n * @param {RTCSessionDescription} description - The description in plan-b format.\n * @param {RTCSessionDescription} current - The current description set on\n * the peerconnection in Unified-plan format, i.e., the readonly attribute\n * remoteDescription on the RTCPeerConnection object.\n * @returns RTCSessionDescription - The transformed session description.\n */\n toUnifiedPlan(description, current = null) {\n if (!description || typeof description.sdp !== 'string') {\n console.warn('An empty description was passed as an argument.');\n\n return description;\n }\n\n // Objectify the SDP for easier manipulation.\n const session = transform.parse(description.sdp);\n\n // If the SDP contains no media, there's nothing to transform.\n if (!session.media || !session.media.length) {\n console.warn('The description has no media.');\n\n return description;\n }\n\n // Make sure this is a plan-b sdp.\n if (session.media.length > 3 || session.media.every(m => PLAN_B_MIDS.indexOf(m.mid) === -1)) {\n console.warn('The description does not look like plan-b');\n\n return description;\n }\n const currentDesc = current ? transform.parse(current.sdp) : null;\n const iceRestart = checkForIceRestart(session, currentDesc);\n const newIceUfrag = session.media[0].iceUfrag;\n const newIcePwd = session.media[0].icePwd;\n const newFingerprint = session.media[0].fingerprint;\n const media = {};\n\n session.media.forEach(mLine => {\n const type = mLine.type;\n\n if (type === 'application') {\n if (!currentDesc || !currentDesc.media) {\n const newMline = clonedeep(mLine);\n\n newMline.mid = Object.keys(media).length.toString();\n media[mLine.mid] = newMline;\n\n return;\n }\n const mLineForData = currentDesc.media.findIndex(m => m.type === type);\n\n if (mLineForData) {\n currentDesc.media[mLineForData] = mLine;\n currentDesc.media[mLineForData].mid = mLineForData;\n }\n\n return;\n }\n\n // Create an inverted sourceGroup map here to put all the grouped SSRCs in the same m-line.\n const ssrc2group = createSourceGroupMap(mLine.ssrcGroups);\n\n // If there are no sources advertised for a media type, add the description if this is the first\n // remote offer, i.e., no current description was passed. Chrome in Unified plan does not produce\n // recvonly ssrcs unlike Firefox and Safari.\n if (!mLine.sources) {\n if (!currentDesc) {\n const newMline = clonedeep(mLine);\n\n newMline.mid = Object.keys(media).length.toString();\n media[mLine.mid] = newMline;\n }\n\n return;\n }\n mLine.sources.forEach((ssrc, idx) => {\n // Do not add the receive-only ssrcs that Jicofo sends in the source-add.\n // These ssrcs do not have the \"msid\" attribute set.\n if (!ssrc.msid) {\n return;\n }\n\n // If there is no description set on the peerconnection, create new m-lines.\n if (!currentDesc || !currentDesc.media) {\n if (checkIfMlineForSsrcExists(ssrc, ssrc2group, Object.values(media))) {\n return;\n }\n const newMline = clonedeep(mLine);\n\n newMline.mid = Object.keys(media).length.toString();\n newMline.direction = idx\n ? 'sendonly'\n : mLine.direction === 'sendonly' ? 'sendonly' : 'sendrecv';\n newMline.bundleOnly = undefined;\n addSourcesToMline(newMline, ssrc, ssrc2group, mLine.sources);\n media[newMline.mid] = newMline;\n\n return;\n }\n\n // Create and append the m-lines to the existing description.\n if (checkIfMlineForSsrcExists(ssrc, ssrc2group, currentDesc.media)) {\n return;\n }\n const newMline = clonedeep(mLine);\n\n newMline.mid = currentDesc.media.length.toString();\n newMline.direction = 'sendonly';\n addSourcesToMline(newMline, ssrc, ssrc2group, mLine.sources);\n currentDesc.media.push(newMline);\n });\n });\n session.media = currentDesc ? currentDesc.media : Object.values(media);\n const mids = [];\n\n session.media.forEach(mLine => {\n mids.push(mLine.mid);\n if (iceRestart) {\n mLine.iceUfrag = newIceUfrag;\n mLine.icePwd = newIcePwd;\n mLine.fingerprint = newFingerprint;\n }\n });\n\n // We regenerate the BUNDLE group (since we regenerated the mids)\n session.groups.forEach(group => {\n if (group.type === 'BUNDLE') {\n group.mids = mids.join(' ');\n }\n });\n\n // msid semantic\n session.msidSemantic = {\n semantic: 'WMS',\n token: '*'\n };\n\n // Increment the session version every time.\n session.origin.sessionVersion++;\n const resultSdp = transform.write(session);\n\n return new RTCSessionDescription({\n type: description.type,\n sdp: resultSdp\n });\n }\n}\n","export enum SignalingEvents {\n /**\n * Event triggered when participant's muted status changes.\n * @param {string} endpointId the track owner's identifier (MUC nickname)\n * @param {MediaType} mediaType \"audio\" or \"video\"\n * @param {boolean} isMuted the new muted state\n */\n PEER_MUTED_CHANGED = 'signaling.peerMuted',\n\n /**\n * Event triggered when participant's video type changes.\n * @param {string} endpointId the video owner's ID (MUC nickname)\n * @param {VideoType} videoType the new value\n */\n PEER_VIDEO_TYPE_CHANGED = 'signaling.peerVideoType'\n}\n\n// exported for backward compatibility\nexport const PEER_MUTED_CHANGED = SignalingEvents.PEER_MUTED_CHANGED;\nexport const PEER_VIDEO_TYPE_CHANGED = SignalingEvents.PEER_VIDEO_TYPE_CHANGED;\n","\nimport Listenable from '../../modules/util/Listenable';\nimport * as MediaType from '../../service/RTC/MediaType';\n\n/**\n * @typedef {string} EndpointId\n */\n/**\n * @typedef {string} SourceName\n */\n/**\n * @typedef {Object} SourceInfo\n *\n * @property {SourceName} sourceName - Name of the media source.\n * @property {boolean} [muted=false] - Tells if the source is muted (paused?).\n * @property {string} [videoType] - Type of the video for video type.\n */\n\n/**\n * Generates a source name.\n *\n * @param {EndpointId} endpointId - Jitsi Endpoint Id.\n * @param {MediaType} mediaType - the media type string.\n * @param {number} trackIdx - Track index (or sender idx? - to be figured out) starting from 0.\n * @returns {SourceName} eg. endpointA-v0\n */\nexport function getSourceNameForJitsiTrack(endpointId, mediaType, trackIdx) {\n const firstLetterOfMediaType = mediaType.substring(0, 1);\n\n return `${endpointId}-${firstLetterOfMediaType}${trackIdx}`;\n}\n\n/**\n * Extracts MediaType from give source name (must be in the correct format as generated by\n * {@link getSourceNameForJitsiTrack}).\n *\n * @param {SourceName} sourceName - the source name.\n * @returns {MediaType}\n */\nexport function getMediaTypeFromSourceName(sourceName) {\n const firstLetterOfMediaTypeIdx = sourceName.indexOf('-') + 1;\n\n if (firstLetterOfMediaTypeIdx <= 0) {\n throw new Error(`Invalid source name: ${sourceName}`);\n }\n\n const firstLetterOfMediaType = sourceName.substr(firstLetterOfMediaTypeIdx, 1);\n\n for (const type of Object.values(MediaType)) {\n if (type.substr(0, 1) === firstLetterOfMediaType) {\n return type;\n }\n }\n\n throw new Error(`Invalid source name: ${sourceName}`);\n}\n\n/**\n * An object that carries the info about specific media type advertised by\n * participant in the signaling channel.\n * @typedef {Object} PeerMediaInfo\n * @property {boolean} muted indicates if the media is currently muted\n * @property {VideoType|undefined} videoType the type of the video if applicable\n */\n\n/**\n * Interface used to expose the information carried over the signaling channel\n * which is not available to the RTC module in the media SDP.\n *\n * @interface SignalingLayer\n */\nexport default class SignalingLayer extends Listenable {\n\n /**\n * Obtains the endpoint ID for given SSRC.\n * @param {number} ssrc the SSRC number.\n * @return {string|null} the endpoint ID for given media SSRC.\n */\n getSSRCOwner(ssrc) { // eslint-disable-line no-unused-vars\n throw new Error('not implemented');\n }\n\n /**\n * Obtains the info about given media advertised in the MUC presence of\n * the participant identified by the given MUC JID.\n * @param {string} owner the MUC jid of the participant for whom\n * {@link PeerMediaInfo} will be obtained.\n * @param {MediaType} mediaType the type of the media for which presence\n * info will be obtained.\n * @return {PeerMediaInfo|null} presenceInfo an object with media presence\n * info or null either if there is no presence available for given\n * JID or if the media type given is invalid.\n *\n * @deprecated This method is to be replaced with getPeerSourceInfo.\n */\n getPeerMediaInfo(owner, mediaType) { // eslint-disable-line no-unused-vars\n throw new Error('not implemented');\n }\n\n /**\n * Obtains the info about a source for given name and endpoint ID.\n * @param {EndpointId} owner - The owner's endpoint ID.\n * @param {SourceName} sourceName - The name of the source for which the info is to be obtained.\n * @returns {SourceInfo | undefined}\n */\n getPeerSourceInfo(owner, sourceName) { // eslint-disable-line no-unused-vars\n throw new Error('not implemented');\n }\n\n /**\n * Obtains the source name for given SSRC.\n * @param {number} ssrc the track's SSRC identifier.\n * @returns {SourceName | undefined} the track's source name.\n */\n getTrackSourceName(ssrc) { // eslint-disable-line no-unused-vars\n throw new Error('not implemented');\n }\n}\n","import * as transform from 'sdp-transform';\n\n/**\n * Parses the primary SSRC of given SSRC group.\n * @param {object} group the SSRC group object as defined by the 'sdp-transform'\n * @return {Number} the primary SSRC number\n */\nexport function parsePrimarySSRC(group) {\n return parseInt(group.ssrcs.split(' ')[0], 10);\n}\n\n/**\n * Parses the secondary SSRC of given SSRC group.\n * @param {object} group the SSRC group object as defined by the 'sdp-transform'\n * @return {Number} the secondary SSRC number\n */\nexport function parseSecondarySSRC(group) {\n return parseInt(group.ssrcs.split(' ')[1], 10);\n}\n\n/**\n * Tells how many distinct SSRCs are contained in given media line.\n * @param {Object} mLine the media line object as defined by 'sdp-transform' lib\n * @return {number}\n */\nfunction _getSSRCCount(mLine) {\n if (!mLine.ssrcs) {\n return 0;\n }\n\n return mLine.ssrcs\n .map(ssrcInfo => ssrcInfo.id)\n .filter((ssrc, index, array) => array.indexOf(ssrc) === index)\n .length;\n}\n\n/**\n * A wrapper around 'sdp-transform' media description object which provides\n * utility methods for common SDP/SSRC related operations.\n */\nclass MLineWrap {\n\n /**\n * Creates new MLineWrap>\n * @param {Object} mLine the media line object as defined by 'sdp-transform'\n * lib.\n */\n constructor(mLine) {\n if (!mLine) {\n throw new Error('mLine is undefined');\n }\n\n this.mLine = mLine;\n }\n\n /**\n * Getter for the mLine's \"ssrcs\" array. If the array was undefined an empty\n * one will be preassigned.\n *\n * @return {Array} an array of 'sdp-transform' SSRC attributes\n * objects.\n */\n get ssrcs() {\n if (!this.mLine.ssrcs) {\n this.mLine.ssrcs = [];\n }\n\n return this.mLine.ssrcs;\n }\n\n /**\n * Setter for the mLine's \"ssrcs\" array.\n *\n * @param {Array} ssrcs an array of 'sdp-transform' SSRC attributes\n * objects.\n */\n set ssrcs(ssrcs) {\n this.mLine.ssrcs = ssrcs;\n }\n\n /**\n * Returns the direction of the underlying media description.\n * @return {string} the media direction name as defined in the SDP.\n */\n get direction() {\n return this.mLine.direction;\n }\n\n /**\n * Modifies the direction of the underlying media description.\n * @param {string} direction the new direction to be set\n */\n set direction(direction) {\n this.mLine.direction = direction;\n }\n\n /**\n * Exposes the SSRC group array of the underlying media description object.\n * @return {Array.}\n */\n get ssrcGroups() {\n if (!this.mLine.ssrcGroups) {\n this.mLine.ssrcGroups = [];\n }\n\n return this.mLine.ssrcGroups;\n }\n\n /**\n * Modifies the SSRC groups array of the underlying media description\n * object.\n * @param {Array.} ssrcGroups\n */\n set ssrcGroups(ssrcGroups) {\n this.mLine.ssrcGroups = ssrcGroups;\n }\n\n /**\n * Obtains value from SSRC attribute.\n * @param {number} ssrcNumber the SSRC number for which attribute is to be\n * found\n * @param {string} attrName the name of the SSRC attribute to be found.\n * @return {string|undefined} the value of SSRC attribute or\n * undefined if no such attribute exists.\n */\n getSSRCAttrValue(ssrcNumber, attrName) {\n const attribute = this.ssrcs.find(\n ssrcObj => ssrcObj.id === ssrcNumber\n && ssrcObj.attribute === attrName);\n\n\n return attribute && attribute.value;\n }\n\n /**\n * Removes all attributes for given SSRC number.\n * @param {number} ssrcNum the SSRC number for which all attributes will be\n * removed.\n */\n removeSSRC(ssrcNum) {\n if (!this.mLine.ssrcs || !this.mLine.ssrcs.length) {\n return;\n }\n\n this.mLine.ssrcs\n = this.mLine.ssrcs.filter(ssrcObj => ssrcObj.id !== ssrcNum);\n }\n\n /**\n * Adds SSRC attribute\n * @param {object} ssrcObj the SSRC attribute object as defined in\n * the 'sdp-transform' lib.\n */\n addSSRCAttribute(ssrcObj) {\n this.ssrcs.push(ssrcObj);\n }\n\n /**\n * Finds a SSRC group matching both semantics and SSRCs in order.\n * @param {string} semantics the name of the semantics\n * @param {string} [ssrcs] group SSRCs as a string (like it's defined in\n * SSRC group object of the 'sdp-transform' lib) e.g. \"1232546 342344 25434\"\n * @return {object|undefined} the SSRC group object or undefined if\n * not found.\n */\n findGroup(semantics, ssrcs) {\n return this.ssrcGroups.find(\n group =>\n group.semantics === semantics\n && (!ssrcs || ssrcs === group.ssrcs));\n }\n\n /**\n * Finds all groups matching given semantic's name.\n * @param {string} semantics the name of the semantics\n * @return {Array.} an array of SSRC group objects as defined by\n * the 'sdp-transform' lib.\n */\n findGroups(semantics) {\n return this.ssrcGroups.filter(\n group => group.semantics === semantics);\n }\n\n /**\n * Finds all groups matching given semantic's name and group's primary SSRC.\n * @param {string} semantics the name of the semantics\n * @param {number} primarySSRC the primary SSRC number to be matched\n * @return {Object} SSRC group object as defined by the 'sdp-transform' lib.\n */\n findGroupByPrimarySSRC(semantics, primarySSRC) {\n return this.ssrcGroups.find(\n group => group.semantics === semantics\n && parsePrimarySSRC(group) === primarySSRC);\n }\n\n /**\n * @param {string|null} msid the media stream id or null to match\n * the first SSRC object with any 'msid' value.\n * @return {Object|undefined} the SSRC object as defined by 'sdp-transform'\n * lib.\n */\n findSSRCByMSID(msid) {\n return this.ssrcs.find(\n ssrcObj => ssrcObj.attribute === 'msid'\n && (msid === null || ssrcObj.value === msid));\n }\n\n /**\n * Gets the SSRC count for the underlying media description.\n * @return {number}\n */\n getSSRCCount() {\n return _getSSRCCount(this.mLine);\n }\n\n /**\n * Checks whether the underlying media description contains any SSRC groups.\n * @return {boolean} true if there are any SSRC groups or\n * false otherwise.\n */\n containsAnySSRCGroups() {\n return this.mLine.ssrcGroups !== undefined;\n }\n\n /**\n * Finds the primary video SSRC.\n * @returns {number|undefined} the primary video ssrc\n * @throws Error if the underlying media description is not a video\n */\n getPrimaryVideoSsrc() {\n const mediaType = this.mLine.type;\n\n if (mediaType !== 'video') {\n throw new Error(\n `getPrimarySsrc doesn't work with '${mediaType}'`);\n }\n\n const numSsrcs = _getSSRCCount(this.mLine);\n\n if (numSsrcs === 1) {\n // Not using \"ssrcs\" getter on purpose here\n return this.mLine.ssrcs[0].id;\n }\n\n // Look for a SIM, FID, or FEC-FR group\n if (this.mLine.ssrcGroups) {\n const simGroup = this.findGroup('SIM');\n\n if (simGroup) {\n return parsePrimarySSRC(simGroup);\n }\n const fidGroup = this.findGroup('FID');\n\n if (fidGroup) {\n return parsePrimarySSRC(fidGroup);\n }\n const fecGroup = this.findGroup('FEC-FR');\n\n if (fecGroup) {\n return parsePrimarySSRC(fecGroup);\n }\n }\n\n }\n\n /**\n * Obtains RTX SSRC from the underlying video description (the\n * secondary SSRC of the first \"FID\" group found)\n * @param {number} primarySsrc the video ssrc for which to find the\n * corresponding rtx ssrc\n * @returns {number|undefined} the rtx ssrc (or undefined if there isn't\n * one)\n */\n getRtxSSRC(primarySsrc) {\n const fidGroup = this.findGroupByPrimarySSRC('FID', primarySsrc);\n\n\n return fidGroup && parseSecondarySSRC(fidGroup);\n }\n\n /**\n * Obtains all SSRCs contained in the underlying media description.\n * @return {Array.} an array with all SSRC as numbers.\n */\n getSSRCs() {\n return this.ssrcs\n .map(ssrcInfo => ssrcInfo.id)\n .filter((ssrc, index, array) => array.indexOf(ssrc) === index);\n }\n\n /**\n * Obtains primary video SSRCs.\n * @return {Array.} an array of all primary video SSRCs as numbers.\n * @throws Error if the wrapped media description is not a video.\n */\n getPrimaryVideoSSRCs() {\n const mediaType = this.mLine.type;\n\n if (mediaType !== 'video') {\n throw new Error(\n `getPrimaryVideoSSRCs doesn't work with ${mediaType}`);\n }\n\n const videoSSRCs = this.getSSRCs();\n\n for (const ssrcGroupInfo of this.ssrcGroups) {\n // Right now, FID and FEC-FR groups are the only ones we parse to\n // disqualify streams. If/when others arise we'll\n // need to add support for them here\n if (ssrcGroupInfo.semantics === 'FID'\n || ssrcGroupInfo.semantics === 'FEC-FR') {\n // secondary streams should be filtered out\n const secondarySsrc = parseSecondarySSRC(ssrcGroupInfo);\n\n videoSSRCs.splice(\n videoSSRCs.indexOf(secondarySsrc), 1);\n }\n }\n\n return videoSSRCs;\n }\n\n /**\n * Dumps all SSRC groups of this media description to JSON.\n */\n dumpSSRCGroups() {\n return JSON.stringify(this.mLine.ssrcGroups);\n }\n\n /**\n * Removes all SSRC groups which contain given SSRC number at any position.\n * @param {number} ssrc the SSRC for which all matching groups are to be\n * removed.\n */\n removeGroupsWithSSRC(ssrc) {\n if (!this.mLine.ssrcGroups) {\n return;\n }\n\n this.mLine.ssrcGroups = this.mLine.ssrcGroups\n .filter(groupInfo => groupInfo.ssrcs.indexOf(`${ssrc}`) === -1);\n }\n\n /**\n * Removes groups that match given semantics.\n * @param {string} semantics e.g. \"SIM\" or \"FID\"\n */\n removeGroupsBySemantics(semantics) {\n if (!this.mLine.ssrcGroups) {\n return;\n }\n\n this.mLine.ssrcGroups\n = this.mLine.ssrcGroups\n .filter(groupInfo => groupInfo.semantics !== semantics);\n }\n\n /**\n * Replaces SSRC (does not affect SSRC groups, but only attributes).\n * @param {number} oldSSRC the old SSRC number\n * @param {number} newSSRC the new SSRC number\n */\n replaceSSRC(oldSSRC, newSSRC) {\n if (this.mLine.ssrcs) {\n this.mLine.ssrcs.forEach(ssrcInfo => {\n if (ssrcInfo.id === oldSSRC) {\n ssrcInfo.id = newSSRC;\n }\n });\n }\n }\n\n /**\n * Adds given SSRC group to this media description.\n * @param {object} group the SSRC group object as defined by\n * the 'sdp-transform' lib.\n */\n addSSRCGroup(group) {\n this.ssrcGroups.push(group);\n }\n}\n\n/**\n * Utility class for SDP manipulation using the 'sdp-transform' library.\n *\n * Typical use usage scenario:\n *\n * const transformer = new SdpTransformWrap(rawSdp);\n * const videoMLine = transformer.selectMedia('video);\n * if (videoMLine) {\n * videoMLiner.addSSRCAttribute({\n * id: 2342343,\n * attribute: \"cname\",\n * value: \"someCname\"\n * });\n * rawSdp = transformer.toRawSdp();\n * }\n */\nexport class SdpTransformWrap {\n\n /**\n * Creates new instance and parses the raw SDP into objects using\n * 'sdp-transform' lib.\n * @param {string} rawSDP the SDP in raw text format.\n */\n constructor(rawSDP) {\n this.parsedSDP = transform.parse(rawSDP);\n }\n\n /**\n * Selects all the m-lines from the SDP for a given media type.\n *\n * @param {string} mediaType the name of the media e.g. 'audio', 'video', 'data'.\n * @return {MLineWrap|null} return {@link MLineWrap} instance for the media line or null if not found. The\n * object returned references the underlying SDP state held by this SdpTransformWrap instance (it's not a\n * copy).\n */\n selectMedia(mediaType) {\n const selectedMLines = this.parsedSDP.media\n .filter(mLine => mLine.type === mediaType)\n .map(mLine => new MLineWrap(mLine));\n\n return selectedMLines ?? null;\n }\n\n /**\n * Converts the currently stored SDP state in this instance to raw text SDP\n * format.\n * @return {string}\n */\n toRawSDP() {\n return transform.write(this.parsedSDP);\n }\n}\n","import { getLogger } from '@jitsi/logger';\n\nimport MediaDirection from '../../service/RTC/MediaDirection';\nimport * as MediaType from '../../service/RTC/MediaType';\nimport { getSourceNameForJitsiTrack } from '../../service/RTC/SignalingLayer';\nimport VideoType from '../../service/RTC/VideoType';\nimport FeatureFlags from '../flags/FeatureFlags';\n\nimport { SdpTransformWrap } from './SdpTransformUtil';\n\nconst logger = getLogger(__filename);\n\n/**\n * Fakes local SDP exposed to {@link JingleSessionPC} through the local\n * description getter. Modifies the SDP, so that it will contain muted local\n * video tracks description, even though their underlying {MediaStreamTrack}s\n * are no longer in the WebRTC peerconnection. That prevents from SSRC updates\n * being sent to Jicofo/remote peer and prevents sRD/sLD cycle on the remote\n * side.\n */\nexport default class LocalSdpMunger {\n\n /**\n * Creates new LocalSdpMunger instance.\n *\n * @param {TraceablePeerConnection} tpc\n * @param {string} localEndpointId - The endpoint id of the local user.\n */\n constructor(tpc, localEndpointId) {\n this.tpc = tpc;\n this.localEndpointId = localEndpointId;\n }\n\n /**\n * Makes sure that muted local video tracks associated with the parent\n * {@link TraceablePeerConnection} are described in the local SDP. It's done\n * in order to prevent from sending 'source-remove'/'source-add' Jingle\n * notifications when local video track is muted (MediaStream is\n * removed from the peerconnection).\n *\n * NOTE 1 video track is assumed\n *\n * @param {SdpTransformWrap} transformer the transformer instance which will\n * be used to process the SDP.\n * @return {boolean} true if there were any modifications to\n * the SDP wrapped by transformer.\n * @private\n */\n _addMutedLocalVideoTracksToSDP(transformer) {\n // Go over each video tracks and check if the SDP has to be changed\n const localVideos = this.tpc.getLocalTracks(MediaType.VIDEO);\n\n if (!localVideos.length) {\n return false;\n } else if (localVideos.length !== 1) {\n logger.error(\n `${this.tpc} there is more than 1 video track ! `\n + 'Strange things may happen !', localVideos);\n }\n\n const videoMLine = transformer.selectMedia(MediaType.VIDEO)?.[0];\n\n if (!videoMLine) {\n logger.debug(\n `${this.tpc} unable to hack local video track SDP`\n + '- no \"video\" media');\n\n return false;\n }\n\n let modified = false;\n\n for (const videoTrack of localVideos) {\n const muted = videoTrack.isMuted();\n const mediaStream = videoTrack.getOriginalStream();\n const isCamera = videoTrack.videoType === VideoType.CAMERA;\n\n // During the mute/unmute operation there are periods of time when\n // the track's underlying MediaStream is not added yet to\n // the PeerConnection. The SDP needs to be munged in such case.\n const isInPeerConnection\n = mediaStream && this.tpc.isMediaStreamInPc(mediaStream);\n const shouldFakeSdp = isCamera && (muted || !isInPeerConnection);\n\n if (!shouldFakeSdp) {\n continue; // eslint-disable-line no-continue\n }\n\n // Inject removed SSRCs\n const requiredSSRCs\n = this.tpc.isSimulcastOn()\n ? this.tpc.simulcast.ssrcCache\n : [ this.tpc.sdpConsistency.cachedPrimarySsrc ];\n\n if (!requiredSSRCs.length) {\n logger.error(`No SSRCs stored for: ${videoTrack} in ${this.tpc}`);\n\n continue; // eslint-disable-line no-continue\n }\n\n modified = true;\n\n // We need to fake sendrecv.\n // NOTE the SDP produced here goes only to Jicofo and is never set\n // as localDescription. That's why\n // TraceablePeerConnection.mediaTransferActive is ignored here.\n videoMLine.direction = MediaDirection.SENDRECV;\n\n // Check if the recvonly has MSID\n const primarySSRC = requiredSSRCs[0];\n\n // FIXME The cname could come from the stream, but may turn out to\n // be too complex. It is fine to come up with any value, as long as\n // we only care about the actual SSRC values when deciding whether\n // or not an update should be sent.\n const primaryCname = `injected-${primarySSRC}`;\n\n for (const ssrcNum of requiredSSRCs) {\n // Remove old attributes\n videoMLine.removeSSRC(ssrcNum);\n\n // Inject\n videoMLine.addSSRCAttribute({\n id: ssrcNum,\n attribute: 'cname',\n value: primaryCname\n });\n videoMLine.addSSRCAttribute({\n id: ssrcNum,\n attribute: 'msid',\n value: videoTrack.storedMSID\n });\n }\n if (requiredSSRCs.length > 1) {\n const group = {\n ssrcs: requiredSSRCs.join(' '),\n semantics: 'SIM'\n };\n\n if (!videoMLine.findGroup(group.semantics, group.ssrcs)) {\n // Inject the group\n videoMLine.addSSRCGroup(group);\n }\n }\n\n // Insert RTX\n // FIXME in P2P RTX is used by Chrome regardless of config option\n // status. Because of that 'source-remove'/'source-add'\n // notifications are still sent to remove/add RTX SSRC and FID group\n if (!this.tpc.options.disableRtx) {\n this.tpc.rtxModifier.modifyRtxSsrcs2(videoMLine);\n }\n }\n\n return modified;\n }\n\n /**\n * Returns a string that can be set as the MSID attribute for a source.\n *\n * @param {string} mediaType - Media type of the source.\n * @param {string} trackId - Id of the MediaStreamTrack associated with the source.\n * @param {string} streamId - Id of the MediaStream associated with the source.\n * @returns {string|null}\n */\n _generateMsidAttribute(mediaType, trackId, streamId = null) {\n if (!(mediaType && trackId)) {\n logger.warn(`Unable to munge local MSID - track id=${trackId} or media type=${mediaType} is missing`);\n\n return null;\n }\n const pcId = this.tpc.id;\n\n // Handle a case on Firefox when the browser doesn't produce a 'a:ssrc' line with the 'msid' attribute or has\n // '-' for the stream id part of the msid line. Jicofo needs an unique identifier to be associated with a ssrc\n // and uses the msid for that.\n if (streamId === '-' || !streamId) {\n return `${this.localEndpointId}-${mediaType}-${pcId} ${trackId}-${pcId}`;\n }\n\n return `${streamId}-${pcId} ${trackId}-${pcId}`;\n }\n\n /**\n * Modifies 'cname', 'msid', 'label' and 'mslabel' by appending the id of {@link LocalSdpMunger#tpc} at the end,\n * preceding by a dash sign.\n *\n * @param {MLineWrap} mediaSection - The media part (audio or video) of the session description which will be\n * modified in place.\n * @returns {void}\n * @private\n */\n _transformMediaIdentifiers(mediaSection) {\n const mediaType = mediaSection.mLine?.type;\n const pcId = this.tpc.id;\n const sourceToMsidMap = new Map();\n\n for (const ssrcLine of mediaSection.ssrcs) {\n switch (ssrcLine.attribute) {\n case 'cname':\n case 'label':\n case 'mslabel':\n ssrcLine.value = ssrcLine.value && `${ssrcLine.value}-${pcId}`;\n break;\n case 'msid': {\n if (ssrcLine.value) {\n const streamAndTrackIDs = ssrcLine.value.split(' ');\n\n let streamId = streamAndTrackIDs[0];\n const trackId = streamAndTrackIDs[1];\n\n // eslint-disable-next-line max-depth\n if (FeatureFlags.isMultiStreamSupportEnabled()\n && this.tpc.usesUnifiedPlan()\n && mediaType === MediaType.VIDEO) {\n\n // eslint-disable-next-line max-depth\n if (streamId === '-' || !streamId) {\n streamId = `${this.localEndpointId}-${mediaType}`;\n }\n\n // eslint-disable-next-line max-depth\n if (!sourceToMsidMap.has(trackId)) {\n streamId = `${streamId}-${sourceToMsidMap.size}`;\n sourceToMsidMap.set(trackId, streamId);\n }\n }\n\n ssrcLine.value = this._generateMsidAttribute(mediaType, trackId, sourceToMsidMap.get(trackId));\n } else {\n logger.warn(`Unable to munge local MSID - weird format detected: ${ssrcLine.value}`);\n }\n break;\n }\n }\n }\n\n // Additional transformations related to MSID are applicable to Unified-plan implementation only.\n if (!this.tpc.usesUnifiedPlan()) {\n return;\n }\n\n // If the msid attribute is missing, then remove the ssrc from the transformed description so that a\n // source-remove is signaled to Jicofo. This happens when the direction of the transceiver (or m-line)\n // is set to 'inactive' or 'recvonly' on Firefox, Chrome (unified) and Safari.\n const mediaDirection = mediaSection.mLine?.direction;\n\n if (mediaDirection === MediaDirection.RECVONLY || mediaDirection === MediaDirection.INACTIVE) {\n mediaSection.ssrcs = undefined;\n mediaSection.ssrcGroups = undefined;\n\n // Add the msid attribute if it is missing when the direction is sendrecv/sendonly. Firefox doesn't produce a\n // a=ssrc line with msid attribute for p2p connection.\n } else {\n const msidLine = mediaSection.mLine?.msid;\n const trackId = msidLine && msidLine.split(' ')[1];\n const sources = [ ...new Set(mediaSection.mLine?.ssrcs?.map(s => s.id)) ];\n\n for (const source of sources) {\n const msidExists = mediaSection.ssrcs\n .find(ssrc => ssrc.id === source && ssrc.attribute === 'msid');\n\n if (!msidExists) {\n const generatedMsid = this._generateMsidAttribute(mediaType, trackId);\n\n mediaSection.ssrcs.push({\n id: source,\n attribute: 'msid',\n value: generatedMsid\n });\n }\n }\n }\n }\n\n /**\n * Maybe modifies local description to fake local video tracks SDP when\n * those are muted.\n *\n * @param {object} desc the WebRTC SDP object instance for the local\n * description.\n * @returns {RTCSessionDescription}\n */\n maybeAddMutedLocalVideoTracksToSDP(desc) {\n if (!desc) {\n throw new Error('No local description passed in.');\n }\n\n const transformer = new SdpTransformWrap(desc.sdp);\n\n if (this._addMutedLocalVideoTracksToSDP(transformer)) {\n return new RTCSessionDescription({\n type: desc.type,\n sdp: transformer.toRawSDP()\n });\n }\n\n return desc;\n }\n\n /**\n * This transformation will make sure that stream identifiers are unique\n * across all of the local PeerConnections even if the same stream is used\n * by multiple instances at the same time.\n * Each PeerConnection assigns different SSRCs to the same local\n * MediaStream, but the MSID remains the same as it's used to identify\n * the stream by the WebRTC backend. The transformation will append\n * {@link TraceablePeerConnection#id} at the end of each stream's identifier\n * (\"cname\", \"msid\", \"label\" and \"mslabel\").\n *\n * @param {RTCSessionDescription} sessionDesc - The local session\n * description (this instance remains unchanged).\n * @return {RTCSessionDescription} - Transformed local session description\n * (a modified copy of the one given as the input).\n */\n transformStreamIdentifiers(sessionDesc) {\n // FIXME similar check is probably duplicated in all other transformers\n if (!sessionDesc || !sessionDesc.sdp || !sessionDesc.type) {\n return sessionDesc;\n }\n\n const transformer = new SdpTransformWrap(sessionDesc.sdp);\n const audioMLine = transformer.selectMedia(MediaType.AUDIO)?.[0];\n\n if (audioMLine) {\n this._transformMediaIdentifiers(audioMLine);\n this._injectSourceNames(audioMLine);\n }\n\n const videoMLine = transformer.selectMedia(MediaType.VIDEO)?.[0];\n\n if (videoMLine) {\n this._transformMediaIdentifiers(videoMLine);\n this._injectSourceNames(videoMLine);\n }\n\n return new RTCSessionDescription({\n type: sessionDesc.type,\n sdp: transformer.toRawSDP()\n });\n }\n\n /**\n * Injects source names. Source names are need to for multiple streams per endpoint support. The final plan is to\n * use the \"mid\" attribute for source names, but because the SDP to Jingle conversion still operates in the Plan-B\n * semantics (one source name per media), a custom \"name\" attribute is injected into SSRC lines..\n *\n * @param {MLineWrap} mediaSection - The media part (audio or video) of the session description which will be\n * modified in place.\n * @returns {void}\n * @private\n */\n _injectSourceNames(mediaSection) {\n if (!FeatureFlags.isSourceNameSignalingEnabled()) {\n return;\n }\n\n const sources = [ ...new Set(mediaSection.mLine?.ssrcs?.map(s => s.id)) ];\n const mediaType = mediaSection.mLine?.type;\n\n if (!mediaType) {\n throw new Error('_transformMediaIdentifiers - no media type in mediaSection');\n }\n\n for (const source of sources) {\n const nameExists = mediaSection.ssrcs.find(ssrc => ssrc.id === source && ssrc.attribute === 'name');\n\n if (!nameExists) {\n // Inject source names as a=ssrc:3124985624 name:endpointA-v0\n mediaSection.ssrcs.push({\n id: source,\n attribute: 'name',\n value: getSourceNameForJitsiTrack(this.localEndpointId, mediaType, 0)\n });\n }\n }\n }\n}\n","import { getLogger } from '@jitsi/logger';\n\nimport MediaDirection from '../../service/RTC/MediaDirection';\nimport * as MediaType from '../../service/RTC/MediaType';\n\nimport SDPUtil from './SDPUtil';\nimport { parseSecondarySSRC, SdpTransformWrap } from './SdpTransformUtil';\n\nconst logger = getLogger(__filename);\n\n/**\n * Begin helper functions\n */\n/**\n * Updates or inserts the appropriate rtx information for primarySsrc with\n * the given rtxSsrc. If no rtx ssrc for primarySsrc currently exists, it will\n * add the appropriate ssrc and ssrc group lines. If primarySsrc already has\n * an rtx ssrc, the appropriate ssrc and group lines will be updated\n * @param {MLineWrap} mLine\n * @param {object} primarySsrcInfo the info (ssrc, msid & cname) for the\n * primary ssrc\n * @param {number} rtxSsrc the rtx ssrc to associate with the primary ssrc\n */\nfunction updateAssociatedRtxStream(mLine, primarySsrcInfo, rtxSsrc) {\n const primarySsrc = primarySsrcInfo.id;\n const primarySsrcMsid = primarySsrcInfo.msid;\n const primarySsrcCname = primarySsrcInfo.cname;\n\n const previousRtxSSRC = mLine.getRtxSSRC(primarySsrc);\n\n if (previousRtxSSRC === rtxSsrc) {\n return;\n }\n if (previousRtxSSRC) {\n // Stream already had an rtx ssrc that is different than the one given,\n // remove all trace of the old one\n mLine.removeSSRC(previousRtxSSRC);\n mLine.removeGroupsWithSSRC(previousRtxSSRC);\n }\n mLine.addSSRCAttribute({\n id: rtxSsrc,\n attribute: 'cname',\n value: primarySsrcCname\n });\n mLine.addSSRCAttribute({\n id: rtxSsrc,\n attribute: 'msid',\n value: primarySsrcMsid\n });\n mLine.addSSRCGroup({\n semantics: 'FID',\n ssrcs: `${primarySsrc} ${rtxSsrc}`\n });\n}\n\n/**\n * End helper functions\n */\n\n/**\n * Adds any missing RTX streams for video streams\n * and makes sure that they remain consistent\n */\nexport default class RtxModifier {\n /**\n * Constructor\n */\n constructor() {\n /**\n * Map of video ssrc to corresponding RTX\n * ssrc\n */\n this.correspondingRtxSsrcs = new Map();\n }\n\n /**\n * Clear the cached map of primary video ssrcs to\n * their corresponding rtx ssrcs so that they will\n * not be used for the next call to modifyRtxSsrcs\n */\n clearSsrcCache() {\n this.correspondingRtxSsrcs.clear();\n }\n\n /**\n * Explicitly set the primary video ssrc -> rtx ssrc\n * mapping to be used in modifyRtxSsrcs\n * @param {Map} ssrcMapping a mapping of primary video\n * ssrcs to their corresponding rtx ssrcs\n */\n setSsrcCache(ssrcMapping) {\n logger.debug('Setting ssrc cache to ', ssrcMapping);\n this.correspondingRtxSsrcs = ssrcMapping;\n }\n\n /**\n * Adds RTX ssrcs for any video ssrcs that don't already have them. If the video ssrc has been seen before, and\n * already had an RTX ssrc generated, the same RTX ssrc will be used again.\n *\n * @param {string} sdpStr sdp in raw string format\n * @returns {string} The modified sdp in raw string format.\n */\n modifyRtxSsrcs(sdpStr) {\n let modified = false;\n const sdpTransformer = new SdpTransformWrap(sdpStr);\n const videoMLines = sdpTransformer.selectMedia(MediaType.VIDEO);\n\n if (!videoMLines?.length) {\n logger.debug(`No 'video' media found in the sdp: ${sdpStr}`);\n\n return sdpStr;\n }\n\n for (const videoMLine of videoMLines) {\n if (this.modifyRtxSsrcs2(videoMLine)) {\n modified = true;\n }\n }\n\n return modified ? sdpTransformer.toRawSDP() : sdpStr;\n }\n\n /**\n * Does the same thing as {@link modifyRtxSsrcs}, but takes the {@link MLineWrap} instance wrapping video media as\n * an argument.\n * @param {MLineWrap} videoMLine\n * @return {boolean} true if the SDP wrapped by {@link SdpTransformWrap} has been modified or\n * false otherwise.\n */\n modifyRtxSsrcs2(videoMLine) {\n if (videoMLine.direction === MediaDirection.RECVONLY) {\n return false;\n }\n if (videoMLine.getSSRCCount() < 1) {\n return false;\n }\n const primaryVideoSsrcs = videoMLine.getPrimaryVideoSSRCs();\n\n for (const ssrc of primaryVideoSsrcs) {\n const msid = videoMLine.getSSRCAttrValue(ssrc, 'msid');\n const cname = videoMLine.getSSRCAttrValue(ssrc, 'cname');\n let correspondingRtxSsrc = this.correspondingRtxSsrcs.get(ssrc);\n\n if (!correspondingRtxSsrc) {\n // If there's one in the sdp already for it, we'll just set\n // that as the corresponding one\n const previousAssociatedRtxStream = videoMLine.getRtxSSRC(ssrc);\n\n if (previousAssociatedRtxStream) {\n correspondingRtxSsrc = previousAssociatedRtxStream;\n } else {\n correspondingRtxSsrc = SDPUtil.generateSsrc();\n }\n this.correspondingRtxSsrcs.set(ssrc, correspondingRtxSsrc);\n }\n updateAssociatedRtxStream(\n videoMLine,\n {\n id: ssrc,\n cname,\n msid\n },\n correspondingRtxSsrc);\n }\n\n // FIXME we're not looking into much details whether the SDP has been\n // modified or not once the precondition requirements are met.\n return true;\n }\n\n /**\n * Strip all rtx streams from the given sdp.\n *\n * @param {string} sdpStr sdp in raw string format\n * @returns {string} sdp string with all rtx streams stripped\n */\n stripRtx(sdpStr) {\n const sdpTransformer = new SdpTransformWrap(sdpStr);\n const videoMLines = sdpTransformer.selectMedia(MediaType.VIDEO);\n\n if (!videoMLines?.length) {\n logger.debug(`No 'video' media found in the sdp: ${sdpStr}`);\n\n return sdpStr;\n }\n\n for (const videoMLine of videoMLines) {\n if (videoMLine.direction !== MediaDirection.RECVONLY\n && videoMLine.getSSRCCount()\n && videoMLine.containsAnySSRCGroups()) {\n const fidGroups = videoMLine.findGroups('FID');\n\n // Remove the fid groups from the mline\n videoMLine.removeGroupsBySemantics('FID');\n\n // Get the rtx ssrcs and remove them from the mline\n for (const fidGroup of fidGroups) {\n const rtxSsrc = parseSecondarySSRC(fidGroup);\n\n videoMLine.removeSSRC(rtxSsrc);\n }\n }\n }\n\n return sdpTransformer.toRawSDP();\n }\n}\n","import { getLogger } from '@jitsi/logger';\n\nimport * as MediaType from '../../service/RTC/MediaType';\n\nimport {\n parsePrimarySSRC,\n parseSecondarySSRC,\n SdpTransformWrap\n} from './SdpTransformUtil';\n\nconst logger = getLogger(__filename);\n\n/**\n * Handles the work of keeping video ssrcs consistent across multiple\n * o/a cycles, making it such that all stream operations can be\n * kept local and do not need to be signaled.\n * NOTE: This only keeps the 'primary' video ssrc consistent: meaning\n * the primary video stream\n */\nexport default class SdpConsistency {\n /**\n * Constructor\n * @param {string} logPrefix the log prefix appended to every logged\n * message, currently used to distinguish for which\n * TraceablePeerConnection the instance works.\n */\n constructor(logPrefix) {\n this.clearVideoSsrcCache();\n this.logPrefix = logPrefix;\n }\n\n /**\n * Clear the cached video primary and primary rtx ssrcs so that\n * they will not be used for the next call to\n * makeVideoPrimarySsrcsConsistent\n */\n clearVideoSsrcCache() {\n this.cachedPrimarySsrc = null;\n this.injectRecvOnly = false;\n }\n\n /**\n * Explicitly set the primary ssrc to be used in\n * makeVideoPrimarySsrcsConsistent\n * @param {number} primarySsrc the primarySsrc to be used\n * in future calls to makeVideoPrimarySsrcsConsistent\n * @throws Error if primarySsrc is not a number\n */\n setPrimarySsrc(primarySsrc) {\n if (typeof primarySsrc !== 'number') {\n throw new Error('Primary SSRC must be a number!');\n }\n this.cachedPrimarySsrc = primarySsrc;\n }\n\n /**\n * Checks whether or not there is a primary video SSRC cached already.\n * @return {boolean}\n */\n hasPrimarySsrcCached() {\n return Boolean(this.cachedPrimarySsrc);\n }\n\n /**\n * Given an sdp string, either:\n * 1) record the primary video and primary rtx ssrcs to be\n * used in future calls to makeVideoPrimarySsrcsConsistent or\n * 2) change the primary and primary rtx ssrcs in the given sdp\n * to match the ones previously cached\n * @param {string} sdpStr the sdp string to (potentially)\n * change to make the video ssrcs consistent\n * @returns {string} a (potentially) modified sdp string\n * with ssrcs consistent with this class' cache\n */\n makeVideoPrimarySsrcsConsistent(sdpStr) {\n const sdpTransformer = new SdpTransformWrap(sdpStr);\n const videoMLine = sdpTransformer.selectMedia(MediaType.VIDEO)?.[0];\n\n if (!videoMLine) {\n logger.debug(`${this.logPrefix} no 'video' media found in the sdp: ${sdpStr}`);\n\n return sdpStr;\n }\n\n if (videoMLine.direction === 'recvonly') {\n // If the mline is recvonly, we'll add the primary\n // ssrc as a recvonly ssrc\n if (this.cachedPrimarySsrc && this.injectRecvOnly) {\n videoMLine.addSSRCAttribute({\n id: this.cachedPrimarySsrc,\n attribute: 'cname',\n value: `recvonly-${this.cachedPrimarySsrc}`\n });\n } else {\n logger.info(`${this.logPrefix} no SSRC found for the recvonly video stream!`);\n }\n } else {\n const newPrimarySsrc = videoMLine.getPrimaryVideoSsrc();\n\n if (!newPrimarySsrc) {\n logger.info(`${this.logPrefix} sdp-consistency couldn't parse new primary ssrc`);\n\n return sdpStr;\n }\n if (this.cachedPrimarySsrc) {\n videoMLine.replaceSSRC(newPrimarySsrc, this.cachedPrimarySsrc);\n for (const group of videoMLine.ssrcGroups) {\n if (group.semantics === 'FID') {\n const primarySsrc = parsePrimarySSRC(group);\n const rtxSsrc = parseSecondarySSRC(group);\n\n // eslint-disable-next-line max-depth\n if (primarySsrc === newPrimarySsrc) {\n group.ssrcs\n = `${this.cachedPrimarySsrc} ${rtxSsrc}`;\n }\n }\n }\n } else {\n this.cachedPrimarySsrc = newPrimarySsrc;\n }\n this.injectRecvOnly = true;\n }\n\n return sdpTransformer.toRawSDP();\n }\n}\n","import MediaDirection from '../../service/RTC/MediaDirection';\nimport * as MediaType from '../../service/RTC/MediaType';\n\nimport * as transform from 'sdp-transform';\n\nconst DEFAULT_NUM_OF_LAYERS = 3;\n\ninterface Description {\n type: RTCSdpType;\n sdp: string;\n}\n\ninterface Options {\n numOfLayers?: number\n}\n\n/**\n * This class handles SDP munging for enabling simulcast for local video streams in Unified plan. A set of random SSRCs\n * are generated for the higher layer streams and they are cached for a given mid. The cached SSRCs are then reused on\n * the subsequent iterations while munging the local description. This class also handles imploding of the simulcast\n * SSRCs for remote endpoints into the primary FID group in remote description since Jicofo signals all SSRCs relevant\n * to a given endpoint.\n */\nexport default class SdpSimulcast {\n private _options: Options;\n private _ssrcCache: Map>;\n\n /**\n * Creates a new instance.\n *\n * @param options\n */\n constructor(options: Options) {\n this._options = options;\n this._ssrcCache = new Map();\n\n if (!this._options.numOfLayers) {\n this._options.numOfLayers = DEFAULT_NUM_OF_LAYERS;\n }\n }\n\n /**\n * Updates the given media description using the SSRCs that were cached for the mid associated\n * with the media description and returns the modified media description.\n *\n * @param mLine\n * @returns\n */\n _fillSsrcsFromCache(mLine: transform.MediaDescription) : any {\n const mid = mLine.mid;\n const cachedSsrcs = this._ssrcCache.get(mid);\n const newSsrcs = this._parseSimLayers(mLine);\n const newMsid = this._getSsrcAttribute(mLine, newSsrcs[0], 'msid');\n const newCname = this._getSsrcAttribute(mLine, newSsrcs[0], 'cname');\n\n mLine.ssrcs = [];\n mLine.ssrcGroups = [];\n\n for (const ssrc of cachedSsrcs) {\n mLine.ssrcs.push({\n id: ssrc,\n attribute: 'msid',\n value: newMsid\n });\n mLine.ssrcs.push({\n id: ssrc,\n attribute: 'cname',\n value: newCname\n });\n }\n\n mLine.ssrcGroups.push({\n semantics: 'SIM',\n ssrcs: cachedSsrcs.join(' ')\n });\n\n return mLine;\n }\n\n /**\n * Generates a new set of SSRCs for the higher simulcast layers/streams and adds the attributes and SIM group to\n * the given media description and returns the modified media description.\n *\n * @param mLine\n * @param primarySsrc\n * @returns\n */\n _generateNewSsrcsForSimulcast(mLine: transform.MediaDescription, primarySsrc: number) : any {\n const cname = this._getSsrcAttribute(mLine, primarySsrc, 'cname');\n let msid = this._getSsrcAttribute(mLine, primarySsrc, 'msid');\n const addAssociatedAttributes = (mLine: transform.MediaDescription, ssrc: number) => {\n mLine.ssrcs.push({\n id: ssrc,\n attribute: 'cname',\n value: cname\n });\n mLine.ssrcs.push({\n id: ssrc,\n attribute: 'msid',\n value: msid\n });\n }\n\n // In Unified-plan mode, the a=ssrc lines with the msid attribute are not present (only cname attributes are\n // present) in the answers that Chrome and Safari generate for an offer received from Jicofo. Generate these\n // a=ssrc lines using the msid values from the a=msid line.\n if (!msid) {\n msid = mLine.msid;\n const primarySsrcs = mLine.ssrcs;\n\n primarySsrcs.forEach(ssrc => {\n mLine.ssrcs.push({\n id: ssrc.id,\n attribute: 'msid',\n value: msid\n });\n })\n }\n\n // Generate SIM layers.\n const simSsrcs = [];\n \n for (let i = 0; i < this._options.numOfLayers - 1; ++i) {\n const simSsrc = this._generateSsrc();\n\n addAssociatedAttributes(mLine, simSsrc);\n simSsrcs.push(simSsrc);\n }\n\n mLine.ssrcGroups = mLine.ssrcGroups || [];\n mLine.ssrcGroups.push({\n semantics: 'SIM',\n ssrcs: primarySsrc + ' ' + simSsrcs.join(' ')\n });\n \n return mLine;\n }\n\n /**\n * Returns a random number to be used for the SSRC.\n *\n * @returns\n */\n _generateSsrc() : number {\n const max = 0xffffffff;\n\n return Math.floor(Math.random() * max);\n }\n\n /**\n * Returns the requested attribute value for a SSRC from a given media description.\n *\n * @param mLine\n * @param ssrc\n * @param attributeName\n * @returns\n */\n _getSsrcAttribute(mLine: transform.MediaDescription, ssrc: number, attributeName: string) : string | undefined {\n return mLine.ssrcs?.find(\n ssrcInfo => Number(ssrcInfo.id) === ssrc\n && ssrcInfo.attribute === attributeName)?.value;\n }\n\n /**\n * Returns an array of all the primary SSRCs in the SIM group for a given media description.\n *\n * @param mLine\n * @returns\n */\n _parseSimLayers(mLine: transform.MediaDescription) : Array | null {\n const simGroup = mLine.ssrcGroups?.find(group => group.semantics === 'SIM');\n\n if (simGroup) {\n return simGroup.ssrcs.split(' ').map(ssrc => Number(ssrc));\n }\n\n if (mLine.ssrcs?.length) {\n return [ Number(mLine.ssrcs[0].id) ];\n }\n\n return null;\n }\n\n /**\n * Munges the given media description to enable simulcast for the video media sections that are in either have\n * SENDRECV or SENDONLY as the media direction thereby ignoring all the RECVONLY transceivers created for remote\n * endpoints.\n * NOTE: This needs to be called only when simulcast is enabled.\n *\n * @param description\n * @returns\n */\n mungeLocalDescription(description: Description) : Description {\n if (!description || !description.sdp) {\n return description;\n }\n const session = transform.parse(description.sdp);\n\n for (let media of session.media) {\n // Ignore recvonly and inactive transceivers created for remote sources.\n if (media.direction === MediaDirection.RECVONLY || media.direction === MediaDirection.INACTIVE) {\n continue;\n }\n\n // Ignore audio m-lines.\n if (media.type !== MediaType.VIDEO) {\n continue;\n }\n const mid = media.mid;\n const numSsrcs = new Set(media.ssrcs?.map(ssrcInfo => ssrcInfo.id));\n const numGroups = media.ssrcGroups?.length ?? 0;\n let primarySsrc: number;\n\n // Do not munge if the description has no ssrcs or if simulcast is already enabled.\n if (numSsrcs.size === 0 || numSsrcs.size > 2 || (numSsrcs.size === 2 && numGroups === 0)) {\n continue;\n }\n if (numSsrcs.size === 1) {\n primarySsrc = Number(media.ssrcs[0]?.id);\n } else {\n const fidGroup = media.ssrcGroups.find(group => group.semantics === 'FID');\n\n if (fidGroup) {\n primarySsrc = Number(fidGroup.ssrcs.split(' ')[0]);\n }\n }\n\n if (this._ssrcCache.has(mid)) {\n media = this._fillSsrcsFromCache(media);\n } else {\n media = this._generateNewSsrcsForSimulcast(media, primarySsrc);\n const simulcastSsrcs = this._parseSimLayers(media);\n\n // Update the SSRCs in the cache so that they can re-used for the same mid again.\n this._ssrcCache.set(mid, simulcastSsrcs);\n }\n }\n\n return new RTCSessionDescription({\n type: description.type,\n sdp: transform.write(session)\n });\n }\n\n /**\n * Munges the given media description by removing the SSRCs and related FID groups for the higher layer streams.\n *\n * @param description\n * @returns\n */\n mungeRemoteDescription(description: Description) : Description {\n if (!description || !description.sdp) {\n return description;\n }\n\n const session = transform.parse(description.sdp);\n\n for (const media of session.media) {\n if (media.type !== MediaType.VIDEO) {\n continue;\n }\n\n if (media.direction !== MediaDirection.SENDONLY) {\n continue;\n }\n\n // Ignore m-lines that do not have any SSRCs or SSRC groups. These are the ones associated with remote\n // sources that have left the call. These will be recycled when a new remote source joins the call.\n if (!media.ssrcGroups?.length || !media?.ssrcs.length) {\n continue;\n }\n\n // Cache the SSRCs and the source groups.\n const mungedSsrcs = new Set(media.ssrcs.slice());\n const mungedSsrcGroups = new Set(media.ssrcGroups.slice());\n const fidGroups = media.ssrcGroups.filter(group => group.semantics === 'FID');\n const simGroup = media.ssrcGroups.find(group => group.semantics === 'SIM');\n const primarySsrc = simGroup?.ssrcs.split(' ')[0];;\n\n // When simulcast and RTX are both enabled.\n if (fidGroups.length && simGroup) {\n const fidGroup = fidGroups.find(group => group.ssrcs.includes(primarySsrc));\n const secondarySsrc = fidGroup.ssrcs.split(' ')[1];\n\n for (const ssrcGroup of media.ssrcGroups) {\n if (ssrcGroup !== fidGroup) {\n mungedSsrcGroups.delete(ssrcGroup);\n }\n }\n for (const ssrc of media.ssrcs) {\n if (ssrc.id.toString() !== primarySsrc\n && ssrc.id.toString() !== secondarySsrc) {\n mungedSsrcs.delete(ssrc);\n }\n }\n\n // When simulcast is enabled but RTX is disabled.\n } else if (simGroup) {\n mungedSsrcGroups.delete(simGroup);\n\n for (const ssrc of media.ssrcs) {\n if (ssrc.id.toString() !== primarySsrc) {\n mungedSsrcs.delete(ssrc);\n }\n }\n }\n\n media.ssrcs = Array.from(mungedSsrcs);\n media.ssrcGroups = Array.from(mungedSsrcGroups);\n }\n\n return new RTCSessionDescription ({\n type: description.type,\n sdp: transform.write(session)\n });\n }\n}\n","import { getLogger } from '@jitsi/logger';\n\nimport { JitsiConferenceEvents } from '../../JitsiConferenceEvents';\nimport * as JitsiTrackEvents from '../../JitsiTrackEvents';\nimport RTCEvents from '../../service/RTC/RTCEvents';\nimport { createTrackStreamingStatusEvent } from '../../service/statistics/AnalyticsEvents';\nimport JitsiConference from '../../types/hand-crafted/JitsiConference';\nimport JitsiRemoteTrack from '../../types/hand-crafted/modules/RTC/JitsiRemoteTrack';\nimport RTC from '../../types/hand-crafted/modules/RTC/RTC';\nimport { VideoType } from '../../types/hand-crafted/service/RTC/VideoType';\nimport browser from '../browser';\nimport Statistics from '../statistics/statistics';\n\n/** Track streaming statuses. */\nexport enum TrackStreamingStatus {\n\n /**\n * Status indicating that streaming is currently active.\n */\n ACTIVE = 'active',\n\n /**\n * Status indicating that streaming is currently inactive.\n * Inactive means the streaming was stopped on purpose from the bridge, like exiting forwarded sources or\n * adaptivity decided to drop video because of not enough bandwidth.\n */\n INACTIVE = 'inactive',\n\n /**\n * Status indicating that streaming is currently interrupted.\n */\n INTERRUPTED = 'interrupted',\n\n /**\n * Status indicating that streaming is currently restoring.\n */\n RESTORING = 'restoring',\n }\n\ntype StreamingStatusMap = {\n // TODO: Replace this hand crafted VideoType when we convert VideoType.js to Typescript.\n videoType?: VideoType, \n startedMs?: number,\n p2p?: boolean,\n streamingStatus?: string,\n value?: number\n};\n\nconst logger = getLogger(__filename);\n\n/**\n * Default value of 500 milliseconds for {@link TrackStreamingStatusImpl.outOfForwardedSourcesTimeout}.\n */\nconst DEFAULT_NOT_IN_FORWARDED_SOURCES_TIMEOUT = 500;\n\n/**\n * Default value of 2500 milliseconds for {@link TrackStreamingStatusImpl.p2pRtcMuteTimeout}.\n */\nconst DEFAULT_P2P_RTC_MUTE_TIMEOUT = 2500;\n\n/**\n * Default value of 10000 milliseconds for {@link TrackStreamingStatusImpl.rtcMuteTimeout}.\n */\nconst DEFAULT_RTC_MUTE_TIMEOUT = 10000;\n\n/**\n * The time to wait a track to be restored. Track which was out of forwarded sources should be inactive and when\n * entering forwarded sources it becomes restoring and when data is received from bridge it will become active, but if\n * no data is received for some time we set status of that track streaming to interrupted.\n */\nconst DEFAULT_RESTORING_TIMEOUT = 10000;\n\n/**\n * Class is responsible for emitting JitsiTrackEvents.TRACK_STREAMING_STATUS_CHANGED events.\n */\nexport class TrackStreamingStatusImpl {\n rtc: RTC;\n conference: JitsiConference;\n track: JitsiRemoteTrack;\n\n /** This holds the timeout callback ID scheduled using window.setTimeout. */\n trackTimer: number | null;\n\n /**\n * If video track frozen detection through RTC mute event is supported, we wait some time until video track is\n * considered frozen. But because when the track falls out of forwarded sources it is expected for the video to\n * freeze this timeout must be significantly reduced in \"out of forwarded sources\" case.\n *\n * Basically this value is used instead of {@link rtcMuteTimeout} when track is not in forwarded sources.\n */\n outOfForwardedSourcesTimeout: number;\n\n /**\n * How long we are going to wait for the corresponding signaling mute event after the RTC video track muted\n * event is fired on the Media stream, before the connection interrupted is fired. The default value is\n * {@link DEFAULT_P2P_RTC_MUTE_TIMEOUT}.\n */\n p2pRtcMuteTimeout: number;\n\n /**\n * How long we're going to wait after the RTC video track muted event for the corresponding signalling mute\n * event, before the connection interrupted is fired. The default value is {@link DEFAULT_RTC_MUTE_TIMEOUT}.\n *\n * @returns amount of time in milliseconds\n */\n rtcMuteTimeout: number;\n\n /**\n * This holds a timestamp indicating when remote video track was RTC muted. The purpose of storing the\n * timestamp is to avoid the transition to disconnected status in case of legitimate video mute operation where\n * the signalling video muted event can arrive shortly after RTC muted event.\n *\n * The timestamp is measured in milliseconds obtained with Date.now().\n *\n * FIXME merge this logic with NO_DATA_FROM_SOURCE event implemented in JitsiLocalTrack by extending the event\n * to the remote track and allowing to set different timeout for local and remote tracks.\n */\n rtcMutedTimestamp: number | null;\n\n /** This holds the restoring timeout callback ID scheduled using window.setTimeout. */\n restoringTimer: ReturnType | null;\n\n /**\n * This holds the current streaming status (along with all the internal events that happen while in that\n * state).\n *\n * The goal is to send this information to the analytics backend for post-mortem analysis.\n */\n streamingStatusMap: StreamingStatusMap;\n\n _onP2PStatus: () => void;\n _onUserLeft: () => void;\n _onTrackRtcMuted: () => void;\n _onTrackRtcUnmuted: () => void;\n _onSignallingMuteChanged: () => void;\n _onTrackVideoTypeChanged: () => void;\n _onLastNValueChanged: () => void;\n _onForwardedSourcesChanged: () => void;\n\n /* eslint-disable max-params*/\n /**\n * Calculates the new {@link TrackStreamingStatus} based on the values given for some specific remote track. It is\n * assumed that the conference is currently in the JVB mode (in contrary to the P2P mode)\n * @param isInForwardedSources - indicates whether the track is in the forwarded sources set. When set to\n * false it means that JVB is not sending any video for the track.\n * @param isRestoringTimedout - if true it means that the track has been outside of forwarded sources too\n * long to be considered {@link TrackStreamingStatus.RESTORING}.\n * @param isVideoMuted - true if the track is video muted and we should not expect to receive any video.\n * @param isVideoTrackFrozen - if the current browser support video frozen detection then it will be set to\n * true when the video track is frozen. If the current browser does not support frozen detection the it's always\n * false.\n * @return {TrackStreamingStatus} the new streaming status for the track for whom the values above were provided.\n * @private\n */\n static _getNewStateForJvbMode(\n isInForwardedSources: boolean,\n isRestoringTimedout: boolean,\n isVideoMuted: boolean,\n isVideoTrackFrozen: boolean): TrackStreamingStatus {\n\n // We are currently not checking the endpoint connection status received from the JVB.\n if (isVideoMuted) {\n // If the connection is active according to JVB and the track is video muted there is no way for the\n // connection to be inactive, because the detection logic below only makes sense for video.\n return TrackStreamingStatus.ACTIVE;\n }\n\n // Logic when isVideoTrackFrozen is supported\n if (browser.supportsVideoMuteOnConnInterrupted()) {\n if (!isVideoTrackFrozen) {\n // If the video is playing we're good\n return TrackStreamingStatus.ACTIVE;\n } else if (isInForwardedSources) {\n return isRestoringTimedout ? TrackStreamingStatus.INTERRUPTED : TrackStreamingStatus.RESTORING;\n }\n\n return TrackStreamingStatus.INACTIVE;\n }\n\n // Because this browser is incapable of detecting frozen video we must rely on the forwarded sources value\n return isInForwardedSources ? TrackStreamingStatus.ACTIVE : TrackStreamingStatus.INACTIVE;\n }\n\n /* eslint-enable max-params*/\n\n /**\n * In P2P mode we don't care about any values coming from the JVB and the streaming status can be only active or\n * interrupted.\n * @param isVideoMuted - true if video muted\n * @param isVideoTrackFrozen - true if the video track for the remote track is currently frozen. If the\n * current browser does not support video frozen detection then it's always false.\n * @return {TrackStreamingStatus}\n * @private\n */\n static _getNewStateForP2PMode(isVideoMuted: boolean, isVideoTrackFrozen: boolean): TrackStreamingStatus {\n if (!browser.supportsVideoMuteOnConnInterrupted()) {\n // There's no way to detect problems in P2P when there's no video track frozen detection...\n return TrackStreamingStatus.ACTIVE;\n }\n\n return isVideoMuted || !isVideoTrackFrozen\n ? TrackStreamingStatus.ACTIVE : TrackStreamingStatus.INTERRUPTED;\n }\n\n /**\n * Creates new instance of TrackStreamingStatus.\n *\n * @constructor\n * @param rtc - the RTC service instance\n * @param conference - parent conference instance\n * @param {Object} options\n * @param {number} [options.p2pRtcMuteTimeout=2500] custom value for\n * {@link TrackStreamingStatusImpl.p2pRtcMuteTimeout}.\n * @param {number} [options.rtcMuteTimeout=2000] custom value for\n * {@link TrackStreamingStatusImpl.rtcMuteTimeout}.\n * @param {number} [options.outOfForwardedSourcesTimeout=500] custom value for\n * {@link TrackStreamingStatusImpl.outOfForwardedSourcesTimeout}.\n */\n constructor(rtc: RTC, conference: JitsiConference, track: JitsiRemoteTrack, options: {\n outOfForwardedSourcesTimeout: number,\n p2pRtcMuteTimeout: number,\n rtcMuteTimeout: number\n }) {\n this.rtc = rtc;\n this.conference = conference;\n this.track = track;\n\n this.restoringTimer = null;\n this.rtcMutedTimestamp = null;\n this.streamingStatusMap = {};\n this.trackTimer = null;\n\n this.outOfForwardedSourcesTimeout = typeof options.outOfForwardedSourcesTimeout === 'number'\n ? options.outOfForwardedSourcesTimeout : DEFAULT_NOT_IN_FORWARDED_SOURCES_TIMEOUT;\n\n this.p2pRtcMuteTimeout = typeof options.p2pRtcMuteTimeout === 'number'\n ? options.p2pRtcMuteTimeout : DEFAULT_P2P_RTC_MUTE_TIMEOUT;\n\n this.rtcMuteTimeout = typeof options.rtcMuteTimeout === 'number'\n ? options.rtcMuteTimeout : DEFAULT_RTC_MUTE_TIMEOUT;\n logger.info(`RtcMuteTimeout set to: ${this.rtcMuteTimeout}`);\n }\n\n /**\n * Gets the video frozen timeout for given source name.\n * @return how long are we going to wait since RTC video muted even, before a video track is considered\n * frozen.\n * @private\n */\n _getVideoFrozenTimeout(): number {\n const sourceName = this.track.getSourceName();\n\n return this.rtc.isInForwardedSources(sourceName)\n ? this.rtcMuteTimeout\n : this.conference.isP2PActive() ? this.p2pRtcMuteTimeout : this.outOfForwardedSourcesTimeout;\n }\n\n /**\n * Initializes TrackStreamingStatus and bind required event listeners.\n */\n init(): void {\n // Handles P2P status changes\n this._onP2PStatus = this.figureOutStreamingStatus.bind(this);\n this.conference.on(JitsiConferenceEvents.P2P_STATUS, this._onP2PStatus);\n\n // Used to send analytics events for the participant that left the call.\n this._onUserLeft = this.onUserLeft.bind(this);\n this.conference.on(JitsiConferenceEvents.USER_LEFT, this._onUserLeft);\n\n // On some browsers MediaStreamTrack trigger \"onmute\"/\"onunmute\" events for video type tracks when they stop\n // receiving data which is often a sign that remote user is having connectivity issues.\n if (browser.supportsVideoMuteOnConnInterrupted()) {\n\n this._onTrackRtcMuted = this.onTrackRtcMuted.bind(this);\n this.rtc.addListener(RTCEvents.REMOTE_TRACK_MUTE, this._onTrackRtcMuted);\n\n this._onTrackRtcUnmuted = this.onTrackRtcUnmuted.bind(this);\n this.rtc.addListener(RTCEvents.REMOTE_TRACK_UNMUTE, this._onTrackRtcUnmuted);\n\n // Listened which will be bound to JitsiRemoteTrack to listen for signalling mute/unmute events.\n this._onSignallingMuteChanged = this.onSignallingMuteChanged.bind(this);\n this.track.on(JitsiTrackEvents.TRACK_MUTE_CHANGED, this._onSignallingMuteChanged);\n\n // Used to send an analytics event when the video type changes.\n this._onTrackVideoTypeChanged = this.onTrackVideoTypeChanged.bind(this);\n this.track.on(JitsiTrackEvents.TRACK_VIDEOTYPE_CHANGED, this._onTrackVideoTypeChanged);\n }\n\n this._onForwardedSourcesChanged = this.onForwardedSourcesChanged.bind(this);\n this.conference.on(JitsiConferenceEvents.FORWARDED_SOURCES_CHANGED, this._onForwardedSourcesChanged);\n\n this._onLastNValueChanged = this.figureOutStreamingStatus.bind(this);\n this.rtc.on(RTCEvents.LASTN_VALUE_CHANGED, this._onLastNValueChanged);\n }\n\n /**\n * Removes all event listeners and disposes of all resources held by this instance.\n */\n dispose(): void {\n if (browser.supportsVideoMuteOnConnInterrupted()) {\n this.rtc.removeListener(RTCEvents.REMOTE_TRACK_MUTE, this._onTrackRtcMuted);\n this.rtc.removeListener(RTCEvents.REMOTE_TRACK_UNMUTE, this._onTrackRtcUnmuted);\n\n this.track.off(JitsiTrackEvents.TRACK_MUTE_CHANGED, this._onSignallingMuteChanged);\n }\n\n this.conference.off(JitsiConferenceEvents.FORWARDED_SOURCES_CHANGED, this._onForwardedSourcesChanged);\n this.conference.off(JitsiConferenceEvents.P2P_STATUS, this._onP2PStatus);\n this.conference.off(JitsiConferenceEvents.USER_LEFT, this._onUserLeft);\n this.rtc.removeListener(RTCEvents.LASTN_VALUE_CHANGED, this._onLastNValueChanged);\n\n this.clearTimeout();\n this.clearRtcMutedTimestamp();\n this.maybeSendTrackStreamingStatusEvent(Date.now());\n this.figureOutStreamingStatus();\n }\n\n /**\n * Changes streaming status.\n * @param newStatus\n */\n _changeStreamingStatus(newStatus: TrackStreamingStatus): void {\n if (this.track.getTrackStreamingStatus() !== newStatus) {\n\n const sourceName = this.track.getSourceName();\n\n this.track._setTrackStreamingStatus(newStatus);\n\n logger.debug(`Emit track streaming status(${Date.now()}) ${sourceName}: ${newStatus}`);\n\n // Log the event on CallStats\n Statistics.sendLog(\n JSON.stringify({\n id: 'track.streaming.status',\n track: sourceName,\n status: newStatus\n }));\n\n this.track.emit(JitsiTrackEvents.TRACK_STREAMING_STATUS_CHANGED, newStatus);\n }\n }\n\n /**\n * Reset the postponed \"streaming interrupted\" event which was previously scheduled as a timeout on RTC 'onmute'\n * event.\n */\n clearTimeout(): void {\n if (this.trackTimer) {\n window.clearTimeout(this.trackTimer);\n this.trackTimer = null;\n }\n }\n\n /**\n * Clears the timestamp of the RTC muted event for remote video track.\n */\n clearRtcMutedTimestamp(): void {\n this.rtcMutedTimestamp = null;\n }\n\n /**\n * Checks if track is considered frozen.\n * @return true if the video has frozen or false when it's either not considered frozen\n * (yet) or if freeze detection is not supported by the current browser.\n *\n * FIXME merge this logic with NO_DATA_FROM_SOURCE event implemented in JitsiLocalTrack by extending the event to\n * the remote track and allowing to set different timeout for local and remote tracks.\n */\n isVideoTrackFrozen(): boolean {\n if (!browser.supportsVideoMuteOnConnInterrupted()) {\n return false;\n }\n\n const isVideoRTCMuted = this.track.isWebRTCTrackMuted();\n const rtcMutedTimestamp = this.rtcMutedTimestamp;\n const timeout = this._getVideoFrozenTimeout();\n\n return isVideoRTCMuted && typeof rtcMutedTimestamp === 'number' && (Date.now() - rtcMutedTimestamp) >= timeout;\n }\n\n /**\n * Figures out (and updates) the current streaming status for the track identified by the source name.\n */\n figureOutStreamingStatus(): void {\n const sourceName = this.track.getSourceName();\n const inP2PMode = this.conference.isP2PActive();\n const isRestoringTimedOut = this._isRestoringTimedout();\n const audioOnlyMode = this.conference.getLastN() === 0;\n\n // NOTE Overriding videoMuted to true for audioOnlyMode should disable any detection based on video playback or\n // forwarded sources.\n const isVideoMuted = this.track.isMuted() || audioOnlyMode;\n const isVideoTrackFrozen = this.isVideoTrackFrozen();\n const isInForwardedSources = this.rtc.isInForwardedSources(sourceName);\n\n const newState\n = inP2PMode\n ? TrackStreamingStatusImpl._getNewStateForP2PMode(\n isVideoMuted,\n isVideoTrackFrozen)\n : TrackStreamingStatusImpl._getNewStateForJvbMode(\n isInForwardedSources,\n isRestoringTimedOut,\n isVideoMuted,\n isVideoTrackFrozen);\n\n // if the new state is not restoring clear timers and timestamps that we use to track the restoring state\n if (newState !== TrackStreamingStatus.RESTORING) {\n this._clearRestoringTimer();\n }\n\n logger.debug(\n `Figure out conn status for ${sourceName}, is video muted: ${\n isVideoMuted} video track frozen: ${\n isVideoTrackFrozen} p2p mode: ${\n inP2PMode} is in forwarded sources: ${\n isInForwardedSources} currentStatus => newStatus: ${\n this.track.getTrackStreamingStatus()} => ${newState}`);\n\n const oldStreamingStatus = this.streamingStatusMap || {};\n\n // Send an analytics event (guard on either the p2p flag or the streaming status has changed since the last\n // time this code block run).\n if (!('p2p' in oldStreamingStatus)\n || !('streamingStatus' in oldStreamingStatus)\n || oldStreamingStatus.p2p !== inP2PMode\n || oldStreamingStatus.streamingStatus !== newState) {\n\n const nowMs = Date.now();\n\n this.maybeSendTrackStreamingStatusEvent(nowMs);\n\n this.streamingStatusMap = {\n ...oldStreamingStatus,\n streamingStatus: newState,\n p2p: inP2PMode,\n startedMs: nowMs\n };\n\n // sometimes (always?) we're late to hook the TRACK_VIDEOTYPE_CHANGED event and the video type is not in\n // oldStreamingStatus.\n if (!('videoType' in this.streamingStatusMap)) {\n this.streamingStatusMap.videoType = this.track.getVideoType();\n }\n }\n this._changeStreamingStatus(newState);\n }\n\n /**\n * Computes the duration of the current streaming status for the track (i.e. 15 seconds in the INTERRUPTED state)\n * and sends a track streaming status event.\n * @param nowMs - The current time (in millis).\n */\n maybeSendTrackStreamingStatusEvent(nowMs: number): void {\n const trackStreamingStatus = this.streamingStatusMap;\n\n if (trackStreamingStatus\n && 'startedMs' in trackStreamingStatus\n && 'videoType' in trackStreamingStatus\n && 'streamingStatus' in trackStreamingStatus\n && 'p2p' in trackStreamingStatus) {\n trackStreamingStatus.value = nowMs - trackStreamingStatus.startedMs;\n Statistics.sendAnalytics(createTrackStreamingStatusEvent(trackStreamingStatus));\n }\n }\n\n /**\n * On change in forwarded sources set check all leaving and entering track to change their corresponding statuses.\n *\n * @param leavingForwardedSources - The array of sourceName leaving forwarded sources.\n * @param enteringForwardedSources - The array of sourceName entering forwarded sources.\n * @param timestamp - The time in millis\n * @private\n */\n onForwardedSourcesChanged(\n leavingForwardedSources: string[] = [],\n enteringForwardedSources: string[] = [],\n timestamp: number): void {\n\n const sourceName = this.track.getSourceName();\n\n logger.debug(`Fowarded sources changed leaving=${leavingForwardedSources}, entering=${\n enteringForwardedSources} at ${timestamp}`);\n\n // If the browser doesn't fire the mute/onmute events when the remote peer stops/starts sending media,\n // calculate the streaming status for all the tracks since it won't get triggered automatically on the track\n // that has started/stopped receiving media.\n if (!browser.supportsVideoMuteOnConnInterrupted()) {\n this.figureOutStreamingStatus();\n }\n\n if (leavingForwardedSources.includes(sourceName)) {\n this.track._clearEnteredForwardedSourcesTimestamp();\n this._clearRestoringTimer();\n browser.supportsVideoMuteOnConnInterrupted() && this.figureOutStreamingStatus();\n }\n\n if (enteringForwardedSources.includes(sourceName)) {\n // store the timestamp this track is entering forwarded sources\n this.track._setEnteredForwardedSourcesTimestamp(timestamp);\n browser.supportsVideoMuteOnConnInterrupted() && this.figureOutStreamingStatus();\n }\n }\n\n /**\n * Clears the restoring timer for video track and the timestamp for entering forwarded sources.\n */\n _clearRestoringTimer(): void {\n const rTimer = this.restoringTimer;\n\n if (rTimer) {\n clearTimeout(rTimer);\n this.restoringTimer = null;\n }\n }\n\n /**\n * Checks whether a track had stayed enough in restoring state, compares current time and the time the track\n * entered in forwarded sources. If it hasn't timedout and there is no timer added, add new timer in order to give\n * it more time to become active or mark it as interrupted on next check.\n *\n * @returns true if the track was in restoring state more than the timeout\n * ({@link DEFAULT_RESTORING_TIMEOUT}.) in order to set its status to interrupted.\n * @private\n */\n _isRestoringTimedout(): boolean {\n const enteredForwardedSourcesTimestamp = this.track._getEnteredForwardedSourcesTimestamp();\n\n if (enteredForwardedSourcesTimestamp\n && (Date.now() - enteredForwardedSourcesTimestamp) >= DEFAULT_RESTORING_TIMEOUT) {\n return true;\n }\n\n // still haven't reached timeout, if there is no timer scheduled, schedule one so we can track the restoring\n // state and change it after reaching the timeout\n const rTimer = this.restoringTimer;\n\n if (!rTimer) {\n this.restoringTimer = setTimeout(() => this.figureOutStreamingStatus(), DEFAULT_RESTORING_TIMEOUT);\n }\n\n return false;\n }\n\n /** Checks whether a track is the current track. */\n _isCurrentTrack(track: JitsiRemoteTrack): boolean {\n return track.getSourceName() === this.track.getSourceName();\n }\n\n /**\n * Sends a last/final track streaming status event for the track of the user that left the conference.\n * @param id - The id of the participant that left the conference.\n */\n onUserLeft(id: string): void {\n if (this.track.getParticipantId() === id) {\n this.maybeSendTrackStreamingStatusEvent(Date.now());\n this.streamingStatusMap = {};\n }\n }\n\n /**\n * Handles RTC 'onmute' event for the video track.\n *\n * @param track - The video track for which 'onmute' event will be processed.\n */\n onTrackRtcMuted(track: JitsiRemoteTrack): void {\n if (!this._isCurrentTrack(track)) {\n return;\n }\n\n const sourceName = track.getSourceName();\n\n logger.debug(`Detector track RTC muted: ${sourceName}`, Date.now());\n\n this.rtcMutedTimestamp = Date.now();\n if (!track.isMuted()) {\n // If the user is not muted according to the signalling we'll give it some time, before the streaming\n // interrupted event is triggered.\n this.clearTimeout();\n\n // The timeout is reduced when track is not in the forwarded sources\n const timeout = this._getVideoFrozenTimeout();\n\n this.trackTimer = window.setTimeout(() => {\n logger.debug(`Set RTC mute timeout for: ${sourceName} of ${timeout} ms`);\n this.clearTimeout();\n this.figureOutStreamingStatus();\n }, timeout);\n }\n }\n\n /**\n * Handles RTC 'onunmute' event for the video track.\n *\n * @param track - The video track for which 'onunmute' event will be processed.\n */\n onTrackRtcUnmuted(track: JitsiRemoteTrack): void {\n if (!this._isCurrentTrack(track)) {\n return;\n }\n\n const sourceName = this.track.getSourceName();\n\n logger.debug(`Detector track RTC unmuted: ${sourceName}`, Date.now());\n\n this.clearTimeout();\n this.clearRtcMutedTimestamp();\n\n this.figureOutStreamingStatus();\n }\n\n /**\n * Here the signalling \"mute\"/\"unmute\" events are processed.\n *\n * @param track - The remote video track for which the signalling mute/unmute event will be\n * processed.\n */\n onSignallingMuteChanged(track: JitsiRemoteTrack): void {\n if (!this._isCurrentTrack(track)) {\n return;\n }\n\n const sourceName = this.track.getSourceName();\n\n logger.debug(`Detector on track signalling mute changed: ${sourceName}`, track.isMuted());\n\n this.figureOutStreamingStatus();\n }\n\n /**\n * Sends a track streaming status event as a result of the video type changing.\n * @deprecated this will go away with full multiple streams support\n * @param type - The video type.\n */\n onTrackVideoTypeChanged(type: VideoType): void {\n const nowMs = Date.now();\n\n this.maybeSendTrackStreamingStatusEvent(nowMs);\n\n this.streamingStatusMap = {\n ...this.streamingStatusMap || {},\n videoType: type,\n startedMs: nowMs\n };\n }\n}\n\nexport default TrackStreamingStatusImpl;\n","import * as JitsiTrackEvents from '../../JitsiTrackEvents';\nimport { createTtfmEvent } from '../../service/statistics/AnalyticsEvents';\nimport TrackStreamingStatusImpl, { TrackStreamingStatus } from '../connectivity/TrackStreamingStatus';\nimport FeatureFlags from '../flags/FeatureFlags';\nimport Statistics from '../statistics/statistics';\n\nimport JitsiTrack from './JitsiTrack';\n\nconst logger = require('@jitsi/logger').getLogger(__filename);\n\nconst RTCEvents = require('../../service/RTC/RTCEvents');\n\nlet ttfmTrackerAudioAttached = false;\nlet ttfmTrackerVideoAttached = false;\n\n/**\n * List of container events that we are going to process. _onContainerEventHandler will be added as listener to the\n * container for every event in the list.\n */\nconst containerEvents = [ 'abort', 'canplaythrough', 'ended', 'error' ];\n\n/* eslint-disable max-params */\n\n/**\n * Represents a single media track (either audio or video).\n */\nexport default class JitsiRemoteTrack extends JitsiTrack {\n /**\n * Creates new JitsiRemoteTrack instance.\n * @param {RTC} rtc the RTC service instance.\n * @param {JitsiConference} conference the conference to which this track\n * belongs to\n * @param {string} ownerEndpointId the endpoint ID of the track owner\n * @param {MediaStream} stream WebRTC MediaStream, parent of the track\n * @param {MediaStreamTrack} track underlying WebRTC MediaStreamTrack for\n * the new JitsiRemoteTrack\n * @param {MediaType} mediaType the type of the media\n * @param {VideoType} videoType the type of the video if applicable\n * @param {number} ssrc the SSRC number of the Media Stream\n * @param {boolean} muted the initial muted state\n * @param {boolean} isP2P indicates whether or not this track belongs to a\n * P2P session\n * @param {String} sourceName the source name signaled for the track\n * @throws {TypeError} if ssrc is not a number.\n * @constructor\n */\n constructor(\n rtc,\n conference,\n ownerEndpointId,\n stream,\n track,\n mediaType,\n videoType,\n ssrc,\n muted,\n isP2P,\n sourceName) {\n super(\n conference,\n stream,\n track,\n () => {\n // Nothing to do if the track is inactive.\n },\n mediaType,\n videoType);\n this.rtc = rtc;\n\n // Prevent from mixing up type of SSRC which should be a number\n if (typeof ssrc !== 'number') {\n throw new TypeError(`SSRC ${ssrc} is not a number`);\n }\n this.ssrc = ssrc;\n this.ownerEndpointId = ownerEndpointId;\n this.muted = muted;\n this.isP2P = isP2P;\n this._sourceName = sourceName;\n this._trackStreamingStatus = null;\n this._trackStreamingStatusImpl = null;\n\n /**\n * This holds the timestamp indicating when remote video track entered forwarded sources set. Track entering\n * forwardedSources will have streaming status restoring and when we start receiving video will become active,\n * but if video is not received for certain time {@link DEFAULT_RESTORING_TIMEOUT} that track streaming status\n * will become interrupted.\n */\n this._enteredForwardedSourcesTimestamp = null;\n\n this.addEventListener = this.on = this._addEventListener.bind(this);\n this.removeEventListener = this.off = this._removeEventListener.bind(this);\n\n logger.debug(`New remote track added: ${this}`);\n\n // we want to mark whether the track has been ever muted\n // to detect ttfm events for startmuted conferences, as it can\n // significantly increase ttfm values\n this.hasBeenMuted = muted;\n\n // Bind 'onmute' and 'onunmute' event handlers\n if (this.rtc && this.track) {\n this._bindTrackHandlers();\n }\n this._containerHandlers = {};\n containerEvents.forEach(event => {\n this._containerHandlers[event] = this._containerEventHandler.bind(this, event);\n });\n }\n\n /* eslint-enable max-params */\n /**\n * Attaches the track handlers.\n *\n * @returns {void}\n */\n _bindTrackHandlers() {\n this.track.addEventListener('mute', () => this._onTrackMute());\n this.track.addEventListener('unmute', () => this._onTrackUnmute());\n this.track.addEventListener('ended', () => {\n logger.debug(`\"onended\" event(${Date.now()}): ${this}`);\n });\n }\n\n /**\n * Overrides addEventListener method to init TrackStreamingStatus instance when there are listeners for the\n * {@link JitsiTrackEvents.TRACK_STREAMING_STATUS_CHANGED} event.\n *\n * @param {string} event - event name\n * @param {function} handler - event handler\n */\n _addEventListener(event, handler) {\n super.addListener(event, handler);\n\n if (FeatureFlags.isSourceNameSignalingEnabled()\n && event === JitsiTrackEvents.TRACK_STREAMING_STATUS_CHANGED\n && this.listenerCount(JitsiTrackEvents.TRACK_STREAMING_STATUS_CHANGED)\n && !this._trackStreamingStatusImpl\n ) {\n this._initTrackStreamingStatus();\n logger.debug(`Initializing track streaming status: ${this._sourceName}`);\n }\n }\n\n /**\n * Overrides removeEventListener method to dispose TrackStreamingStatus instance.\n *\n * @param {string} event - event name\n * @param {function} handler - event handler\n */\n _removeEventListener(event, handler) {\n super.removeListener(event, handler);\n\n if (FeatureFlags.isSourceNameSignalingEnabled()\n && event === JitsiTrackEvents.TRACK_STREAMING_STATUS_CHANGED\n && !this.listenerCount(JitsiTrackEvents.TRACK_STREAMING_STATUS_CHANGED)\n ) {\n this._disposeTrackStreamingStatus();\n logger.debug(`Disposing track streaming status: ${this._sourceName}`);\n }\n }\n\n /**\n * Callback invoked when the track is muted. Emits an event notifying\n * listeners of the mute event.\n *\n * @private\n * @returns {void}\n */\n _onTrackMute() {\n logger.debug(`\"onmute\" event(${Date.now()}): ${this}`);\n\n this.rtc.eventEmitter.emit(RTCEvents.REMOTE_TRACK_MUTE, this);\n }\n\n /**\n * Callback invoked when the track is unmuted. Emits an event notifying\n * listeners of the mute event.\n *\n * @private\n * @returns {void}\n */\n _onTrackUnmute() {\n logger.debug(`\"onunmute\" event(${Date.now()}): ${this}`);\n\n this.rtc.eventEmitter.emit(RTCEvents.REMOTE_TRACK_UNMUTE, this);\n }\n\n /**\n * Removes attached event listeners and dispose TrackStreamingStatus .\n *\n * @returns {Promise}\n */\n dispose() {\n if (FeatureFlags.isSourceNameSignalingEnabled()) {\n this._disposeTrackStreamingStatus();\n }\n\n return super.dispose();\n }\n\n /**\n * Sets current muted status and fires an events for the change.\n * @param value the muted status.\n */\n setMute(value) {\n if (this.muted === value) {\n return;\n }\n\n if (value) {\n this.hasBeenMuted = true;\n }\n\n // we can have a fake video stream\n if (this.stream) {\n this.stream.muted = value;\n }\n\n this.muted = value;\n this.emit(JitsiTrackEvents.TRACK_MUTE_CHANGED, this);\n }\n\n /**\n * Returns the current muted status of the track.\n * @returns {boolean|*|JitsiRemoteTrack.muted} true if the track is\n * muted and false otherwise.\n */\n isMuted() {\n return this.muted;\n }\n\n /**\n * Returns the participant id which owns the track.\n *\n * @returns {string} the id of the participants. It corresponds to the\n * Colibri endpoint id/MUC nickname in case of Jitsi-meet.\n */\n getParticipantId() {\n return this.ownerEndpointId;\n }\n\n /**\n * Return false;\n */\n isLocal() {\n return false;\n }\n\n /**\n * Returns the synchronization source identifier (SSRC) of this remote\n * track.\n *\n * @returns {number} the SSRC of this remote track.\n */\n getSSRC() {\n return this.ssrc;\n }\n\n\n /**\n * Returns the tracks source name\n *\n * @returns {string} the track's source name\n */\n getSourceName() {\n return this._sourceName;\n }\n\n /**\n * Changes the video type of the track.\n *\n * @param {string} type - The new video type(\"camera\", \"desktop\").\n */\n _setVideoType(type) {\n if (this.videoType === type) {\n return;\n }\n this.videoType = type;\n this.emit(JitsiTrackEvents.TRACK_VIDEOTYPE_CHANGED, type);\n }\n\n /**\n * Handles track play events.\n */\n _playCallback() {\n const type = this.isVideoTrack() ? 'video' : 'audio';\n\n const now = window.performance.now();\n\n console.log(`(TIME) Render ${type}:\\t`, now);\n this.conference.getConnectionTimes()[`${type}.render`] = now;\n\n // The conference can be started without calling GUM\n // FIXME if there would be a module for connection times this kind\n // of logic (gumDuration or ttfm) should end up there\n const gumStart = window.connectionTimes['obtainPermissions.start'];\n const gumEnd = window.connectionTimes['obtainPermissions.end'];\n const gumDuration\n = !isNaN(gumEnd) && !isNaN(gumStart) ? gumEnd - gumStart : 0;\n\n // Subtract the muc.joined-to-session-initiate duration because jicofo\n // waits until there are 2 participants to start Jingle sessions.\n const ttfm = now\n - (this.conference.getConnectionTimes()['session.initiate']\n - this.conference.getConnectionTimes()['muc.joined'])\n - gumDuration;\n\n this.conference.getConnectionTimes()[`${type}.ttfm`] = ttfm;\n console.log(`(TIME) TTFM ${type}:\\t`, ttfm);\n\n Statistics.sendAnalytics(createTtfmEvent(\n {\n 'media_type': type,\n muted: this.hasBeenMuted,\n value: ttfm\n }));\n\n }\n\n /**\n * Attach time to first media tracker only if there is conference and only\n * for the first element.\n * @param container the HTML container which can be 'video' or 'audio'\n * element.\n * @private\n */\n _attachTTFMTracker(container) {\n if ((ttfmTrackerAudioAttached && this.isAudioTrack())\n || (ttfmTrackerVideoAttached && this.isVideoTrack())) {\n return;\n }\n\n if (this.isAudioTrack()) {\n ttfmTrackerAudioAttached = true;\n }\n if (this.isVideoTrack()) {\n ttfmTrackerVideoAttached = true;\n }\n\n container.addEventListener('canplay', this._playCallback.bind(this));\n }\n\n /**\n * Called when the track has been attached to a new container.\n *\n * @param {HTMLElement} container the HTML container which can be 'video' or 'audio' element.\n * @private\n */\n _onTrackAttach(container) {\n containerEvents.forEach(event => {\n container.addEventListener(event, this._containerHandlers[event]);\n });\n }\n\n /**\n * Called when the track has been detached from a container.\n *\n * @param {HTMLElement} container the HTML container which can be 'video' or 'audio' element.\n * @private\n */\n _onTrackDetach(container) {\n containerEvents.forEach(event => {\n container.removeEventListener(event, this._containerHandlers[event]);\n });\n }\n\n /**\n * An event handler for events triggered by the attached container.\n *\n * @param {string} type - The type of the event.\n */\n _containerEventHandler(type) {\n logger.debug(`${type} handler was called for a container with attached ${this}`);\n }\n\n /**\n * Returns a string with a description of the current status of the track.\n *\n * @returns {string}\n */\n _getStatus() {\n const { enabled, muted, readyState } = this.track;\n\n return `readyState: ${readyState}, muted: ${muted}, enabled: ${enabled}`;\n }\n\n /**\n * Initializes trackStreamingStatusImpl.\n */\n _initTrackStreamingStatus() {\n const config = this.conference.options.config;\n\n this._trackStreamingStatus = TrackStreamingStatus.ACTIVE;\n\n this._trackStreamingStatusImpl = new TrackStreamingStatusImpl(\n this.rtc,\n this.conference,\n this,\n {\n // These options are not public API, leaving it here only as an entry point through config for\n // tuning up purposes. Default values should be adjusted as soon as optimal values are discovered.\n p2pRtcMuteTimeout: config._p2pConnStatusRtcMuteTimeout,\n rtcMuteTimeout: config._peerConnStatusRtcMuteTimeout,\n outOfForwardedSourcesTimeout: config._peerConnStatusOutOfLastNTimeout\n });\n\n this._trackStreamingStatusImpl.init();\n }\n\n /**\n * Disposes trackStreamingStatusImpl and clears trackStreamingStatus.\n */\n _disposeTrackStreamingStatus() {\n if (this._trackStreamingStatusImpl) {\n this._trackStreamingStatusImpl.dispose();\n this._trackStreamingStatusImpl = null;\n this._trackStreamingStatus = null;\n }\n }\n\n /**\n * Updates track's streaming status.\n *\n * @param {string} state the current track streaming state. {@link TrackStreamingStatus}.\n */\n _setTrackStreamingStatus(status) {\n this._trackStreamingStatus = status;\n }\n\n /**\n * Returns track's streaming status.\n *\n * @returns {string} the streaming status TrackStreamingStatus of the track. Returns null\n * if trackStreamingStatusImpl hasn't been initialized.\n *\n * {@link TrackStreamingStatus}.\n */\n getTrackStreamingStatus() {\n return this._trackStreamingStatus;\n }\n\n /**\n * Clears the timestamp of when the track entered forwarded sources.\n */\n _clearEnteredForwardedSourcesTimestamp() {\n this._enteredForwardedSourcesTimestamp = null;\n }\n\n /**\n * Updates the timestamp of when the track entered forwarded sources.\n *\n * @param {number} timestamp the time in millis\n */\n _setEnteredForwardedSourcesTimestamp(timestamp) {\n this._enteredForwardedSourcesTimestamp = timestamp;\n }\n\n /**\n * Returns the timestamp of when the track entered forwarded sources.\n *\n * @returns {number} the time in millis\n */\n _getEnteredForwardedSourcesTimestamp() {\n return this._enteredForwardedSourcesTimestamp;\n }\n\n /**\n * Creates a text representation of this remote track instance.\n * @return {string}\n */\n toString() {\n return `RemoteTrack[userID: ${this.getParticipantId()}, type: ${this.getType()}, ssrc: ${\n this.getSSRC()}, p2p: ${this.isP2P}, sourceName: ${this._sourceName}, status: ${this._getStatus()}]`;\n }\n}\n","import { getLogger } from '@jitsi/logger';\nimport transform from 'sdp-transform';\n\nimport MediaDirection from '../../service/RTC/MediaDirection';\nimport * as MediaType from '../../service/RTC/MediaType';\nimport VideoType from '../../service/RTC/VideoType';\nimport browser from '../browser';\n\nconst logger = getLogger(__filename);\nconst DESKTOP_SHARE_RATE = 500000;\nconst LD_BITRATE = 200000;\nconst SD_BITRATE = 700000;\nconst SIM_LAYER_1_RID = '1';\nconst SIM_LAYER_2_RID = '2';\nconst SIM_LAYER_3_RID = '3';\n\nexport const HD_BITRATE = 2500000;\nexport const HD_SCALE_FACTOR = 1;\nexport const LD_SCALE_FACTOR = 4;\nexport const SD_SCALE_FACTOR = 2;\nexport const SIM_LAYER_RIDS = [ SIM_LAYER_1_RID, SIM_LAYER_2_RID, SIM_LAYER_3_RID ];\n\n/**\n * Handles track related operations on TraceablePeerConnection when browser is\n * running in unified plan mode.\n */\nexport class TPCUtils {\n /**\n * Creates a new instance for a given TraceablePeerConnection\n *\n * @param peerconnection - the tpc instance for which we have utility functions.\n */\n constructor(peerconnection) {\n this.pc = peerconnection;\n const bitrateSettings = this.pc.options?.videoQuality?.maxBitratesVideo;\n const standardBitrates = {\n low: LD_BITRATE,\n standard: SD_BITRATE,\n high: HD_BITRATE\n };\n\n // Check if the max. bitrates for video are specified through config.js videoQuality settings.\n // Right now only VP8 bitrates are configured on the simulcast encodings, VP9 bitrates have to be\n // configured on the SDP using b:AS line.\n this.videoBitrates = bitrateSettings ?? standardBitrates;\n const encodingBitrates = this.videoBitrates.VP8 ?? this.videoBitrates;\n\n /**\n * The startup configuration for the stream encodings that are applicable to\n * the video stream when a new sender is created on the peerconnection. The initial\n * config takes into account the differences in browser's simulcast implementation.\n *\n * Encoding parameters:\n * active - determine the on/off state of a particular encoding.\n * maxBitrate - max. bitrate value to be applied to that particular encoding\n * based on the encoding's resolution and config.js videoQuality settings if applicable.\n * rid - Rtp Stream ID that is configured for a particular simulcast stream.\n * scaleResolutionDownBy - the factor by which the encoding is scaled down from the\n * original resolution of the captured video.\n */\n this.localStreamEncodingsConfig = [\n {\n active: true,\n maxBitrate: browser.isFirefox() ? encodingBitrates.high : encodingBitrates.low,\n rid: SIM_LAYER_1_RID,\n scaleResolutionDownBy: browser.isFirefox() ? HD_SCALE_FACTOR : LD_SCALE_FACTOR\n },\n {\n active: true,\n maxBitrate: encodingBitrates.standard,\n rid: SIM_LAYER_2_RID,\n scaleResolutionDownBy: SD_SCALE_FACTOR\n },\n {\n active: true,\n maxBitrate: browser.isFirefox() ? encodingBitrates.low : encodingBitrates.high,\n rid: SIM_LAYER_3_RID,\n scaleResolutionDownBy: browser.isFirefox() ? LD_SCALE_FACTOR : HD_SCALE_FACTOR\n }\n ];\n }\n\n /**\n * Obtains stream encodings that need to be configured on the given track based\n * on the track media type and the simulcast setting.\n * @param {JitsiLocalTrack} localTrack\n */\n _getStreamEncodings(localTrack) {\n if (this.pc.isSimulcastOn() && localTrack.isVideoTrack()) {\n return this.localStreamEncodingsConfig;\n }\n\n return localTrack.isVideoTrack()\n ? [ {\n active: true,\n maxBitrate: this.videoBitrates.high\n } ]\n : [ { active: true } ];\n }\n\n /**\n * Ensures that the ssrcs associated with a FID ssrc-group appear in the correct order, i.e.,\n * the primary ssrc first and the secondary rtx ssrc later. This is important for unified\n * plan since we have only one FID group per media description.\n * @param {Object} description the webRTC session description instance for the remote\n * description.\n * @private\n */\n ensureCorrectOrderOfSsrcs(description) {\n const parsedSdp = transform.parse(description.sdp);\n\n parsedSdp.media.forEach(mLine => {\n if (mLine.type === MediaType.AUDIO) {\n return;\n }\n if (!mLine.ssrcGroups || !mLine.ssrcGroups.length) {\n return;\n }\n let reorderedSsrcs = [];\n\n const ssrcs = new Set();\n\n mLine.ssrcGroups.map(group =>\n group.ssrcs\n .split(' ')\n .filter(Boolean)\n .forEach(ssrc => ssrcs.add(ssrc))\n );\n\n ssrcs.forEach(ssrc => {\n const sources = mLine.ssrcs.filter(source => source.id.toString() === ssrc);\n\n reorderedSsrcs = reorderedSsrcs.concat(sources);\n });\n mLine.ssrcs = reorderedSsrcs;\n });\n\n return new RTCSessionDescription({\n type: description.type,\n sdp: transform.write(parsedSdp)\n });\n }\n\n /**\n * Returns the transceiver associated with a given RTCRtpSender/RTCRtpReceiver.\n *\n * @param {string} mediaType - type of track associated with the transceiver 'audio' or 'video'.\n * @param {JitsiLocalTrack} localTrack - local track to be used for lookup.\n * @returns {RTCRtpTransceiver}\n */\n findTransceiver(mediaType, localTrack = null) {\n const transceiver = localTrack?.track && localTrack.getOriginalStream()\n ? this.pc.peerconnection.getTransceivers().find(t => t.sender?.track?.id === localTrack.getTrackId())\n : this.pc.peerconnection.getTransceivers().find(t => t.receiver?.track?.kind === mediaType);\n\n return transceiver;\n }\n\n /**\n * Takes in a *unified plan* offer and inserts the appropriate\n * parameters for adding simulcast receive support.\n * @param {Object} desc - A session description object\n * @param {String} desc.type - the type (offer/answer)\n * @param {String} desc.sdp - the sdp content\n *\n * @return {Object} A session description (same format as above) object\n * with its sdp field modified to advertise simulcast receive support\n */\n insertUnifiedPlanSimulcastReceive(desc) {\n // a=simulcast line is not needed on browsers where we SDP munging is used for enabling on simulcast.\n // Remove this check when the client switches to RID/MID based simulcast on all browsers.\n if (browser.usesSdpMungingForSimulcast()) {\n return desc;\n }\n const sdp = transform.parse(desc.sdp);\n const idx = sdp.media.findIndex(mline => mline.type === MediaType.VIDEO);\n\n if (sdp.media[idx].rids && (sdp.media[idx].simulcast_03 || sdp.media[idx].simulcast)) {\n // Make sure we don't have the simulcast recv line on video descriptions other than\n // the first video description.\n sdp.media.forEach((mline, i) => {\n if (mline.type === MediaType.VIDEO && i !== idx) {\n sdp.media[i].rids = undefined;\n sdp.media[i].simulcast = undefined;\n\n // eslint-disable-next-line camelcase\n sdp.media[i].simulcast_03 = undefined;\n }\n });\n\n return new RTCSessionDescription({\n type: desc.type,\n sdp: transform.write(sdp)\n });\n }\n\n // In order of highest to lowest spatial quality\n sdp.media[idx].rids = [\n {\n id: SIM_LAYER_1_RID,\n direction: 'recv'\n },\n {\n id: SIM_LAYER_2_RID,\n direction: 'recv'\n },\n {\n id: SIM_LAYER_3_RID,\n direction: 'recv'\n }\n ];\n\n // Firefox 72 has stopped parsing the legacy rid= parameters in simulcast attributes.\n // eslint-disable-next-line max-len\n // https://www.fxsitecompat.dev/en-CA/docs/2019/pt-and-rid-in-webrtc-simulcast-attributes-are-no-longer-supported/\n const simulcastLine = browser.isFirefox() && browser.isVersionGreaterThan(71)\n ? `recv ${SIM_LAYER_RIDS.join(';')}`\n : `recv rid=${SIM_LAYER_RIDS.join(';')}`;\n\n // eslint-disable-next-line camelcase\n sdp.media[idx].simulcast_03 = {\n value: simulcastLine\n };\n\n return new RTCSessionDescription({\n type: desc.type,\n sdp: transform.write(sdp)\n });\n }\n\n /**\n * Adds {@link JitsiLocalTrack} to the WebRTC peerconnection for the first time.\n * @param {JitsiLocalTrack} track - track to be added to the peerconnection.\n * @param {boolean} isInitiator - boolean that indicates if the endpoint is offerer in a p2p connection.\n * @returns {void}\n */\n addTrack(localTrack, isInitiator) {\n const track = localTrack.getTrack();\n\n if (isInitiator) {\n // Use pc.addTransceiver() for the initiator case when local tracks are getting added\n // to the peerconnection before a session-initiate is sent over to the peer.\n const transceiverInit = {\n direction: MediaDirection.SENDRECV,\n streams: [ localTrack.getOriginalStream() ],\n sendEncodings: []\n };\n\n if (!browser.isFirefox()) {\n transceiverInit.sendEncodings = this._getStreamEncodings(localTrack);\n }\n this.pc.peerconnection.addTransceiver(track, transceiverInit);\n } else {\n // Use pc.addTrack() for responder case so that we can re-use the m-lines that were created\n // when setRemoteDescription was called. pc.addTrack() automatically attaches to any existing\n // unused \"recv-only\" transceiver.\n this.pc.peerconnection.addTrack(track);\n }\n }\n\n /**\n * Returns the calculated active state of the simulcast encodings based on the frame height requested for the send\n * stream. All the encodings that have a resolution lower than the frame height requested will be enabled.\n *\n * @param {JitsiLocalTrack} localVideoTrack The local video track.\n * @param {number} newHeight The resolution requested for the video track.\n * @returns {Array}\n */\n calculateEncodingsActiveState(localVideoTrack, newHeight) {\n const localTrack = localVideoTrack.getTrack();\n const { height } = localTrack.getSettings();\n const encodingsState = this.localStreamEncodingsConfig\n .map(encoding => height / encoding.scaleResolutionDownBy)\n .map((frameHeight, idx) => {\n let active = localVideoTrack.getVideoType() === VideoType.CAMERA\n\n // Keep the LD stream enabled even when the LD stream's resolution is higher than of the requested\n // resolution. This can happen when camera is captured at resolutions higher than 720p but the\n // requested resolution is 180. Since getParameters doesn't give us information about the resolutions\n // of the simulcast encodings, we have to rely on our initial config for the simulcast streams.\n ? newHeight > 0 && this.localStreamEncodingsConfig[idx]?.scaleResolutionDownBy === LD_SCALE_FACTOR\n ? true\n : frameHeight <= newHeight\n\n // Keep all the encodings for desktop track active.\n : true;\n\n // Disable the lower spatial layers for screensharing in Unified plan when low fps screensharing is in\n // progress. Sending all three streams often results in the browser suspending the high resolution in low\n // b/w and cpu cases, especially on the low end machines. Suspending the low resolution streams ensures\n // that the highest resolution stream is available always. Safari is an exception here since it does not\n // send the desktop stream at all if only the high resolution stream is enabled.\n if (this.pc.isSharingLowFpsScreen()\n && this.pc.usesUnifiedPlan()\n && !browser.isWebKitBased()\n && this.localStreamEncodingsConfig[idx].scaleResolutionDownBy !== HD_SCALE_FACTOR) {\n active = false;\n }\n\n return active;\n });\n\n return encodingsState;\n }\n\n /**\n * Returns the calculates max bitrates that need to be configured on the simulcast encodings based on the video\n * type and other considerations associated with screenshare.\n *\n * @param {JitsiLocalTrack} localVideoTrack The local video track.\n * @returns {Array}\n */\n calculateEncodingsBitrates(localVideoTrack) {\n const videoType = localVideoTrack.getVideoType();\n const desktopShareBitrate = this.pc.options?.videoQuality?.desktopBitrate || DESKTOP_SHARE_RATE;\n const presenterEnabled = localVideoTrack._originalStream\n && localVideoTrack._originalStream.id !== localVideoTrack.getStreamId();\n\n const encodingsBitrates = this.localStreamEncodingsConfig\n .map(encoding => {\n const bitrate = this.pc.isSharingLowFpsScreen() && !browser.isWebKitBased()\n\n // For low fps screensharing, set a max bitrate of 500 Kbps when presenter is not turned on, 2500 Kbps\n // otherwise.\n ? presenterEnabled ? HD_BITRATE : desktopShareBitrate\n\n // For high fps screenshare, 'maxBitrate' setting must be cleared on Chrome in plan-b, because\n // if simulcast is enabled for screen and maxBitrates are set then Chrome will not send the\n // desktop stream.\n : videoType === VideoType.DESKTOP && browser.isChromiumBased() && !this.pc.usesUnifiedPlan()\n ? undefined\n : encoding.maxBitrate;\n\n return bitrate;\n });\n\n return encodingsBitrates;\n }\n\n /**\n * Replaces the existing track on a RTCRtpSender with the given track.\n *\n * @param {JitsiLocalTrack} oldTrack - existing track on the sender that needs to be removed.\n * @param {JitsiLocalTrack} newTrack - new track that needs to be added to the sender.\n * @returns {Promise} - resolved with the associated transceiver when done, rejected otherwise.\n */\n replaceTrack(oldTrack, newTrack) {\n const mediaType = newTrack?.getType() ?? oldTrack?.getType();\n const track = newTrack?.getTrack() ?? null;\n let transceiver;\n\n // If old track exists, replace the track on the corresponding sender.\n if (oldTrack) {\n transceiver = this.pc.peerconnection.getTransceivers().find(t => t.sender.track === oldTrack.getTrack());\n\n // Find the first recvonly transceiver when more than one track of the same media type is being added to the pc.\n // As part of the track addition, a new m-line was added to the remote description with direction set to\n // recvonly.\n } else if (this.pc.getLocalTracks(mediaType)?.length && !newTrack.conference) {\n transceiver = this.pc.peerconnection.getTransceivers().find(\n t => t.receiver.track.kind === mediaType\n && t.direction === MediaDirection.RECVONLY\n && t.currentDirection === MediaDirection.INACTIVE);\n\n // For unmute operations, find the transceiver based on the track index in the source name if present, otherwise\n // it is assumed to be the first local track that was added to the peerconnection.\n } else {\n transceiver = this.pc.peerconnection.getTransceivers().find(t => t.receiver.track.kind === mediaType);\n\n const sourceName = newTrack.getSourceName();\n\n if (sourceName) {\n const trackIndex = Number(sourceName.split('-')[1].substring(1));\n\n if (trackIndex) {\n transceiver = this.pc.peerconnection.getTransceivers()\n .filter(t => t.receiver.track.kind === mediaType\n && t.direction !== MediaDirection.RECVONLY)[trackIndex];\n }\n }\n }\n\n if (!transceiver) {\n return Promise.reject(new Error('replace track failed'));\n }\n logger.debug(`${this.pc} Replacing ${oldTrack} with ${newTrack}`);\n\n return transceiver.sender.replaceTrack(track)\n .then(() => Promise.resolve(transceiver));\n }\n\n /**\n * Enables/disables audio transmission on the peer connection. When\n * disabled the audio transceiver direction will be set to 'inactive'\n * which means that no data will be sent nor accepted, but\n * the connection should be kept alive.\n * @param {boolean} active - true to enable audio media transmission or\n * false to disable.\n * @returns {void}\n */\n setAudioTransferActive(active) {\n this.setMediaTransferActive(MediaType.AUDIO, active);\n }\n\n /**\n * Set the simulcast stream encoding properties on the RTCRtpSender.\n * @param {JitsiLocalTrack} track - the current track in use for which\n * the encodings are to be set.\n * @returns {Promise} - resolved when done.\n */\n setEncodings(track) {\n const mediaType = track.getType();\n const transceiver = this.findTransceiver(mediaType, track);\n const parameters = transceiver?.sender?.getParameters();\n\n // Resolve if the encodings are not available yet. This happens immediately after the track is added to the\n // peerconnection on chrome in unified-plan. It is ok to ignore and not report the error here since the\n // action that triggers 'addTrack' (like unmute) will also configure the encodings and set bitrates after that.\n if (!parameters?.encodings?.length) {\n return Promise.resolve();\n }\n parameters.encodings = this._getStreamEncodings(track);\n\n return transceiver.sender.setParameters(parameters);\n }\n\n /**\n * Enables/disables media transmission on the peerconnection by changing the direction\n * on the transceiver for the specified media type.\n * @param {String} mediaType - 'audio' or 'video'\n * @param {boolean} active - true to enable media transmission or false\n * to disable.\n * @returns {void}\n */\n setMediaTransferActive(mediaType, active) {\n const transceivers = this.pc.peerconnection.getTransceivers()\n .filter(t => t.receiver && t.receiver.track && t.receiver.track.kind === mediaType);\n const localTracks = this.pc.getLocalTracks(mediaType);\n\n logger.info(`${this.pc} ${active ? 'Enabling' : 'Suspending'} ${mediaType} media transfer.`);\n transceivers.forEach((transceiver, idx) => {\n if (active) {\n // The first transceiver is for the local track and only this one can be set to 'sendrecv'\n if (idx === 0 && localTracks.length) {\n transceiver.direction = MediaDirection.SENDRECV;\n } else {\n transceiver.direction = MediaDirection.RECVONLY;\n }\n } else {\n transceiver.direction = MediaDirection.INACTIVE;\n }\n });\n }\n\n /**\n * Enables/disables video media transmission on the peer connection. When\n * disabled the SDP video media direction in the local SDP will be adjusted to\n * 'inactive' which means that no data will be sent nor accepted, but\n * the connection should be kept alive.\n * @param {boolean} active - true to enable video media transmission or\n * false to disable.\n * @returns {void}\n */\n setVideoTransferActive(active) {\n this.setMediaTransferActive(MediaType.VIDEO, active);\n }\n\n /**\n * Ensures that the resolution of the stream encodings are consistent with the values\n * that were configured on the RTCRtpSender when the source was added to the peerconnection.\n * This should prevent us from overriding the default values if the browser returns\n * erroneous values when RTCRtpSender.getParameters is used for getting the encodings info.\n * @param {Object} parameters - the RTCRtpEncodingParameters obtained from the browser.\n * @returns {void}\n */\n updateEncodingsResolution(parameters) {\n if (!(browser.isWebKitBased() && parameters.encodings && Array.isArray(parameters.encodings))) {\n return;\n }\n const allEqualEncodings\n = encodings => encodings.every(encoding => typeof encoding.scaleResolutionDownBy !== 'undefined'\n && encoding.scaleResolutionDownBy === encodings[0].scaleResolutionDownBy);\n\n // Implement the workaround only when all the encodings report the same resolution.\n if (allEqualEncodings(parameters.encodings)) {\n parameters.encodings.forEach((encoding, idx) => {\n encoding.scaleResolutionDownBy = this.localStreamEncodingsConfig[idx].scaleResolutionDownBy;\n });\n }\n }\n}\n","import { getLogger } from '@jitsi/logger';\nimport { Interop } from '@jitsi/sdp-interop';\nimport transform from 'sdp-transform';\n\nimport * as CodecMimeType from '../../service/RTC/CodecMimeType';\nimport MediaDirection from '../../service/RTC/MediaDirection';\nimport * as MediaType from '../../service/RTC/MediaType';\nimport RTCEvents from '../../service/RTC/RTCEvents';\nimport * as SignalingEvents from '../../service/RTC/SignalingEvents';\nimport { getSourceNameForJitsiTrack } from '../../service/RTC/SignalingLayer';\nimport * as VideoType from '../../service/RTC/VideoType';\nimport { SS_DEFAULT_FRAME_RATE } from '../RTC/ScreenObtainer';\nimport browser from '../browser';\nimport FeatureFlags from '../flags/FeatureFlags';\nimport LocalSdpMunger from '../sdp/LocalSdpMunger';\nimport RtxModifier from '../sdp/RtxModifier';\nimport SDP from '../sdp/SDP';\nimport SDPUtil from '../sdp/SDPUtil';\nimport SdpConsistency from '../sdp/SdpConsistency';\nimport SdpSimulcast from '../sdp/SdpSimulcast.ts';\nimport { SdpTransformWrap } from '../sdp/SdpTransformUtil';\nimport * as GlobalOnErrorHandler from '../util/GlobalOnErrorHandler';\n\nimport JitsiRemoteTrack from './JitsiRemoteTrack';\nimport RTC from './RTC';\nimport RTCUtils from './RTCUtils';\nimport {\n HD_BITRATE,\n HD_SCALE_FACTOR,\n SIM_LAYER_RIDS,\n TPCUtils\n} from './TPCUtils';\n\n// FIXME SDP tools should end up in some kind of util module\n\nconst logger = getLogger(__filename);\nconst DEGRADATION_PREFERENCE_CAMERA = 'maintain-framerate';\nconst DEGRADATION_PREFERENCE_DESKTOP = 'maintain-resolution';\n\n/* eslint-disable max-params */\n\n/**\n * Creates new instance of 'TraceablePeerConnection'.\n *\n * @param {RTC} rtc the instance of RTC service\n * @param {number} id the peer connection id assigned by the parent RTC module.\n * @param {SignalingLayer} signalingLayer the signaling layer instance\n * @param {object} pcConfig The {@code RTCConfiguration} to use for the WebRTC peer connection.\n * @param {object} constraints WebRTC 'PeerConnection' constraints\n * @param {boolean} isP2P indicates whether or not the new instance will be used in a peer to peer connection.\n * @param {object} options TracablePeerConnection config options.\n * @param {boolean} options.disableSimulcast if set to 'true' will disable the simulcast.\n * @param {boolean} options.disableRtx if set to 'true' will disable the RTX.\n * @param {string} options.disabledCodec the mime type of the code that should not be negotiated on the peerconnection.\n * @param {string} options.preferredCodec the mime type of the codec that needs to be made the preferred codec for the\n * peerconnection.\n * @param {boolean} options.startSilent If set to 'true' no audio will be sent or received.\n * @param {boolean} options.usesUnifiedPlan Indicates if the browser is running in unified plan mode.\n *\n * FIXME: initially the purpose of TraceablePeerConnection was to be able to\n * debug the peer connection. Since many other responsibilities have been added\n * it would make sense to extract a separate class from it and come up with\n * a more suitable name.\n *\n * @constructor\n */\nexport default function TraceablePeerConnection(\n rtc,\n id,\n signalingLayer,\n pcConfig,\n constraints,\n isP2P,\n options) {\n\n /**\n * Indicates whether or not this peer connection instance is actively\n * sending/receiving audio media. When set to false the SDP audio\n * media direction will be adjusted to 'inactive' in order to suspend\n * the transmission.\n * @type {boolean}\n * @private\n */\n this.audioTransferActive = !(options.startSilent === true);\n\n /**\n * The DTMF sender instance used to send DTMF tones.\n *\n * @type {RTCDTMFSender|undefined}\n * @private\n */\n this._dtmfSender = undefined;\n\n /**\n * @typedef {Object} TouchToneRequest\n * @property {string} tones - The DTMF tones string as defined by\n * {@code RTCDTMFSender.insertDTMF}, 'tones' argument.\n * @property {number} duration - The amount of time in milliseconds that\n * each DTMF should last.\n * @property {string} interToneGap - The length of time in miliseconds to\n * wait between tones.\n */\n /**\n * TouchToneRequests which are waiting to be played. This queue is filled\n * if there are touch tones currently being played.\n *\n * @type {Array}\n * @private\n */\n this._dtmfTonesQueue = [];\n\n /**\n * Indicates whether or not this peer connection instance is actively\n * sending/receiving video media. When set to false the SDP video\n * media direction will be adjusted to 'inactive' in order to suspend\n * the transmission.\n * @type {boolean}\n * @private\n */\n this.videoTransferActive = true;\n\n /**\n * The parent instance of RTC service which created this\n * TracablePeerConnection.\n * @type {RTC}\n */\n this.rtc = rtc;\n\n /**\n * The peer connection identifier assigned by the RTC module.\n * @type {number}\n */\n this.id = id;\n\n /**\n * Indicates whether or not this instance is used in a peer to peer\n * connection.\n * @type {boolean}\n */\n this.isP2P = isP2P;\n\n /**\n * The map holds remote tracks associated with this peer connection. It maps user's JID to media type and a set of\n * remote tracks.\n * @type {Map>>}\n */\n this.remoteTracks = new Map();\n\n /**\n * A map which stores local tracks mapped by {@link JitsiLocalTrack.rtcId}\n * @type {Map}\n */\n this.localTracks = new Map();\n\n /**\n * Keeps tracks of the WebRTC MediaStreams that have been added to\n * the underlying WebRTC PeerConnection.\n * @type {Array}\n * @private\n */\n this._addedStreams = [];\n\n /**\n * @typedef {Object} TPCGroupInfo\n * @property {string} semantics the SSRC groups semantics\n * @property {Array} ssrcs group's SSRCs in order where the first\n * one is group's primary SSRC, the second one is secondary (RTX) and so\n * on...\n */\n /**\n * @typedef {Object} TPCSSRCInfo\n * @property {Array} ssrcs an array which holds all track's SSRCs\n * @property {Array} groups an array stores all track's SSRC\n * groups\n */\n /**\n * Holds the info about local track's SSRCs mapped per their\n * {@link JitsiLocalTrack.rtcId}\n * @type {Map}\n */\n this.localSSRCs = new Map();\n\n /**\n * The local ICE username fragment for this session.\n */\n this.localUfrag = null;\n\n /**\n * The remote ICE username fragment for this session.\n */\n this.remoteUfrag = null;\n\n /**\n * The DTLS transport object for the PeerConnection.\n * Note: this assume only one shared transport exists because we bundled\n * all streams on the same underlying transport.\n */\n this._dtlsTransport = null;\n\n /**\n * The signaling layer which operates this peer connection.\n * @type {SignalingLayer}\n */\n this.signalingLayer = signalingLayer;\n\n // SignalingLayer listeners\n this._peerVideoTypeChanged = this._peerVideoTypeChanged.bind(this);\n this.signalingLayer.on(\n SignalingEvents.PEER_VIDEO_TYPE_CHANGED,\n this._peerVideoTypeChanged);\n\n this._peerMutedChanged = this._peerMutedChanged.bind(this);\n this.signalingLayer.on(\n SignalingEvents.PEER_MUTED_CHANGED,\n this._peerMutedChanged);\n this.options = options;\n\n // Make sure constraints is properly formatted in order to provide information about whether or not this\n // connection is P2P to rtcstats.\n const safeConstraints = constraints || {};\n\n safeConstraints.optional = safeConstraints.optional || [];\n\n // The `optional` parameter needs to be of type array, otherwise chrome will throw an error.\n // Firefox and Safari just ignore it.\n if (Array.isArray(safeConstraints.optional)) {\n safeConstraints.optional.push({ rtcStatsSFUP2P: this.isP2P });\n } else {\n logger.warn('Optional param is not an array, rtcstats p2p data is omitted.');\n }\n\n this.peerconnection = new RTCUtils.RTCPeerConnectionType(pcConfig, safeConstraints);\n\n this.tpcUtils = new TPCUtils(this);\n this.updateLog = [];\n this.stats = {};\n this.statsinterval = null;\n\n /**\n * Flag used to indicate if simulcast is turned off and a cap of 500 Kbps is applied on screensharing.\n */\n this._capScreenshareBitrate = this.options.capScreenshareBitrate;\n\n /**\n * Flag used to indicate if the browser is running in unified plan mode.\n */\n this._usesUnifiedPlan = options.usesUnifiedPlan;\n\n /**\n * Flag used to indicate if RTCRtpTransceiver#setCodecPreferences is to be used instead of SDP\n * munging for codec selection.\n */\n this._usesTransceiverCodecPreferences = browser.supportsCodecPreferences() && this._usesUnifiedPlan;\n this._usesTransceiverCodecPreferences\n && logger.info('Using RTCRtpTransceiver#setCodecPreferences for codec selection');\n\n /**\n * @type {number} The max number of stats to keep in this.stats. Limit to\n * 300 values, i.e. 5 minutes; set to 0 to disable\n */\n this.maxstats = options.maxstats;\n\n this.interop = new Interop();\n\n if (this._usesUnifiedPlan) {\n this.simulcast = new SdpSimulcast({ numOfLayers: SIM_LAYER_RIDS.length });\n } else {\n const Simulcast = require('@jitsi/sdp-simulcast');\n\n this.simulcast = new Simulcast(\n {\n numOfLayers: SIM_LAYER_RIDS.length,\n explodeRemoteSimulcast: false,\n usesUnifiedPlan: false\n });\n }\n\n this.sdpConsistency = new SdpConsistency(this.toString());\n\n /**\n * Munges local SDP provided to the Jingle Session in order to prevent from\n * sending SSRC updates on attach/detach and mute/unmute (for video).\n * @type {LocalSdpMunger}\n */\n this.localSdpMunger = new LocalSdpMunger(this, this.rtc.getLocalEndpointId());\n\n /**\n * TracablePeerConnection uses RTC's eventEmitter\n * @type {EventEmitter}\n */\n this.eventEmitter = rtc.eventEmitter;\n this.rtxModifier = new RtxModifier();\n\n /**\n * The height constraint applied on the video sender. The default value is 2160 (4K) when layer suspension is\n * explicitly disabled.\n */\n this._senderVideoMaxHeight = 2160;\n\n /**\n * The height constraints to be applied on the sender per local video source (source name as the key).\n * @type {Map}\n */\n this._senderMaxHeights = new Map();\n\n // override as desired\n this.trace = (what, info) => {\n logger.debug(what, info);\n\n this.updateLog.push({\n time: new Date(),\n type: what,\n value: info || ''\n });\n };\n this.onicecandidate = null;\n this.peerconnection.onicecandidate = event => {\n this.trace(\n 'onicecandidate',\n JSON.stringify(event.candidate, null, ' '));\n\n if (this.onicecandidate !== null) {\n this.onicecandidate(event);\n }\n };\n\n // Use track events when browser is running in unified plan mode and stream events in plan-b mode.\n if (this._usesUnifiedPlan) {\n this.onTrack = evt => {\n const stream = evt.streams[0];\n\n this._remoteTrackAdded(stream, evt.track, evt.transceiver);\n stream.addEventListener('removetrack', e => {\n this._remoteTrackRemoved(stream, e.track);\n });\n };\n this.peerconnection.addEventListener('track', this.onTrack);\n } else {\n this.peerconnection.onaddstream = event => this._remoteStreamAdded(event.stream);\n this.peerconnection.onremovestream = event => this._remoteStreamRemoved(event.stream);\n }\n this.onsignalingstatechange = null;\n this.peerconnection.onsignalingstatechange = event => {\n this.trace('onsignalingstatechange', this.signalingState);\n if (this.onsignalingstatechange !== null) {\n this.onsignalingstatechange(event);\n }\n };\n this.oniceconnectionstatechange = null;\n this.peerconnection.oniceconnectionstatechange = event => {\n this.trace('oniceconnectionstatechange', this.iceConnectionState);\n if (this.oniceconnectionstatechange !== null) {\n this.oniceconnectionstatechange(event);\n }\n };\n this.onnegotiationneeded = null;\n this.peerconnection.onnegotiationneeded = event => {\n this.trace('onnegotiationneeded');\n if (this.onnegotiationneeded !== null) {\n this.onnegotiationneeded(event);\n }\n };\n this.onconnectionstatechange = null;\n this.peerconnection.onconnectionstatechange = event => {\n this.trace('onconnectionstatechange', this.connectionState);\n if (this.onconnectionstatechange !== null) {\n this.onconnectionstatechange(event);\n }\n };\n this.ondatachannel = null;\n this.peerconnection.ondatachannel = event => {\n this.trace('ondatachannel');\n if (this.ondatachannel !== null) {\n this.ondatachannel(event);\n }\n };\n\n if (this.maxstats) {\n this.statsinterval = window.setInterval(() => {\n this.getStats().then(stats => {\n if (typeof stats?.result === 'function') {\n const results = stats.result();\n\n for (let i = 0; i < results.length; ++i) {\n const res = results[i];\n\n res.names().forEach(name => {\n this._processStat(res, name, res.stat(name));\n });\n }\n } else {\n stats.forEach(r => this._processStat(r, '', r));\n }\n });\n }, 1000);\n }\n\n logger.info(`Create new ${this}`);\n}\n\n/* eslint-enable max-params */\n\n/**\n * Process stat and adds it to the array of stats we store.\n * @param report the current stats report.\n * @param name the name of the report, if available\n * @param statValue the value to add.\n * @private\n */\nTraceablePeerConnection.prototype._processStat\n = function(report, name, statValue) {\n const id = `${report.id}-${name}`;\n let s = this.stats[id];\n const now = new Date();\n\n if (!s) {\n this.stats[id] = s = {\n startTime: now,\n endTime: now,\n values: [],\n times: []\n };\n }\n s.values.push(statValue);\n s.times.push(now.getTime());\n if (s.values.length > this.maxstats) {\n s.values.shift();\n s.times.shift();\n }\n s.endTime = now;\n };\n\n/**\n * Returns a string representation of a SessionDescription object.\n */\nconst dumpSDP = function(description) {\n if (typeof description === 'undefined' || description === null) {\n return '';\n }\n\n return `type: ${description.type}\\r\\n${description.sdp}`;\n};\n\n\n/**\n * Forwards the {@link peerconnection.iceConnectionState} state except that it\n * will convert \"completed\" into \"connected\" where both mean that the ICE has\n * succeeded and is up and running. We never see \"completed\" state for\n * the JVB connection, but it started appearing for the P2P one. This method\n * allows to adapt old logic to this new situation.\n * @return {string}\n */\nTraceablePeerConnection.prototype.getConnectionState = function() {\n const state = this.peerconnection.iceConnectionState;\n\n if (state === 'completed') {\n return 'connected';\n }\n\n return state;\n};\n\n/**\n * Obtains the media direction for given {@link MediaType}. The method takes\n * into account whether or not there are any local tracks for media and\n * the {@link audioTransferActive} and {@link videoTransferActive} flags.\n * @param {MediaType} mediaType\n * @param {boolean} isAddOperation whether the direction is to be calculated after a source-add action.\n * @return {string} one of the SDP direction constants ('sendrecv, 'recvonly'\n * etc.) which should be used when setting local description on the peer\n * connection.\n * @private\n */\nTraceablePeerConnection.prototype.getDesiredMediaDirection = function(mediaType, isAddOperation = false) {\n const hasLocalSource = this.hasAnyTracksOfType(mediaType);\n\n if (this._usesUnifiedPlan) {\n return isAddOperation\n ? hasLocalSource ? MediaDirection.SENDRECV : MediaDirection.SENDONLY\n : hasLocalSource ? MediaDirection.RECVONLY : MediaDirection.INACTIVE;\n }\n\n const mediaTransferActive = mediaType === MediaType.AUDIO ? this.audioTransferActive : this.videoTransferActive;\n\n if (mediaTransferActive) {\n return hasLocalSource ? MediaDirection.SENDRECV : MediaDirection.RECVONLY;\n }\n\n return MediaDirection.INACTIVE;\n};\n\n/**\n * Returns the list of RTCRtpReceivers created for the source of the given media type associated with\n * the set of remote endpoints specified.\n * @param {Array} endpoints list of the endpoints\n * @param {string} mediaType 'audio' or 'video'\n * @returns {Array} list of receivers created by the peerconnection.\n */\nTraceablePeerConnection.prototype._getReceiversByEndpointIds = function(endpoints, mediaType) {\n let remoteTracks = [];\n let receivers = [];\n\n for (const endpoint of endpoints) {\n remoteTracks = remoteTracks.concat(this.getRemoteTracks(endpoint, mediaType));\n }\n\n // Get the ids of the MediaStreamTracks associated with each of these remote tracks.\n const remoteTrackIds = remoteTracks.map(remote => remote.track?.id);\n\n receivers = this.peerconnection.getReceivers()\n .filter(receiver => receiver.track\n && receiver.track.kind === mediaType\n && remoteTrackIds.find(trackId => trackId === receiver.track.id));\n\n return receivers;\n};\n\n/**\n * Tells whether or not this TPC instance is using Simulcast.\n * @return {boolean} true if simulcast is enabled and active or\n * false if it's turned off.\n */\nTraceablePeerConnection.prototype.isSimulcastOn = function() {\n return !this.options.disableSimulcast;\n};\n\n/**\n * Handles {@link SignalingEvents.PEER_VIDEO_TYPE_CHANGED}\n * @param {string} endpointId the video owner's ID (MUC nickname)\n * @param {VideoType} videoType the new value\n * @private\n */\nTraceablePeerConnection.prototype._peerVideoTypeChanged = function(\n endpointId,\n videoType) {\n // Check if endpointId has a value to avoid action on random track\n if (!endpointId) {\n logger.error(`${this} No endpointID on peerVideoTypeChanged`);\n\n return;\n }\n const videoTrack = this.getRemoteTracks(endpointId, MediaType.VIDEO);\n\n if (videoTrack.length) {\n // NOTE 1 track per media type is assumed\n videoTrack[0]._setVideoType(videoType);\n }\n};\n\n/**\n * Handles remote track mute / unmute events.\n * @param {string} endpointId the track owner's identifier (MUC nickname)\n * @param {MediaType} mediaType \"audio\" or \"video\"\n * @param {boolean} isMuted the new mute state\n * @private\n */\nTraceablePeerConnection.prototype._peerMutedChanged = function(\n endpointId,\n mediaType,\n isMuted) {\n // Check if endpointId is a value to avoid doing action on all remote tracks\n if (!endpointId) {\n logger.error(`${this} On peerMuteChanged - no endpoint ID`);\n\n return;\n }\n const track = this.getRemoteTracks(endpointId, mediaType);\n\n if (track.length) {\n // NOTE 1 track per media type is assumed\n track[0].setMute(isMuted);\n }\n};\n\n/**\n * Obtains audio levels of the remote audio tracks by getting the source information on the RTCRtpReceivers.\n * The information relevant to the ssrc is updated each time a RTP packet constaining the ssrc is received.\n * @param {Array} speakerList list of endpoint ids for which audio levels are to be gathered.\n * @returns {Object} containing ssrc and audio level information as a key-value pair.\n */\nTraceablePeerConnection.prototype.getAudioLevels = function(speakerList = []) {\n const audioLevels = {};\n const audioReceivers = speakerList.length\n ? this._getReceiversByEndpointIds(speakerList, MediaType.AUDIO)\n : this.peerconnection.getReceivers()\n .filter(receiver => receiver.track && receiver.track.kind === MediaType.AUDIO && receiver.track.enabled);\n\n audioReceivers.forEach(remote => {\n const ssrc = remote.getSynchronizationSources();\n\n if (ssrc && ssrc.length) {\n // As per spec, this audiolevel is a value between 0..1 (linear), where 1.0\n // represents 0 dBov, 0 represents silence, and 0.5 represents approximately\n // 6 dBSPL change in the sound pressure level from 0 dBov.\n // https://www.w3.org/TR/webrtc/#dom-rtcrtpcontributingsource-audiolevel\n audioLevels[ssrc[0].source] = ssrc[0].audioLevel;\n }\n });\n\n return audioLevels;\n};\n\n/**\n * Obtains local tracks for given {@link MediaType}. If the mediaType\n * argument is omitted the list of all local tracks will be returned.\n * @param {MediaType} [mediaType]\n * @return {Array}\n */\nTraceablePeerConnection.prototype.getLocalTracks = function(mediaType) {\n let tracks = Array.from(this.localTracks.values());\n\n if (mediaType !== undefined) {\n tracks = tracks.filter(track => track.getType() === mediaType);\n }\n\n return tracks;\n};\n\n/**\n * Retrieves the local video tracks.\n *\n * @returns {JitsiLocalTrack|undefined} - local video tracks.\n */\nTraceablePeerConnection.prototype.getLocalVideoTracks = function() {\n return this.getLocalTracks(MediaType.VIDEO);\n};\n\n/**\n * Checks whether or not this {@link TraceablePeerConnection} instance contains any local tracks for given\n * mediaType.\n *\n * @param {MediaType} mediaType - The media type.\n * @return {boolean}\n */\nTraceablePeerConnection.prototype.hasAnyTracksOfType = function(mediaType) {\n if (!mediaType) {\n throw new Error('\"mediaType\" is required');\n }\n\n return this.getLocalTracks(mediaType).length > 0;\n};\n\n/**\n * Obtains all remote tracks currently known to this PeerConnection instance.\n *\n * @param {string} [endpointId] - The track owner's identifier (MUC nickname)\n * @param {MediaType} [mediaType] - The remote tracks will be filtered by their media type if this argument is\n * specified.\n * @return {Array}\n */\nTraceablePeerConnection.prototype.getRemoteTracks = function(endpointId, mediaType) {\n let remoteTracks = [];\n const endpoints = endpointId ? [ endpointId ] : this.remoteTracks.keys();\n\n for (const endpoint of endpoints) {\n const endpointTracksByMediaType = this.remoteTracks.get(endpoint);\n\n if (endpointTracksByMediaType) {\n for (const trackMediaType of endpointTracksByMediaType.keys()) {\n // per media type filtering\n if (!mediaType || mediaType === trackMediaType) {\n remoteTracks = remoteTracks.concat(Array.from(endpointTracksByMediaType.get(trackMediaType)));\n }\n }\n }\n }\n\n return remoteTracks;\n};\n\n/**\n * Parses the remote description and returns the sdp lines of the sources associated with a remote participant.\n *\n * @param {string} id Endpoint id of the remote participant.\n * @returns {Array} The sdp lines that have the ssrc information.\n */\nTraceablePeerConnection.prototype.getRemoteSourceInfoByParticipant = function(id) {\n const removeSsrcInfo = [];\n const remoteTracks = this.getRemoteTracks(id);\n\n if (!remoteTracks?.length) {\n return removeSsrcInfo;\n }\n const primarySsrcs = remoteTracks.map(track => track.getSSRC());\n const sdp = new SDP(this.remoteDescription.sdp);\n\n primarySsrcs.forEach((ssrc, idx) => {\n for (const media of sdp.media) {\n let lines = '';\n let ssrcLines = SDPUtil.findLines(media, `a=ssrc:${ssrc}`);\n\n if (ssrcLines.length) {\n if (!removeSsrcInfo[idx]) {\n removeSsrcInfo[idx] = '';\n }\n\n // Check if there are any FID groups present for the primary ssrc.\n const fidLines = SDPUtil.findLines(media, `a=ssrc-group:FID ${ssrc}`);\n\n if (fidLines.length) {\n const secondarySsrc = fidLines[0].split(' ')[2];\n\n lines += `${fidLines[0]}\\r\\n`;\n ssrcLines = ssrcLines.concat(SDPUtil.findLines(media, `a=ssrc:${secondarySsrc}`));\n }\n removeSsrcInfo[idx] += `${ssrcLines.join('\\r\\n')}\\r\\n`;\n removeSsrcInfo[idx] += lines;\n }\n }\n });\n\n return removeSsrcInfo;\n};\n\n/**\n * Returns the target bitrates configured for the local video source.\n *\n * @returns {Object}\n */\nTraceablePeerConnection.prototype.getTargetVideoBitrates = function() {\n const currentCodec = this.getConfiguredVideoCodec();\n\n return this.tpcUtils.videoBitrates[currentCodec.toUpperCase()] || this.tpcUtils.videoBitrates;\n};\n\n/**\n * Tries to find {@link JitsiTrack} for given SSRC number. It will search both\n * local and remote tracks bound to this instance.\n * @param {number} ssrc\n * @return {JitsiTrack|null}\n */\nTraceablePeerConnection.prototype.getTrackBySSRC = function(ssrc) {\n if (typeof ssrc !== 'number') {\n throw new Error(`SSRC ${ssrc} is not a number`);\n }\n for (const localTrack of this.localTracks.values()) {\n if (this.getLocalSSRC(localTrack) === ssrc) {\n return localTrack;\n }\n }\n for (const remoteTrack of this.getRemoteTracks()) {\n if (remoteTrack.getSSRC() === ssrc) {\n return remoteTrack;\n }\n }\n\n return null;\n};\n\n/**\n * Tries to find SSRC number for given {@link JitsiTrack} id. It will search\n * both local and remote tracks bound to this instance.\n * @param {string} id\n * @return {number|null}\n */\nTraceablePeerConnection.prototype.getSsrcByTrackId = function(id) {\n\n const findTrackById = track => track.getTrack().id === id;\n const localTrack = this.getLocalTracks().find(findTrackById);\n\n if (localTrack) {\n return this.getLocalSSRC(localTrack);\n }\n\n const remoteTrack = this.getRemoteTracks().find(findTrackById);\n\n if (remoteTrack) {\n return remoteTrack.getSSRC();\n }\n\n return null;\n};\n\n/**\n * Called when new remote MediaStream is added to the PeerConnection.\n * @param {MediaStream} stream the WebRTC MediaStream for remote participant\n */\nTraceablePeerConnection.prototype._remoteStreamAdded = function(stream) {\n const streamId = RTC.getStreamID(stream);\n\n if (!RTC.isUserStreamById(streamId)) {\n logger.info(`${this} ignored remote 'stream added' event for non-user stream[id=${streamId}]`);\n\n return;\n }\n\n // Bind 'addtrack'/'removetrack' event handlers\n if (browser.isChromiumBased()) {\n stream.onaddtrack = event => {\n this._remoteTrackAdded(stream, event.track);\n };\n stream.onremovetrack = event => {\n this._remoteTrackRemoved(stream, event.track);\n };\n }\n\n // Call remoteTrackAdded for each track in the stream\n const streamAudioTracks = stream.getAudioTracks();\n\n for (const audioTrack of streamAudioTracks) {\n this._remoteTrackAdded(stream, audioTrack);\n }\n const streamVideoTracks = stream.getVideoTracks();\n\n for (const videoTrack of streamVideoTracks) {\n this._remoteTrackAdded(stream, videoTrack);\n }\n};\n\n\n/**\n * Called on \"track added\" and \"stream added\" PeerConnection events (because we\n * handle streams on per track basis). Finds the owner and the SSRC for\n * the track and passes that to ChatRoom for further processing.\n * @param {MediaStream} stream the WebRTC MediaStream instance which is\n * the parent of the track\n * @param {MediaStreamTrack} track the WebRTC MediaStreamTrack added for remote\n * participant.\n * @param {RTCRtpTransceiver} transceiver the WebRTC transceiver that is created\n * for the remote participant in unified plan.\n */\nTraceablePeerConnection.prototype._remoteTrackAdded = function(stream, track, transceiver = null) {\n const streamId = RTC.getStreamID(stream);\n const mediaType = track.kind;\n\n if (!this.isP2P && !RTC.isUserStreamById(streamId)) {\n logger.info(`${this} ignored remote 'stream added' event for non-user stream[id=${streamId}]`);\n\n return;\n }\n logger.info(`${this} adding remote track for stream[id=${streamId},type=${mediaType}]`);\n\n // look up an associated JID for a stream id\n if (!mediaType) {\n GlobalOnErrorHandler.callErrorHandler(\n new Error(\n `MediaType undefined for remote track, stream id: ${streamId}`\n ));\n\n // Abort\n return;\n }\n\n const remoteSDP = this._usesUnifiedPlan\n ? new SDP(this.peerconnection.remoteDescription.sdp)\n : new SDP(this.remoteDescription.sdp);\n let mediaLines;\n\n // In unified plan mode, find the matching mline using 'mid' if its availble, otherwise use the\n // 'msid' attribute of the stream.\n if (this._usesUnifiedPlan) {\n if (transceiver && transceiver.mid) {\n const mid = transceiver.mid;\n\n mediaLines = remoteSDP.media.filter(mls => SDPUtil.findLine(mls, `a=mid:${mid}`));\n } else {\n mediaLines = remoteSDP.media.filter(mls => {\n const msid = SDPUtil.findLine(mls, 'a=msid:');\n\n return typeof msid !== 'undefined' && streamId === msid.substring(7).split(' ')[0];\n });\n }\n } else {\n mediaLines = remoteSDP.media.filter(mls => mls.startsWith(`m=${mediaType}`));\n }\n\n if (!mediaLines.length) {\n GlobalOnErrorHandler.callErrorHandler(\n new Error(`No media lines found in remote SDP for remote stream[id=${streamId},type=${mediaType}]`));\n\n // Abort\n return;\n }\n\n let ssrcLines = SDPUtil.findLines(mediaLines[0], 'a=ssrc:');\n\n ssrcLines\n = ssrcLines.filter(line => line.indexOf(`msid:${streamId}`) !== -1);\n if (!ssrcLines.length) {\n GlobalOnErrorHandler.callErrorHandler(\n new Error(`No SSRC lines found in remote SDP for remote stream[msid=${streamId},type=${mediaType}]`));\n\n // Abort\n return;\n }\n\n // FIXME the length of ssrcLines[0] not verified, but it will fail\n // with global error handler anyway\n const ssrcStr = ssrcLines[0].substring(7).split(' ')[0];\n const trackSsrc = Number(ssrcStr);\n const ownerEndpointId = this.signalingLayer.getSSRCOwner(trackSsrc);\n\n if (isNaN(trackSsrc) || trackSsrc < 0) {\n GlobalOnErrorHandler.callErrorHandler(\n new Error(\n `Invalid SSRC for remote stream[ssrc=${trackSsrc},id=${streamId},type=${mediaType}]`));\n\n // Abort\n return;\n } else if (!ownerEndpointId) {\n GlobalOnErrorHandler.callErrorHandler(\n new Error(\n `No SSRC owner known for remote stream[ssrc=${trackSsrc},id=${streamId},type=${mediaType}]`));\n\n // Abort\n return;\n }\n\n\n let sourceName;\n\n if (FeatureFlags.isSourceNameSignalingEnabled()) {\n sourceName = this.signalingLayer.getTrackSourceName(trackSsrc);\n\n // If source name was not signaled, we'll generate one which allows testing signaling\n // when mixing legacy(mobile) with new clients.\n if (!sourceName) {\n sourceName = getSourceNameForJitsiTrack(ownerEndpointId, mediaType, 0);\n }\n }\n\n // eslint-disable-next-line no-undef\n logger.info(`${this} creating remote track[endpoint=${ownerEndpointId},ssrc=${trackSsrc},`\n + `type=${mediaType},sourceName=${sourceName}]`);\n\n const peerMediaInfo\n = this.signalingLayer.getPeerMediaInfo(ownerEndpointId, mediaType);\n\n if (!peerMediaInfo) {\n GlobalOnErrorHandler.callErrorHandler(\n new Error(`${this}: no peer media info available for ${ownerEndpointId}`));\n\n return;\n }\n\n const muted = peerMediaInfo.muted;\n const videoType = peerMediaInfo.videoType; // can be undefined\n\n // eslint-disable-next-line no-undef\n this._createRemoteTrack(\n ownerEndpointId, stream, track, mediaType, videoType, trackSsrc, muted, sourceName);\n};\n\n// FIXME cleanup params\n/* eslint-disable max-params */\n\n/**\n * Initializes a new JitsiRemoteTrack instance with the data provided by\n * the signaling layer and SDP.\n *\n * @param {string} ownerEndpointId the owner's endpoint ID (MUC nickname)\n * @param {MediaStream} stream the WebRTC stream instance\n * @param {MediaStreamTrack} track the WebRTC track instance\n * @param {MediaType} mediaType the track's type of the media\n * @param {VideoType} [videoType] the track's type of the video (if applicable)\n * @param {number} ssrc the track's main SSRC number\n * @param {boolean} muted the initial muted status\n * @param {String} sourceName the track's source name\n */\nTraceablePeerConnection.prototype._createRemoteTrack = function(\n ownerEndpointId,\n stream,\n track,\n mediaType,\n videoType,\n ssrc,\n muted,\n sourceName) {\n let remoteTracksMap = this.remoteTracks.get(ownerEndpointId);\n\n if (!remoteTracksMap) {\n remoteTracksMap = new Map();\n remoteTracksMap.set(MediaType.AUDIO, new Set());\n remoteTracksMap.set(MediaType.VIDEO, new Set());\n this.remoteTracks.set(ownerEndpointId, remoteTracksMap);\n }\n\n const userTracksByMediaType = remoteTracksMap.get(mediaType);\n\n if (userTracksByMediaType?.size\n && Array.from(userTracksByMediaType).find(jitsiTrack => jitsiTrack.getTrack() === track)) {\n // Ignore duplicated event which can originate either from 'onStreamAdded' or 'onTrackAdded'.\n logger.info(`${this} ignored duplicated track event for track[endpoint=${ownerEndpointId},type=${mediaType}]`);\n\n return;\n } else if (userTracksByMediaType?.size && !FeatureFlags.isSourceNameSignalingEnabled()) {\n logger.error(`${this} received a second remote track for track[endpoint=${ownerEndpointId},type=${mediaType}]`\n + 'deleting the existing track');\n const existingTrack = Array.from(userTracksByMediaType)[0];\n\n // The exisiting track needs to be removed here. This happens on Safari sometimes when a SSRC is removed from\n // the remote description and the browser doesn't fire a 'removetrack' event on the associated MediaStream.\n this._remoteTrackRemoved(existingTrack.getOriginalStream(), existingTrack.getTrack());\n }\n\n const remoteTrack\n = new JitsiRemoteTrack(\n this.rtc,\n this.rtc.conference,\n ownerEndpointId,\n stream,\n track,\n mediaType,\n videoType,\n ssrc,\n muted,\n this.isP2P,\n sourceName);\n\n userTracksByMediaType.add(remoteTrack);\n this.eventEmitter.emit(RTCEvents.REMOTE_TRACK_ADDED, remoteTrack, this);\n};\n\n/* eslint-enable max-params */\n\n/**\n * Handles remote stream removal.\n * @param stream the WebRTC MediaStream object which is being removed from the\n * PeerConnection\n */\nTraceablePeerConnection.prototype._remoteStreamRemoved = function(stream) {\n if (!RTC.isUserStream(stream)) {\n const id = RTC.getStreamID(stream);\n\n logger.info(`Ignored remote 'stream removed' event for stream[id=${id}]`);\n\n return;\n }\n\n // Call remoteTrackRemoved for each track in the stream\n const streamVideoTracks = stream.getVideoTracks();\n\n for (const videoTrack of streamVideoTracks) {\n this._remoteTrackRemoved(stream, videoTrack);\n }\n const streamAudioTracks = stream.getAudioTracks();\n\n for (const audioTrack of streamAudioTracks) {\n this._remoteTrackRemoved(stream, audioTrack);\n }\n};\n\n/**\n * Handles remote media track removal.\n *\n * @param {MediaStream} stream - WebRTC MediaStream instance which is the parent of the track.\n * @param {MediaStreamTrack} track - WebRTC MediaStreamTrack which has been removed from the PeerConnection.\n * @returns {void}\n */\nTraceablePeerConnection.prototype._remoteTrackRemoved = function(stream, track) {\n const streamId = RTC.getStreamID(stream);\n const trackId = track && RTC.getTrackID(track);\n\n if (!RTC.isUserStreamById(streamId)) {\n logger.info(`${this} ignored remote 'stream removed' event for non-user stream[id=${streamId}]`);\n\n return;\n }\n\n if (!streamId) {\n GlobalOnErrorHandler.callErrorHandler(new Error(`${this} remote track removal failed - no stream ID`));\n\n return;\n }\n\n if (!trackId) {\n GlobalOnErrorHandler.callErrorHandler(new Error(`${this} remote track removal failed - no track ID`));\n\n return;\n }\n\n const toBeRemoved = this.getRemoteTracks().find(\n remoteTrack => remoteTrack.getStreamId() === streamId\n && remoteTrack.getTrackId() === trackId);\n\n if (!toBeRemoved) {\n GlobalOnErrorHandler.callErrorHandler(new Error(`${this} remote track removal failed - track not found`));\n\n return;\n }\n\n logger.info(`${this} remote track removed stream[id=${streamId},trackId=${trackId}]`);\n this._removeRemoteTrack(toBeRemoved);\n};\n\n/**\n * Removes all JitsiRemoteTracks associated with given MUC nickname (resource part of the JID).\n *\n * @param {string} owner - The resource part of the MUC JID.\n * @returns {JitsiRemoteTrack[]} - The array of removed tracks.\n */\nTraceablePeerConnection.prototype.removeRemoteTracks = function(owner) {\n let removedTracks = [];\n const remoteTracksByMedia = this.remoteTracks.get(owner);\n\n if (remoteTracksByMedia) {\n removedTracks = removedTracks.concat(Array.from(remoteTracksByMedia.get(MediaType.AUDIO)));\n removedTracks = removedTracks.concat(Array.from(remoteTracksByMedia.get(MediaType.VIDEO)));\n this.remoteTracks.delete(owner);\n }\n logger.debug(`${this} removed remote tracks[endpoint=${owner},count=${removedTracks.length}`);\n\n return removedTracks;\n};\n\n/**\n * Removes and disposes given JitsiRemoteTrack instance. Emits {@link RTCEvents.REMOTE_TRACK_REMOVED}.\n *\n * @param {JitsiRemoteTrack} toBeRemoved - The remote track to be removed.\n * @returns {void}\n */\nTraceablePeerConnection.prototype._removeRemoteTrack = function(toBeRemoved) {\n toBeRemoved.dispose();\n const participantId = toBeRemoved.getParticipantId();\n const userTracksByMediaType = this.remoteTracks.get(participantId);\n\n if (!userTracksByMediaType) {\n logger.error(`${this} removeRemoteTrack: no remote tracks map for endpoint=${participantId}`);\n } else if (!userTracksByMediaType.get(toBeRemoved.getType())?.delete(toBeRemoved)) {\n logger.error(`${this} Failed to remove ${toBeRemoved} - type mapping messed up ?`);\n }\n this.eventEmitter.emit(RTCEvents.REMOTE_TRACK_REMOVED, toBeRemoved);\n};\n\n/**\n * Returns a map with keys msid/mediaType and TrackSSRCInfo values.\n * @param {RTCSessionDescription} desc the local description.\n * @return {Map}\n */\nTraceablePeerConnection.prototype._extractSSRCMap = function(desc) {\n /**\n * Track SSRC infos mapped by stream ID (msid) or mediaType (unfied-plan)\n * @type {Map}\n */\n const ssrcMap = new Map();\n\n /**\n * Groups mapped by primary SSRC number\n * @type {Map>}\n */\n const groupsMap = new Map();\n\n if (typeof desc !== 'object' || desc === null\n || typeof desc.sdp !== 'string') {\n logger.warn('An empty description was passed as an argument');\n\n return ssrcMap;\n }\n\n const session = transform.parse(desc.sdp);\n\n if (!Array.isArray(session.media)) {\n return ssrcMap;\n }\n\n let media = session.media;\n\n // For unified plan clients, only the first audio and video mlines will have ssrcs for the local sources.\n // The rest of the m-lines are for the recv-only sources, one for each remote source.\n if (this._usesUnifiedPlan) {\n media = [];\n [ MediaType.AUDIO, MediaType.VIDEO ].forEach(mediaType => {\n const mLine = session.media.find(m => m.type === mediaType);\n\n mLine && media.push(mLine);\n });\n }\n\n for (const mLine of media) {\n if (!Array.isArray(mLine.ssrcs)) {\n continue; // eslint-disable-line no-continue\n }\n\n if (Array.isArray(mLine.ssrcGroups)) {\n for (const group of mLine.ssrcGroups) {\n if (typeof group.semantics !== 'undefined'\n && typeof group.ssrcs !== 'undefined') {\n // Parse SSRCs and store as numbers\n const groupSSRCs = group.ssrcs.split(' ').map(ssrcStr => parseInt(ssrcStr, 10));\n const primarySSRC = groupSSRCs[0];\n\n // Note that group.semantics is already present\n group.ssrcs = groupSSRCs;\n\n // eslint-disable-next-line max-depth\n if (!groupsMap.has(primarySSRC)) {\n groupsMap.set(primarySSRC, []);\n }\n groupsMap.get(primarySSRC).push(group);\n }\n }\n }\n\n let ssrcs = mLine.ssrcs;\n\n // Filter the ssrcs with 'msid' attribute for plan-b clients and 'cname' for unified-plan clients.\n ssrcs = this._usesUnifiedPlan\n ? ssrcs.filter(s => s.attribute === 'cname')\n : ssrcs.filter(s => s.attribute === 'msid');\n\n for (const ssrc of ssrcs) {\n // Use the mediaType as key for the source map for unified plan clients since msids are not part of\n // the standard and the unified plan SDPs do not have a proper msid attribute for the sources.\n // Also the ssrcs for sources do not change for Unified plan clients since RTCRtpSender#replaceTrack is\n // used for switching the tracks so it is safe to use the mediaType as the key for the TrackSSRCInfo map.\n const key = this._usesUnifiedPlan ? mLine.type : ssrc.value;\n const ssrcNumber = ssrc.id;\n let ssrcInfo = ssrcMap.get(key);\n\n if (!ssrcInfo) {\n ssrcInfo = {\n ssrcs: [],\n groups: [],\n msid: key\n };\n ssrcMap.set(key, ssrcInfo);\n }\n ssrcInfo.ssrcs.push(ssrcNumber);\n\n if (groupsMap.has(ssrcNumber)) {\n const ssrcGroups = groupsMap.get(ssrcNumber);\n\n for (const group of ssrcGroups) {\n ssrcInfo.groups.push(group);\n }\n }\n }\n }\n\n return ssrcMap;\n};\n\n/**\n * Takes a SessionDescription object and returns a \"normalized\" version.\n * Currently it takes care of ordering the a=ssrc lines and denoting receive\n * only SSRCs.\n */\nconst normalizePlanB = function(desc) {\n if (typeof desc !== 'object' || desc === null\n || typeof desc.sdp !== 'string') {\n logger.warn('An empty description was passed as an argument');\n\n return desc;\n }\n\n // eslint-disable-next-line no-shadow\n const transform = require('sdp-transform');\n const session = transform.parse(desc.sdp);\n\n if (typeof session !== 'undefined'\n && typeof session.media !== 'undefined'\n && Array.isArray(session.media)) {\n session.media.forEach(mLine => {\n\n // Chrome appears to be picky about the order in which a=ssrc lines\n // are listed in an m-line when rtx is enabled (and thus there are\n // a=ssrc-group lines with FID semantics). Specifically if we have\n // \"a=ssrc-group:FID S1 S2\" and the \"a=ssrc:S2\" lines appear before\n // the \"a=ssrc:S1\" lines, SRD fails.\n // So, put SSRC which appear as the first SSRC in an FID ssrc-group\n // first.\n const firstSsrcs = [];\n const newSsrcLines = [];\n\n if (typeof mLine.ssrcGroups !== 'undefined'\n && Array.isArray(mLine.ssrcGroups)) {\n mLine.ssrcGroups.forEach(group => {\n if (typeof group.semantics !== 'undefined'\n && group.semantics === 'FID') {\n if (typeof group.ssrcs !== 'undefined') {\n firstSsrcs.push(Number(group.ssrcs.split(' ')[0]));\n }\n }\n });\n }\n\n if (Array.isArray(mLine.ssrcs)) {\n let i;\n\n for (i = 0; i < mLine.ssrcs.length; i++) {\n if (typeof mLine.ssrcs[i] === 'object'\n && typeof mLine.ssrcs[i].id !== 'undefined'\n && firstSsrcs.indexOf(mLine.ssrcs[i].id) >= 0) {\n newSsrcLines.push(mLine.ssrcs[i]);\n delete mLine.ssrcs[i];\n }\n }\n\n for (i = 0; i < mLine.ssrcs.length; i++) {\n if (typeof mLine.ssrcs[i] !== 'undefined') {\n newSsrcLines.push(mLine.ssrcs[i]);\n }\n }\n\n mLine.ssrcs = replaceDefaultUnifiedPlanMsid(newSsrcLines);\n }\n });\n }\n\n const resStr = transform.write(session);\n\n\n return new RTCSessionDescription({\n type: desc.type,\n sdp: resStr\n });\n};\n\n/**\n * Unified plan differentiates a remote track not associated with a stream using\n * the msid \"-\", which can incorrectly trigger an onaddstream event in plan-b.\n * For jitsi, these tracks are actually receive-only ssrcs. To prevent\n * onaddstream from firing, remove the ssrcs with msid \"-\" except the cname\n * line. Normally the ssrcs are not used by the client, as the bridge controls\n * media flow, but keep one reference to the ssrc for the p2p case.\n *\n * @param {Array} ssrcLines - The ssrc lines from a remote description.\n * @private\n * @returns {Array} ssrcLines with removed lines referencing msid \"-\".\n */\nfunction replaceDefaultUnifiedPlanMsid(ssrcLines = []) {\n if (!browser.isChrome() || !browser.isVersionGreaterThan(70)) {\n return ssrcLines;\n }\n\n let filteredLines = [ ...ssrcLines ];\n\n const problematicSsrcIds = ssrcLines.filter(ssrcLine =>\n ssrcLine.attribute === 'mslabel' && ssrcLine.value === '-')\n .map(ssrcLine => ssrcLine.id);\n\n problematicSsrcIds.forEach(ssrcId => {\n // Find the cname which is to be modified and left in.\n const cnameLine = filteredLines.find(line =>\n line.id === ssrcId && line.attribute === 'cname');\n\n cnameLine.value = `${MediaDirection.RECVONLY}-${ssrcId}`;\n\n // Remove all of lines for the ssrc.\n filteredLines\n = filteredLines.filter(line => line.id !== ssrcId);\n\n // But re-add the cname line so there is a reference kept to the ssrc\n // in the SDP.\n filteredLines.push(cnameLine);\n });\n\n return filteredLines;\n}\n\n/**\n * Makes sure that both audio and video directions are configured as 'sendrecv'.\n * @param {Object} localDescription the SDP object as defined by WebRTC.\n * @param {object} options TracablePeerConnection config options.\n */\nconst enforceSendRecv = function(localDescription, options) {\n if (!localDescription) {\n throw new Error('No local description passed in.');\n }\n\n const transformer = new SdpTransformWrap(localDescription.sdp);\n const audioMedia = transformer.selectMedia(MediaType.AUDIO)?.[0];\n let changed = false;\n\n if (audioMedia && audioMedia.direction !== MediaDirection.SENDRECV) {\n if (options.startSilent) {\n audioMedia.direction = MediaDirection.INACTIVE;\n } else {\n audioMedia.direction = MediaDirection.SENDRECV;\n }\n\n changed = true;\n }\n\n const videoMedia = transformer.selectMedia(MediaType.VIDEO)?.[0];\n\n if (videoMedia && videoMedia.direction !== MediaDirection.SENDRECV) {\n videoMedia.direction = MediaDirection.SENDRECV;\n changed = true;\n }\n\n if (changed) {\n return new RTCSessionDescription({\n type: localDescription.type,\n sdp: transformer.toRawSDP()\n });\n }\n\n return localDescription;\n};\n\n/**\n *\n * @param {JitsiLocalTrack} localTrack\n */\nTraceablePeerConnection.prototype.getLocalSSRC = function(localTrack) {\n const ssrcInfo = this._getSSRC(localTrack.rtcId);\n\n return ssrcInfo && ssrcInfo.ssrcs[0];\n};\n\n/**\n * When doing unified plan simulcast, we'll have a set of ssrcs with the\n * same msid but no ssrc-group, since unified plan signals the simulcast\n * group via the a=simulcast line. Unfortunately, Jicofo will complain\n * if it sees ssrcs with matching msids but no ssrc-group, so we'll inject\n * an ssrc-group line to make Jicofo happy.\n * @param desc A session description object (with 'type' and 'sdp' fields)\n * @return A session description object with its sdp field modified to\n * contain an inject ssrc-group for simulcast\n */\nTraceablePeerConnection.prototype._injectSsrcGroupForUnifiedSimulcast\n = function(desc) {\n const sdp = transform.parse(desc.sdp);\n const video = sdp.media.find(mline => mline.type === 'video');\n\n // Check if the browser supports RTX, add only the primary ssrcs to the SIM group if that is the case.\n video.ssrcGroups = video.ssrcGroups || [];\n const fidGroups = video.ssrcGroups.filter(group => group.semantics === 'FID');\n\n if (video.simulcast || video.simulcast_03) {\n const ssrcs = [];\n\n if (fidGroups && fidGroups.length) {\n fidGroups.forEach(group => {\n ssrcs.push(group.ssrcs.split(' ')[0]);\n });\n } else {\n video.ssrcs.forEach(ssrc => {\n if (ssrc.attribute === 'msid') {\n ssrcs.push(ssrc.id);\n }\n });\n }\n if (video.ssrcGroups.find(group => group.semantics === 'SIM')) {\n // Group already exists, no need to do anything\n return desc;\n }\n\n video.ssrcGroups.push({\n semantics: 'SIM',\n ssrcs: ssrcs.join(' ')\n });\n }\n\n return new RTCSessionDescription({\n type: desc.type,\n sdp: transform.write(sdp)\n });\n };\n\n/* eslint-disable-next-line vars-on-top */\nconst getters = {\n signalingState() {\n return this.peerconnection.signalingState;\n },\n iceConnectionState() {\n return this.peerconnection.iceConnectionState;\n },\n connectionState() {\n return this.peerconnection.connectionState;\n },\n localDescription() {\n let desc = this.peerconnection.localDescription;\n\n if (!desc) {\n logger.debug(`${this} getLocalDescription no localDescription found`);\n\n return {};\n }\n\n this.trace('getLocalDescription::preTransform', dumpSDP(desc));\n\n // If the browser is running in unified plan mode and this is a jvb connection,\n // transform the SDP to Plan B first.\n if (this._usesUnifiedPlan && !this.isP2P) {\n desc = this.interop.toPlanB(desc);\n this.trace('getLocalDescription::postTransform (Plan B)',\n dumpSDP(desc));\n\n desc = this._injectSsrcGroupForUnifiedSimulcast(desc);\n this.trace('getLocalDescription::postTransform (inject ssrc group)',\n dumpSDP(desc));\n } else if (!this._usesUnifiedPlan) {\n if (browser.doesVideoMuteByStreamRemove()) {\n desc = this.localSdpMunger.maybeAddMutedLocalVideoTracksToSDP(desc);\n logger.debug(\n 'getLocalDescription::postTransform (munge local SDP)', desc);\n }\n\n // What comes out of this getter will be signalled over Jingle to\n // the other peer, so we need to make sure the media direction is\n // 'sendrecv' because we won't change the direction later and don't want\n // the other peer to think we can't send or receive.\n //\n // Note that the description we set in chrome does have the accurate\n // direction (e.g. 'recvonly'), since that is technically what is\n // happening (check setLocalDescription impl).\n desc = enforceSendRecv(desc, this.options);\n }\n\n // See the method's doc for more info about this transformation.\n desc = this.localSdpMunger.transformStreamIdentifiers(desc);\n\n return desc;\n },\n remoteDescription() {\n let desc = this.peerconnection.remoteDescription;\n\n if (!desc) {\n logger.debug(`${this} getRemoteDescription no remoteDescription found`);\n\n return {};\n }\n this.trace('getRemoteDescription::preTransform', dumpSDP(desc));\n\n if (this._usesUnifiedPlan) {\n if (this.isP2P) {\n // Adjust the media direction for p2p based on whether a local source has been added.\n desc = this._adjustRemoteMediaDirection(desc);\n } else {\n // If this is a jvb connection, transform the SDP to Plan B first.\n desc = this.interop.toPlanB(desc);\n this.trace('getRemoteDescription::postTransform (Plan B)', dumpSDP(desc));\n }\n }\n\n return desc;\n }\n};\n\nObject.keys(getters).forEach(prop => {\n Object.defineProperty(\n TraceablePeerConnection.prototype,\n prop, {\n get: getters[prop]\n }\n );\n});\n\nTraceablePeerConnection.prototype._getSSRC = function(rtcId) {\n return this.localSSRCs.get(rtcId);\n};\n\n/**\n * Checks if low fps screensharing is in progress.\n *\n * @private\n * @returns {boolean} Returns true if 5 fps screensharing is in progress, false otherwise.\n */\nTraceablePeerConnection.prototype.isSharingLowFpsScreen = function() {\n return this._isSharingScreen() && this._capScreenshareBitrate;\n};\n\n/**\n * Checks if screensharing is in progress.\n *\n * @returns {boolean} Returns true if a desktop track has been added to the peerconnection, false otherwise.\n */\nTraceablePeerConnection.prototype._isSharingScreen = function() {\n const tracks = this.getLocalVideoTracks();\n\n return Boolean(tracks.find(track => track.videoType === VideoType.DESKTOP));\n};\n\n/**\n * Munges the order of the codecs in the SDP passed based on the preference\n * set through config.js settings. All instances of the specified codec are\n * moved up to the top of the list when it is preferred. The specified codec\n * is deleted from the list if the configuration specifies that the codec be\n * disabled.\n * @param {RTCSessionDescription} description that needs to be munged.\n * @returns {RTCSessionDescription} the munged description.\n */\nTraceablePeerConnection.prototype._mungeCodecOrder = function(description) {\n if (!this.codecPreference) {\n return description;\n }\n\n const parsedSdp = transform.parse(description.sdp);\n\n // Only the m-line that defines the source the browser will be sending should need to change.\n // This is typically the first m-line with the matching media type.\n const mLine = parsedSdp.media.find(m => m.type === this.codecPreference.mediaType);\n\n if (!mLine) {\n return description;\n }\n\n if (this.codecPreference.enable) {\n SDPUtil.preferCodec(mLine, this.codecPreference.mimeType);\n\n // Strip the high profile H264 codecs on mobile clients for p2p connection.\n // High profile codecs give better quality at the expense of higher load which\n // we do not want on mobile clients.\n // Jicofo offers only the baseline code for the jvb connection.\n // TODO - add check for mobile browsers once js-utils provides that check.\n if (this.codecPreference.mimeType === CodecMimeType.H264 && browser.isReactNative() && this.isP2P) {\n SDPUtil.stripCodec(mLine, this.codecPreference.mimeType, true /* high profile */);\n }\n\n // Set the max bitrate here on the SDP so that the configured max. bitrate is effective\n // as soon as the browser switches to VP9.\n if (this.codecPreference.mimeType === CodecMimeType.VP9\n && this.getConfiguredVideoCodec() === CodecMimeType.VP9) {\n const bitrates = this.tpcUtils.videoBitrates.VP9 || this.tpcUtils.videoBitrates;\n const hdBitrate = bitrates.high ? bitrates.high : HD_BITRATE;\n const limit = Math.floor((this._isSharingScreen() ? HD_BITRATE : hdBitrate) / 1000);\n\n // Use only the HD bitrate for now as there is no API available yet for configuring\n // the bitrates on the individual SVC layers.\n mLine.bandwidth = [ {\n type: 'AS',\n limit\n } ];\n } else {\n // Clear the bandwidth limit in SDP when VP9 is no longer the preferred codec.\n // This is needed on react native clients as react-native-webrtc returns the\n // SDP that the application passed instead of returning the SDP off the native side.\n // This line automatically gets cleared on web on every renegotiation.\n mLine.bandwidth = undefined;\n }\n } else {\n SDPUtil.stripCodec(mLine, this.codecPreference.mimeType);\n }\n\n return new RTCSessionDescription({\n type: description.type,\n sdp: transform.write(parsedSdp)\n });\n};\n\n/**\n * Add {@link JitsiLocalTrack} to this TPC.\n * @param {JitsiLocalTrack} track\n * @param {boolean} isInitiator indicates if the endpoint is the offerer.\n * @returns {Promise} - resolved when done.\n */\nTraceablePeerConnection.prototype.addTrack = function(track, isInitiator = false) {\n const rtcId = track.rtcId;\n\n logger.info(`${this} adding ${track}`);\n\n if (this.localTracks.has(rtcId)) {\n\n return Promise.reject(new Error(`${track} is already in ${this}`));\n }\n\n this.localTracks.set(rtcId, track);\n const webrtcStream = track.getOriginalStream();\n\n if (this._usesUnifiedPlan) {\n logger.debug(`${this} TPC.addTrack using unified plan`);\n if (webrtcStream || !this.isP2P) {\n try {\n this.tpcUtils.addTrack(track, isInitiator);\n } catch (error) {\n logger.error(`${this} Adding track=${track} failed: ${error?.message}`);\n\n return Promise.reject(error);\n }\n }\n } else {\n // Use addStream API for the plan-b case.\n if (webrtcStream) {\n this._addStream(webrtcStream);\n\n // It's not ok for a track to not have a WebRTC stream if:\n } else if (!browser.doesVideoMuteByStreamRemove()\n || track.isAudioTrack()\n || (track.isVideoTrack() && !track.isMuted())) {\n return Promise.reject(new Error(`${this} no WebRTC stream for track=${track}`));\n }\n\n // Muted video tracks do not have WebRTC stream\n if (browser.doesVideoMuteByStreamRemove() && track.isVideoTrack() && track.isMuted()) {\n const ssrcInfo = this.generateNewStreamSSRCInfo(track);\n\n this.sdpConsistency.setPrimarySsrc(ssrcInfo.ssrcs[0]);\n const simGroup\n = ssrcInfo.groups.find(groupInfo => groupInfo.semantics === 'SIM');\n\n if (simGroup) {\n this.simulcast.setSsrcCache(simGroup.ssrcs);\n }\n const fidGroups\n = ssrcInfo.groups.filter(\n groupInfo => groupInfo.semantics === 'FID');\n\n if (fidGroups) {\n const rtxSsrcMapping = new Map();\n\n fidGroups.forEach(fidGroup => {\n const primarySsrc = fidGroup.ssrcs[0];\n const rtxSsrc = fidGroup.ssrcs[1];\n\n rtxSsrcMapping.set(primarySsrc, rtxSsrc);\n });\n this.rtxModifier.setSsrcCache(rtxSsrcMapping);\n }\n }\n }\n let promiseChain = Promise.resolve();\n\n // On Firefox, the encodings have to be configured on the sender only after the transceiver is created.\n if (browser.isFirefox()) {\n promiseChain = promiseChain.then(() => webrtcStream && this.tpcUtils.setEncodings(track));\n }\n\n return promiseChain;\n};\n\n/**\n * Adds local track as part of the unmute operation.\n * @param {JitsiLocalTrack} track the track to be added as part of the unmute operation.\n *\n * @return {Promise} Promise that resolves to true if the underlying PeerConnection's\n * state has changed and renegotiation is required, false if no renegotiation is needed or\n * Promise is rejected when something goes wrong.\n */\nTraceablePeerConnection.prototype.addTrackUnmute = function(track) {\n logger.info(`${this} Adding track=${track} as unmute`);\n\n if (!this._assertTrackBelongs('addTrackUnmute', track)) {\n // Abort\n\n return Promise.reject('Track not found on the peerconnection');\n }\n\n const webRtcStream = track.getOriginalStream();\n\n if (!webRtcStream) {\n logger.error(`${this} Unable to add track=${track} as unmute - no WebRTC stream`);\n\n return Promise.reject('Stream not found');\n }\n\n if (this._usesUnifiedPlan) {\n return this.tpcUtils.replaceTrack(null, track).then(() => this.isP2P);\n }\n\n this._addStream(webRtcStream);\n\n return Promise.resolve(true);\n};\n\n/**\n * Adds WebRTC media stream to the underlying PeerConnection\n * @param {MediaStream} mediaStream\n * @private\n */\nTraceablePeerConnection.prototype._addStream = function(mediaStream) {\n this.peerconnection.addStream(mediaStream);\n this._addedStreams.push(mediaStream);\n};\n\n/**\n * Removes WebRTC media stream from the underlying PeerConection\n * @param {MediaStream} mediaStream\n */\nTraceablePeerConnection.prototype._removeStream = function(mediaStream) {\n this.peerconnection.removeStream(mediaStream);\n this._addedStreams\n = this._addedStreams.filter(stream => stream !== mediaStream);\n};\n\n/**\n * This method when called will check if given localTrack belongs to\n * this TPC (that it has been previously added using {@link addTrack}). If the\n * track does not belong an error message will be logged.\n * @param {string} methodName the method name that will be logged in an error\n * message\n * @param {JitsiLocalTrack} localTrack\n * @return {boolean} true if given local track belongs to this TPC or\n * false otherwise.\n * @private\n */\nTraceablePeerConnection.prototype._assertTrackBelongs = function(\n methodName,\n localTrack) {\n const doesBelong = this.localTracks.has(localTrack?.rtcId);\n\n if (!doesBelong) {\n logger.error(`${this} ${methodName}: track=${localTrack} does not belong to pc`);\n }\n\n return doesBelong;\n};\n\n/**\n * Returns the codec that is configured on the client as the preferred video codec.\n * This takes into account the current order of codecs in the local description sdp.\n *\n * @returns {CodecMimeType} The codec that is set as the preferred codec to receive\n * video in the local SDP.\n */\nTraceablePeerConnection.prototype.getConfiguredVideoCodec = function() {\n const sdp = this.peerconnection.localDescription?.sdp;\n const defaultCodec = CodecMimeType.VP8;\n\n if (!sdp) {\n return defaultCodec;\n }\n const parsedSdp = transform.parse(sdp);\n const mLine = parsedSdp.media.find(m => m.type === MediaType.VIDEO);\n const codec = mLine.rtp[0].codec;\n\n if (codec) {\n return Object.values(CodecMimeType).find(value => value === codec.toLowerCase());\n }\n\n return defaultCodec;\n};\n\n/**\n * Enables or disables simulcast for screenshare based on the frame rate requested for desktop track capture.\n *\n * @param {number} maxFps framerate to be used for desktop track capture.\n */\nTraceablePeerConnection.prototype.setDesktopSharingFrameRate = function(maxFps) {\n const lowFps = maxFps <= SS_DEFAULT_FRAME_RATE;\n\n this._capScreenshareBitrate = this.isSimulcastOn() && lowFps;\n};\n\n/**\n * Sets the codec preference on the peerconnection. The codec preference goes into effect when\n * the next renegotiation happens.\n *\n * @param {CodecMimeType} preferredCodec the preferred codec.\n * @param {CodecMimeType} disabledCodec the codec that needs to be disabled.\n * @returns {void}\n */\nTraceablePeerConnection.prototype.setVideoCodecs = function(preferredCodec = null, disabledCodec = null) {\n // If both enable and disable are set, disable settings will prevail.\n const enable = disabledCodec === null;\n const mimeType = disabledCodec ? disabledCodec : preferredCodec;\n\n if (this.codecPreference && (preferredCodec || disabledCodec)) {\n this.codecPreference.enable = enable;\n this.codecPreference.mimeType = mimeType;\n } else if (preferredCodec || disabledCodec) {\n this.codecPreference = {\n enable,\n mediaType: MediaType.VIDEO,\n mimeType\n };\n } else {\n logger.warn(`${this} Invalid codec settings[preferred=${preferredCodec},disabled=${disabledCodec}],\n atleast one value is needed`);\n }\n};\n\n/**\n * Tells if the given WebRTC MediaStream has been added to\n * the underlying WebRTC PeerConnection.\n * @param {MediaStream} mediaStream\n * @returns {boolean}\n */\nTraceablePeerConnection.prototype.isMediaStreamInPc = function(mediaStream) {\n return this._addedStreams.indexOf(mediaStream) > -1;\n};\n\n/**\n * Remove local track from this TPC.\n * @param {JitsiLocalTrack} localTrack the track to be removed from this TPC.\n *\n * FIXME It should probably remove a boolean just like {@link removeTrackMute}\n * The same applies to addTrack.\n */\nTraceablePeerConnection.prototype.removeTrack = function(localTrack) {\n const webRtcStream = localTrack.getOriginalStream();\n\n this.trace(\n 'removeStream',\n localTrack.rtcId, webRtcStream ? webRtcStream.id : undefined);\n\n if (!this._assertTrackBelongs('removeStream', localTrack)) {\n // Abort - nothing to be done here\n return;\n }\n this.localTracks.delete(localTrack.rtcId);\n this.localSSRCs.delete(localTrack.rtcId);\n\n if (webRtcStream) {\n this.peerconnection.removeStream(webRtcStream);\n }\n};\n\n/**\n * Returns the sender corresponding to the given media type.\n * @param {MEDIA_TYPE} mediaType - The media type 'audio' or 'video' to be used for the search.\n * @returns {RTPSender|undefined} - The found sender or undefined if no sender\n * was found.\n */\nTraceablePeerConnection.prototype.findSenderByKind = function(mediaType) {\n if (this.peerconnection.getSenders) {\n return this.peerconnection.getSenders().find(s => s.track && s.track.kind === mediaType);\n }\n};\n\n/**\n * Returns the receiver corresponding to the given MediaStreamTrack.\n *\n * @param {MediaSreamTrack} track - The media stream track used for the search.\n * @returns {RTCRtpReceiver|undefined} - The found receiver or undefined if no receiver\n * was found.\n */\nTraceablePeerConnection.prototype.findReceiverForTrack = function(track) {\n return this.peerconnection.getReceivers().find(r => r.track === track);\n};\n\n/**\n * Returns the sender corresponding to the given MediaStreamTrack.\n *\n * @param {MediaSreamTrack} track - The media stream track used for the search.\n * @returns {RTCRtpSender|undefined} - The found sender or undefined if no sender\n * was found.\n */\nTraceablePeerConnection.prototype.findSenderForTrack = function(track) {\n if (this.peerconnection.getSenders) {\n return this.peerconnection.getSenders().find(s => s.track === track);\n }\n};\n\n/**\n * Replaces oldTrack with newTrack from the peer connection.\n * Either oldTrack or newTrack can be null; replacing a valid\n * oldTrack with a null newTrack effectively just removes\n * oldTrack\n *\n * @param {JitsiLocalTrack|null} oldTrack - The current track in use to be replaced on the pc.\n * @param {JitsiLocalTrack|null} newTrack - The new track to be used.\n *\n * @returns {Promise} - If the promise resolves with true, renegotiation will be needed.\n * Otherwise no renegotiation is needed.\n */\nTraceablePeerConnection.prototype.replaceTrack = function(oldTrack, newTrack) {\n if (!(oldTrack || newTrack)) {\n logger.info(`${this} replaceTrack called with no new track and no old track`);\n\n return Promise.resolve();\n }\n\n // If a track is being added to the peerconnection for the first time, we want the source signaling to be sent to\n // Jicofo before the mute state is sent over presence. Therefore, trigger a renegotiation in this case. If we\n // rely on \"negotiationneeded\" fired by the browser to signal new ssrcs, the mute state in presence will be sent\n // before the source signaling which is undesirable.\n const negotiationNeeded = Boolean(!oldTrack || !this.localTracks.has(oldTrack?.rtcId));\n\n if (this._usesUnifiedPlan) {\n logger.debug(`${this} TPC.replaceTrack using unified plan`);\n const mediaType = newTrack?.getType() ?? oldTrack?.getType();\n const stream = newTrack?.getOriginalStream();\n const promise = newTrack && !stream\n\n // Ignore cases when the track is replaced while the device is in a muted state.\n // The track will be replaced again on the peerconnection when the user unmutes.\n ? Promise.resolve()\n : this.tpcUtils.replaceTrack(oldTrack, newTrack);\n\n return promise\n .then(transceiver => {\n oldTrack && this.localTracks.delete(oldTrack.rtcId);\n newTrack && this.localTracks.set(newTrack.rtcId, newTrack);\n const mediaActive = mediaType === MediaType.AUDIO\n ? this.audioTransferActive\n : this.videoTransferActive;\n\n // Set the transceiver direction only if media is not suspended on the connection. This happens when\n // the client is using the p2p connection. Transceiver direction is updated when media is resumed on\n // this connection again.\n if (transceiver && mediaActive) {\n transceiver.direction = newTrack ? MediaDirection.SENDRECV : MediaDirection.RECVONLY;\n }\n\n // Avoid configuring the encodings on Chromium/Safari until simulcast is configured\n // for the newly added track using SDP munging which happens during the renegotiation.\n const configureEncodingsPromise = browser.usesSdpMungingForSimulcast() || !newTrack\n ? Promise.resolve()\n : this.tpcUtils.setEncodings(newTrack);\n\n // Renegotiate only in the case of P2P. We rely on 'negotiationeeded' to be fired for JVB.\n return configureEncodingsPromise.then(() => this.isP2P || negotiationNeeded);\n });\n }\n\n logger.debug(`${this} TPC.replaceTrack using plan B`);\n\n let promiseChain = Promise.resolve();\n\n if (oldTrack) {\n this.removeTrack(oldTrack);\n }\n if (newTrack) {\n promiseChain = this.addTrack(newTrack);\n }\n\n return promiseChain.then(() => true);\n};\n\n/**\n * Removes local track as part of the mute operation.\n * @param {JitsiLocalTrack} localTrack the local track to be remove as part of\n * the mute operation.\n * @return {Promise} Promise that resolves to true if the underlying PeerConnection's\n * state has changed and renegotiation is required, false if no renegotiation is needed or\n * Promise is rejected when something goes wrong.\n */\nTraceablePeerConnection.prototype.removeTrackMute = function(localTrack) {\n const webRtcStream = localTrack.getOriginalStream();\n\n this.trace(\n 'removeStreamMute',\n localTrack.rtcId, webRtcStream ? webRtcStream.id : null);\n\n if (!this._assertTrackBelongs('removeStreamMute', localTrack)) {\n // Abort - nothing to be done here\n return Promise.reject('Track not found in the peerconnection');\n }\n\n if (this._usesUnifiedPlan) {\n return this.tpcUtils.replaceTrack(localTrack, null);\n }\n\n if (webRtcStream) {\n logger.info(`${this} Removing track=${localTrack} as mute`);\n this._removeStream(webRtcStream);\n\n return Promise.resolve(true);\n }\n\n logger.error(`${this} removeStreamMute - no WebRTC stream for track=${localTrack}`);\n\n return Promise.reject('Stream not found');\n};\n\nTraceablePeerConnection.prototype.createDataChannel = function(label, opts) {\n this.trace('createDataChannel', label, opts);\n\n return this.peerconnection.createDataChannel(label, opts);\n};\n\n/**\n * Ensures that the simulcast ssrc-group appears after any other ssrc-groups\n * in the SDP so that simulcast is properly activated.\n *\n * @param {Object} localSdp the WebRTC session description instance for\n * the local description.\n * @private\n */\nTraceablePeerConnection.prototype._ensureSimulcastGroupIsLast = function(localSdp) {\n let sdpStr = localSdp.sdp;\n\n const videoStartIndex = sdpStr.indexOf('m=video');\n const simStartIndex = sdpStr.indexOf('a=ssrc-group:SIM', videoStartIndex);\n let otherStartIndex = sdpStr.lastIndexOf('a=ssrc-group');\n\n if (simStartIndex === -1\n || otherStartIndex === -1\n || otherStartIndex === simStartIndex) {\n return localSdp;\n }\n\n const simEndIndex = sdpStr.indexOf('\\r\\n', simStartIndex);\n const simStr = sdpStr.substring(simStartIndex, simEndIndex + 2);\n\n sdpStr = sdpStr.replace(simStr, '');\n otherStartIndex = sdpStr.lastIndexOf('a=ssrc-group');\n const otherEndIndex = sdpStr.indexOf('\\r\\n', otherStartIndex);\n const sdpHead = sdpStr.slice(0, otherEndIndex);\n const simStrTrimmed = simStr.trim();\n const sdpTail = sdpStr.slice(otherEndIndex);\n\n sdpStr = `${sdpHead}\\r\\n${simStrTrimmed}${sdpTail}`;\n\n return new RTCSessionDescription({\n type: localSdp.type,\n sdp: sdpStr\n });\n};\n\n/**\n * Will adjust audio and video media direction in the given SDP object to\n * reflect the current status of the {@link audioTransferActive} and\n * {@link videoTransferActive} flags.\n * @param {RTCSessionDescription} localDescription the WebRTC session description instance for\n * the local description.\n * @private\n */\nTraceablePeerConnection.prototype._adjustLocalMediaDirection = function(localDescription) {\n const transformer = new SdpTransformWrap(localDescription.sdp);\n let modifiedDirection = false;\n const audioMedia = transformer.selectMedia(MediaType.AUDIO)?.[0];\n\n if (audioMedia) {\n const desiredAudioDirection = this.getDesiredMediaDirection(MediaType.AUDIO);\n\n if (audioMedia.direction !== desiredAudioDirection) {\n audioMedia.direction = desiredAudioDirection;\n logger.info(`${this} Adjusted local audio direction to ${desiredAudioDirection}`);\n modifiedDirection = true;\n }\n } else {\n logger.warn(`${this} No \"audio\" media found in the local description`);\n }\n\n const videoMedia = transformer.selectMedia(MediaType.VIDEO)?.[0];\n\n if (videoMedia) {\n const desiredVideoDirection = this.getDesiredMediaDirection(MediaType.VIDEO);\n\n if (videoMedia.direction !== desiredVideoDirection) {\n videoMedia.direction = desiredVideoDirection;\n logger.info(`${this} Adjusted local video direction to ${desiredVideoDirection}`);\n modifiedDirection = true;\n }\n } else {\n logger.warn(`${this} No \"video\" media found in the local description`);\n }\n\n if (modifiedDirection) {\n return new RTCSessionDescription({\n type: localDescription.type,\n sdp: transformer.toRawSDP()\n });\n }\n\n return localDescription;\n};\n\n/**\n * Adjusts the media direction on the remote description based on availability of local and remote sources in a p2p\n * media connection.\n *\n * @param {RTCSessionDescription} remoteDescription the WebRTC session description instance for the remote description.\n * @returns the transformed remoteDescription.\n * @private\n */\nTraceablePeerConnection.prototype._adjustRemoteMediaDirection = function(remoteDescription) {\n const transformer = new SdpTransformWrap(remoteDescription.sdp);\n\n [ MediaType.AUDIO, MediaType.VIDEO ].forEach(mediaType => {\n const media = transformer.selectMedia(mediaType)?.[0];\n const hasLocalSource = this.hasAnyTracksOfType(mediaType);\n const hasRemoteSource = this.getRemoteTracks(null, mediaType).length > 0;\n\n media.direction = hasLocalSource && hasRemoteSource\n ? MediaDirection.SENDRECV\n : hasLocalSource\n ? MediaDirection.RECVONLY\n : hasRemoteSource ? MediaDirection.SENDONLY : MediaDirection.INACTIVE;\n });\n\n return new RTCSessionDescription({\n type: remoteDescription.type,\n sdp: transformer.toRawSDP()\n });\n};\n\n/**\n * Munges the stereo flag as well as the opusMaxAverageBitrate in the SDP, based\n * on values set through config.js, if present.\n *\n * @param {RTCSessionDescription} description that needs to be munged.\n * @returns {RTCSessionDescription} the munged description.\n */\nTraceablePeerConnection.prototype._mungeOpus = function(description) {\n const { audioQuality } = this.options;\n\n if (!audioQuality?.stereo && !audioQuality?.opusMaxAverageBitrate) {\n return description;\n }\n\n const parsedSdp = transform.parse(description.sdp);\n const mLines = parsedSdp.media;\n\n for (const mLine of mLines) {\n if (mLine.type === 'audio') {\n const { payload } = mLine.rtp.find(protocol => protocol.codec === CodecMimeType.OPUS);\n\n if (!payload) {\n // eslint-disable-next-line no-continue\n continue;\n }\n\n let fmtpOpus = mLine.fmtp.find(protocol => protocol.payload === payload);\n\n if (!fmtpOpus) {\n fmtpOpus = {\n payload,\n config: ''\n };\n }\n\n const fmtpConfig = transform.parseParams(fmtpOpus.config);\n let sdpChanged = false;\n\n if (audioQuality?.stereo) {\n fmtpConfig.stereo = 1;\n sdpChanged = true;\n }\n\n if (audioQuality?.opusMaxAverageBitrate) {\n fmtpConfig.maxaveragebitrate = audioQuality.opusMaxAverageBitrate;\n sdpChanged = true;\n }\n\n if (!sdpChanged) {\n // eslint-disable-next-line no-continue\n continue;\n }\n\n let mungedConfig = '';\n\n for (const key of Object.keys(fmtpConfig)) {\n mungedConfig += `${key}=${fmtpConfig[key]}; `;\n }\n\n fmtpOpus.config = mungedConfig.trim();\n }\n }\n\n return new RTCSessionDescription({\n type: description.type,\n sdp: transform.write(parsedSdp)\n });\n};\n\n/**\n * Sets up the _dtlsTransport object and initializes callbacks for it.\n */\nTraceablePeerConnection.prototype._initializeDtlsTransport = function() {\n // We are assuming here that we only have one bundled transport here\n if (!this.peerconnection.getSenders || this._dtlsTransport) {\n return;\n }\n\n const senders = this.peerconnection.getSenders();\n\n if (senders.length !== 0 && senders[0].transport) {\n this._dtlsTransport = senders[0].transport;\n\n this._dtlsTransport.onerror = error => {\n logger.error(`${this} DtlsTransport error: ${error}`);\n };\n\n this._dtlsTransport.onstatechange = () => {\n this.trace('dtlsTransport.onstatechange', this._dtlsTransport.state);\n };\n }\n};\n\n\n/**\n * Configures the stream encodings depending on the video type and the bitrates configured.\n *\n * @param {JitsiLocalTrack} - The local track for which the sender encodings have to configured.\n * @returns {Promise} promise that will be resolved when the operation is successful and rejected otherwise.\n */\nTraceablePeerConnection.prototype.configureSenderVideoEncodings = function(localVideoTrack = null) {\n if (FeatureFlags.isSourceNameSignalingEnabled()) {\n if (localVideoTrack) {\n return this.setSenderVideoConstraints(\n this._senderMaxHeights.get(localVideoTrack.getSourceName()),\n localVideoTrack);\n }\n const promises = [];\n\n for (const track of this.getLocalVideoTracks()) {\n promises.push(this.setSenderVideoConstraints(this._senderMaxHeights.get(track.getSourceName()), track));\n }\n\n return Promise.allSettled(promises);\n }\n\n let localTrack = localVideoTrack;\n\n if (!localTrack) {\n localTrack = this.getLocalVideoTracks()[0];\n }\n\n return this.setSenderVideoConstraints(this._senderVideoMaxHeight, localTrack);\n};\n\nTraceablePeerConnection.prototype.setLocalDescription = function(description) {\n let localDescription = description;\n\n this.trace('setLocalDescription::preTransform', dumpSDP(localDescription));\n\n // Munge stereo flag and opusMaxAverageBitrate based on config.js\n localDescription = this._mungeOpus(localDescription);\n\n if (!this._usesUnifiedPlan) {\n localDescription = this._adjustLocalMediaDirection(localDescription);\n localDescription = this._ensureSimulcastGroupIsLast(localDescription);\n }\n\n // Munge the order of the codecs based on the preferences set through config.js if we are using SDP munging.\n if (!this._usesTransceiverCodecPreferences) {\n localDescription = this._mungeCodecOrder(localDescription);\n }\n\n this.trace('setLocalDescription::postTransform', dumpSDP(localDescription));\n\n return new Promise((resolve, reject) => {\n this.peerconnection.setLocalDescription(localDescription)\n .then(() => {\n this.trace('setLocalDescriptionOnSuccess');\n const localUfrag = SDPUtil.getUfrag(localDescription.sdp);\n\n if (localUfrag !== this.localUfrag) {\n this.localUfrag = localUfrag;\n this.eventEmitter.emit(RTCEvents.LOCAL_UFRAG_CHANGED, this, localUfrag);\n }\n\n this._initializeDtlsTransport();\n\n resolve();\n }, err => {\n this.trace('setLocalDescriptionOnFailure', err);\n this.eventEmitter.emit(RTCEvents.SET_LOCAL_DESCRIPTION_FAILED, err, this);\n reject(err);\n });\n });\n};\n\n/**\n * Enables/disables audio media transmission on this peer connection. When\n * disabled the SDP audio media direction in the local SDP will be adjusted to\n * 'inactive' which means that no data will be sent nor accepted, but\n * the connection should be kept alive.\n * @param {boolean} active true to enable audio media transmission or\n * false to disable. If the value is not a boolean the call will have\n * no effect.\n * @return {boolean} true if the value has changed and sRD/sLD cycle\n * needs to be executed in order for the changes to take effect or\n * false if the given value was the same as the previous one.\n * @public\n */\nTraceablePeerConnection.prototype.setAudioTransferActive = function(active) {\n logger.debug(`${this} audio transfer active: ${active}`);\n const changed = this.audioTransferActive !== active;\n\n this.audioTransferActive = active;\n\n if (this._usesUnifiedPlan) {\n this.tpcUtils.setAudioTransferActive(active);\n\n // false means no renegotiation up the chain which is not needed in the Unified mode\n return false;\n }\n\n return changed;\n};\n\nTraceablePeerConnection.prototype.setRemoteDescription = function(description) {\n let remoteDescription = description;\n\n this.trace('setRemoteDescription::preTransform', dumpSDP(description));\n\n // Munge stereo flag and opusMaxAverageBitrate based on config.js\n remoteDescription = this._mungeOpus(remoteDescription);\n\n if (this._usesUnifiedPlan) {\n // Translate the SDP to Unified plan format first for the jvb case, p2p case will only have 2 m-lines.\n if (!this.isP2P) {\n const currentDescription = this.peerconnection.remoteDescription;\n\n remoteDescription = this.interop.toUnifiedPlan(remoteDescription, currentDescription);\n this.trace('setRemoteDescription::postTransform (Unified)', dumpSDP(remoteDescription));\n }\n if (this.isSimulcastOn()) {\n // Implode the simulcast ssrcs so that the remote sdp has only the first ssrc in the SIM group.\n remoteDescription = this.simulcast.mungeRemoteDescription(remoteDescription);\n this.trace('setRemoteDescription::postTransform (simulcast)', dumpSDP(remoteDescription));\n\n remoteDescription = this.tpcUtils.insertUnifiedPlanSimulcastReceive(remoteDescription);\n this.trace('setRemoteDescription::postTransform (sim receive)', dumpSDP(remoteDescription));\n }\n remoteDescription = this.tpcUtils.ensureCorrectOrderOfSsrcs(remoteDescription);\n this.trace('setRemoteDescription::postTransform (correct ssrc order)', dumpSDP(remoteDescription));\n } else {\n if (this.isSimulcastOn()) {\n // Implode the simulcast ssrcs so that the remote sdp has only the first ssrc in the SIM group.\n remoteDescription = this.simulcast.mungeRemoteDescription(\n remoteDescription,\n true /* add x-google-conference flag */);\n this.trace('setRemoteDescription::postTransform (simulcast)', dumpSDP(remoteDescription));\n }\n remoteDescription = normalizePlanB(remoteDescription);\n }\n\n // Munge the order of the codecs based on the preferences set through config.js.\n remoteDescription = this._mungeCodecOrder(remoteDescription);\n this.trace('setRemoteDescription::postTransform (munge codec order)', dumpSDP(remoteDescription));\n\n return new Promise((resolve, reject) => {\n this.peerconnection.setRemoteDescription(remoteDescription)\n .then(() => {\n this.trace('setRemoteDescriptionOnSuccess');\n const remoteUfrag = SDPUtil.getUfrag(remoteDescription.sdp);\n\n if (remoteUfrag !== this.remoteUfrag) {\n this.remoteUfrag = remoteUfrag;\n this.eventEmitter.emit(RTCEvents.REMOTE_UFRAG_CHANGED, this, remoteUfrag);\n }\n\n this._initializeDtlsTransport();\n\n resolve();\n }, err => {\n this.trace('setRemoteDescriptionOnFailure', err);\n this.eventEmitter.emit(RTCEvents.SET_REMOTE_DESCRIPTION_FAILED, err, this);\n reject(err);\n });\n });\n};\n\n/**\n * Changes the resolution of the video stream that is sent to the peer based on the resolution requested by the peer\n * and user preference, sets the degradation preference on the sender based on the video type, configures the maximum\n * bitrates on the send stream.\n *\n * @param {number} frameHeight - The max frame height to be imposed on the outgoing video stream.\n * @param {JitsiLocalTrack} - The local track for which the sender constraints have to be applied.\n * @returns {Promise} promise that will be resolved when the operation is successful and rejected otherwise.\n */\nTraceablePeerConnection.prototype.setSenderVideoConstraints = function(frameHeight, localVideoTrack) {\n if (frameHeight < 0) {\n throw new Error(`Invalid frameHeight: ${frameHeight}`);\n }\n\n // XXX: This is not yet supported on mobile.\n if (browser.isReactNative()) {\n return Promise.resolve();\n }\n\n if (FeatureFlags.isSourceNameSignalingEnabled()) {\n this._senderMaxHeights.set(localVideoTrack.getSourceName(), frameHeight);\n } else {\n this._senderVideoMaxHeight = frameHeight;\n }\n\n if (!localVideoTrack || localVideoTrack.isMuted()) {\n return Promise.resolve();\n }\n const videoSender = this.findSenderForTrack(localVideoTrack.getTrack());\n\n if (!videoSender) {\n return Promise.resolve();\n }\n const parameters = videoSender.getParameters();\n\n if (!parameters?.encodings?.length) {\n return Promise.resolve();\n }\n\n // Set the degradation preference.\n const preference = this.isSharingLowFpsScreen()\n ? DEGRADATION_PREFERENCE_DESKTOP // Prefer resolution for low fps share.\n : DEGRADATION_PREFERENCE_CAMERA; // Prefer frame-rate for high fps share and camera.\n\n parameters.degradationPreference = preference;\n logger.info(`${this} Setting degradation preference [preference=${preference},track=${localVideoTrack}`);\n\n // Calculate the encodings active state based on the resolution requested by the bridge.\n this.encodingsEnabledState = this.tpcUtils.calculateEncodingsActiveState(localVideoTrack, frameHeight);\n const maxBitrates = this.tpcUtils.calculateEncodingsBitrates(localVideoTrack);\n const videoType = localVideoTrack.getVideoType();\n\n if (this.isSimulcastOn()) {\n for (const encoding in parameters.encodings) {\n if (parameters.encodings.hasOwnProperty(encoding)) {\n parameters.encodings[encoding].active = this.encodingsEnabledState[encoding];\n\n // Firefox doesn't follow the spec and lets application specify the degradation preference on the\n // encodings.\n browser.isFirefox() && (parameters.encodings[encoding].degradationPreference = preference);\n\n // Max bitrates are configured on the encodings only for VP8.\n if (this.getConfiguredVideoCodec() === CodecMimeType.VP8\n && (this.options?.videoQuality?.maxBitratesVideo\n || this.isSharingLowFpsScreen()\n || this._usesUnifiedPlan)) {\n parameters.encodings[encoding].maxBitrate = maxBitrates[encoding];\n }\n }\n }\n this.tpcUtils.updateEncodingsResolution(parameters);\n\n // For p2p and cases and where simulcast is explicitly disabled.\n } else if (frameHeight > 0) {\n let scaleFactor = HD_SCALE_FACTOR;\n\n // Do not scale down encodings for desktop tracks for non-simulcast case.\n if (videoType === VideoType.CAMERA && localVideoTrack.resolution > frameHeight) {\n scaleFactor = Math.floor(localVideoTrack.resolution / frameHeight);\n }\n\n parameters.encodings[0].active = true;\n parameters.encodings[0].scaleResolutionDownBy = scaleFactor;\n\n // Firefox doesn't follow the spec and lets application specify the degradation preference on the encodings.\n browser.isFirefox() && (parameters.encodings[0].degradationPreference = preference);\n\n // Configure the bitrate.\n if (this.getConfiguredVideoCodec() === CodecMimeType.VP8 && this.options?.videoQuality?.maxBitratesVideo) {\n let bitrate = this.getTargetVideoBitrates()?.high;\n\n if (videoType === VideoType.CAMERA) {\n bitrate = this.tpcUtils.localStreamEncodingsConfig\n .find(layer => layer.scaleResolutionDownBy === scaleFactor)?.maxBitrate ?? bitrate;\n }\n parameters.encodings[0].maxBitrate = bitrate;\n }\n } else {\n parameters.encodings[0].active = false;\n }\n\n logger.info(`${this} setting max height=${frameHeight},encodings=${JSON.stringify(parameters.encodings)}`);\n\n return videoSender.setParameters(parameters).then(() => {\n localVideoTrack.maxEnabledResolution = frameHeight;\n this.eventEmitter.emit(RTCEvents.LOCAL_TRACK_MAX_ENABLED_RESOLUTION_CHANGED, localVideoTrack);\n });\n};\n\n/**\n * Enables/disables video media transmission on this peer connection. When\n * disabled the SDP video media direction in the local SDP will be adjusted to\n * 'inactive' which means that no data will be sent nor accepted, but\n * the connection should be kept alive.\n * @param {boolean} active true to enable video media transmission or\n * false to disable. If the value is not a boolean the call will have\n * no effect.\n * @return {boolean} true if the value has changed and sRD/sLD cycle\n * needs to be executed in order for the changes to take effect or\n * false if the given value was the same as the previous one.\n * @public\n */\nTraceablePeerConnection.prototype.setVideoTransferActive = function(active) {\n logger.debug(`${this} video transfer active: ${active}`);\n const changed = this.videoTransferActive !== active;\n\n this.videoTransferActive = active;\n\n if (this._usesUnifiedPlan) {\n this.tpcUtils.setVideoTransferActive(active);\n\n // false means no renegotiation up the chain which is not needed in the Unified mode\n return false;\n }\n\n return changed;\n};\n\n/**\n * Sends DTMF tones if possible.\n *\n * @param {string} tones - The DTMF tones string as defined by {@code RTCDTMFSender.insertDTMF}, 'tones' argument.\n * @param {number} duration - The amount of time in milliseconds that each DTMF should last. It's 200ms by default.\n * @param {number} interToneGap - The length of time in miliseconds to wait between tones. It's 200ms by default.\n *\n * @returns {void}\n */\nTraceablePeerConnection.prototype.sendTones = function(tones, duration = 200, interToneGap = 200) {\n if (!this._dtmfSender) {\n if (this.peerconnection.getSenders) {\n const rtpSender = this.peerconnection.getSenders().find(s => s.dtmf);\n\n this._dtmfSender = rtpSender && rtpSender.dtmf;\n this._dtmfSender && logger.info(`${this} initialized DTMFSender using getSenders`);\n }\n\n if (!this._dtmfSender) {\n const localAudioTrack = Array.from(this.localTracks.values()).find(t => t.isAudioTrack());\n\n if (this.peerconnection.createDTMFSender && localAudioTrack) {\n this._dtmfSender = this.peerconnection.createDTMFSender(localAudioTrack.getTrack());\n }\n this._dtmfSender && logger.info(`${this} initialized DTMFSender using deprecated createDTMFSender`);\n }\n\n if (this._dtmfSender) {\n this._dtmfSender.ontonechange = this._onToneChange.bind(this);\n }\n }\n\n if (this._dtmfSender) {\n if (this._dtmfSender.toneBuffer) {\n this._dtmfTonesQueue.push({\n tones,\n duration,\n interToneGap\n });\n\n return;\n }\n\n this._dtmfSender.insertDTMF(tones, duration, interToneGap);\n } else {\n logger.warn(`${this} sendTones - failed to select DTMFSender`);\n }\n};\n\n/**\n * Callback ivoked by {@code this._dtmfSender} when it has finished playing\n * a single tone.\n *\n * @param {Object} event - The tonechange event which indicates what characters\n * are left to be played for the current tone.\n * @private\n * @returns {void}\n */\nTraceablePeerConnection.prototype._onToneChange = function(event) {\n // An empty event.tone indicates the current tones have finished playing.\n // Automatically start playing any queued tones on finish.\n if (this._dtmfSender && event.tone === '' && this._dtmfTonesQueue.length) {\n const { tones, duration, interToneGap } = this._dtmfTonesQueue.shift();\n\n this._dtmfSender.insertDTMF(tones, duration, interToneGap);\n }\n};\n\n/**\n * Makes the underlying TraceablePeerConnection generate new SSRC for\n * the recvonly video stream.\n */\nTraceablePeerConnection.prototype.generateRecvonlySsrc = function() {\n const newSSRC = SDPUtil.generateSsrc();\n\n logger.info(`${this} generated new recvonly SSRC=${newSSRC}`);\n this.sdpConsistency.setPrimarySsrc(newSSRC);\n};\n\n/**\n * Makes the underlying TraceablePeerConnection forget the current primary video\n * SSRC.\n */\nTraceablePeerConnection.prototype.clearRecvonlySsrc = function() {\n logger.info(`${this} Clearing primary video SSRC!`);\n this.sdpConsistency.clearVideoSsrcCache();\n};\n\n/**\n * Closes underlying WebRTC PeerConnection instance and removes all remote\n * tracks by emitting {@link RTCEvents.REMOTE_TRACK_REMOVED} for each one of\n * them.\n */\nTraceablePeerConnection.prototype.close = function() {\n this.trace('stop');\n\n // Off SignalingEvents\n this.signalingLayer.off(SignalingEvents.PEER_MUTED_CHANGED, this._peerMutedChanged);\n this.signalingLayer.off(SignalingEvents.PEER_VIDEO_TYPE_CHANGED, this._peerVideoTypeChanged);\n this._usesUnifiedPlan && this.peerconnection.removeEventListener('track', this.onTrack);\n\n for (const peerTracks of this.remoteTracks.values()) {\n for (const remoteTracks of peerTracks.values()) {\n for (const remoteTrack of remoteTracks) {\n this._removeRemoteTrack(remoteTrack);\n }\n }\n }\n this.remoteTracks.clear();\n\n this._addedStreams = [];\n\n this._dtmfSender = null;\n this._dtmfTonesQueue = [];\n\n if (!this.rtc._removePeerConnection(this)) {\n logger.error(`${this} RTC._removePeerConnection returned false`);\n }\n if (this.statsinterval !== null) {\n window.clearInterval(this.statsinterval);\n this.statsinterval = null;\n }\n logger.info(`${this} Closing peerconnection`);\n this.peerconnection.close();\n};\n\nTraceablePeerConnection.prototype.createAnswer = function(constraints) {\n return this._createOfferOrAnswer(false /* answer */, constraints);\n};\n\nTraceablePeerConnection.prototype.createOffer = function(constraints) {\n return this._createOfferOrAnswer(true /* offer */, constraints);\n};\n\nTraceablePeerConnection.prototype._createOfferOrAnswer = function(\n isOffer,\n constraints) {\n const logName = isOffer ? 'Offer' : 'Answer';\n\n this.trace(`create${logName}`, JSON.stringify(constraints, null, ' '));\n\n const handleSuccess = (resultSdp, resolveFn, rejectFn) => {\n try {\n this.trace(\n `create${logName}OnSuccess::preTransform`, dumpSDP(resultSdp));\n\n if (!this._usesUnifiedPlan) {\n // If there are no local video tracks, then a \"recvonly\"\n // SSRC needs to be generated\n if (!this.hasAnyTracksOfType(MediaType.VIDEO)\n && !this.sdpConsistency.hasPrimarySsrcCached()) {\n this.generateRecvonlySsrc();\n }\n\n // eslint-disable-next-line no-param-reassign\n resultSdp = new RTCSessionDescription({\n type: resultSdp.type,\n sdp: this.sdpConsistency.makeVideoPrimarySsrcsConsistent(\n resultSdp.sdp)\n });\n\n this.trace(\n `create${logName}OnSuccess::postTransform `\n + '(make primary audio/video ssrcs consistent)',\n dumpSDP(resultSdp));\n }\n\n const localVideoTrack = this.getLocalVideoTracks()[0];\n\n // Configure simulcast for camera tracks and for desktop tracks that need simulcast.\n if (this.isSimulcastOn() && browser.usesSdpMungingForSimulcast()\n && (localVideoTrack?.getVideoType() === VideoType.CAMERA\n || this._usesUnifiedPlan\n || !this.isSharingLowFpsScreen())) {\n // eslint-disable-next-line no-param-reassign\n resultSdp = this.simulcast.mungeLocalDescription(resultSdp);\n this.trace(`create${logName} OnSuccess::postTransform (simulcast)`, dumpSDP(resultSdp));\n }\n\n if (!this.options.disableRtx && browser.usesSdpMungingForSimulcast()) {\n // eslint-disable-next-line no-param-reassign\n resultSdp = new RTCSessionDescription({\n type: resultSdp.type,\n sdp: this.rtxModifier.modifyRtxSsrcs(resultSdp.sdp)\n });\n\n this.trace(\n `create${logName}`\n + 'OnSuccess::postTransform (rtx modifier)',\n dumpSDP(resultSdp));\n }\n\n const ssrcMap = this._extractSSRCMap(resultSdp);\n\n this._processLocalSSRCsMap(ssrcMap);\n\n resolveFn(resultSdp);\n } catch (e) {\n this.trace(`create${logName}OnError`, e);\n this.trace(`create${logName}OnError`, dumpSDP(resultSdp));\n logger.error(`${this} create${logName}OnError`, e, dumpSDP(resultSdp));\n\n rejectFn(e);\n }\n };\n\n const handleFailure = (err, rejectFn) => {\n this.trace(`create${logName}OnFailure`, err);\n const eventType\n = isOffer\n ? RTCEvents.CREATE_OFFER_FAILED\n : RTCEvents.CREATE_ANSWER_FAILED;\n\n this.eventEmitter.emit(eventType, err, this);\n\n rejectFn(err);\n };\n\n // Set the codec preference before creating an offer or answer so that the generated SDP will have\n // the correct preference order.\n if (this._usesTransceiverCodecPreferences) {\n const transceiver = this.peerconnection.getTransceivers()\n .find(t => t.receiver && t.receiver?.track?.kind === MediaType.VIDEO);\n\n if (transceiver) {\n let capabilities = RTCRtpReceiver.getCapabilities(MediaType.VIDEO)?.codecs;\n const mimeType = this.codecPreference?.mimeType;\n const enable = this.codecPreference?.enable;\n\n if (capabilities && mimeType && enable) {\n // Move the desired codec (all variations of it as well) to the beginning of the list.\n /* eslint-disable-next-line arrow-body-style */\n capabilities.sort(caps => {\n return caps.mimeType.toLowerCase() === `${MediaType.VIDEO}/${mimeType}` ? -1 : 1;\n });\n } else if (capabilities && mimeType) {\n capabilities = capabilities\n .filter(caps => caps.mimeType.toLowerCase() !== `${MediaType.VIDEO}/${mimeType}`);\n }\n\n // Disable ulpfec on Google Chrome and derivatives because\n // https://bugs.chromium.org/p/chromium/issues/detail?id=1276427\n if (browser.isChromiumBased()) {\n capabilities = capabilities\n .filter(caps => caps.mimeType.toLowerCase() !== `${MediaType.VIDEO}/${CodecMimeType.ULPFEC}`);\n }\n\n try {\n transceiver.setCodecPreferences(capabilities);\n } catch (err) {\n logger.warn(`${this} Setting codec[preference=${mimeType},enable=${enable}] failed`, err);\n }\n }\n }\n\n return new Promise((resolve, reject) => {\n let oaPromise;\n\n if (isOffer) {\n oaPromise = this.peerconnection.createOffer(constraints);\n } else {\n oaPromise = this.peerconnection.createAnswer(constraints);\n }\n\n oaPromise\n .then(\n sdp => handleSuccess(sdp, resolve, reject),\n error => handleFailure(error, reject));\n });\n};\n\n/**\n * Extract primary SSRC from given {@link TrackSSRCInfo} object.\n * @param {TrackSSRCInfo} ssrcObj\n * @return {number|null} the primary SSRC or null\n */\nTraceablePeerConnection.prototype._extractPrimarySSRC = function(ssrcObj) {\n if (ssrcObj && ssrcObj.groups && ssrcObj.groups.length) {\n return ssrcObj.groups[0].ssrcs[0];\n } else if (ssrcObj && ssrcObj.ssrcs && ssrcObj.ssrcs.length) {\n return ssrcObj.ssrcs[0];\n }\n\n return null;\n};\n\n/**\n * Goes over the SSRC map extracted from the latest local description and tries\n * to match them with the local tracks (by MSID). Will update the values\n * currently stored in the {@link TraceablePeerConnection.localSSRCs} map.\n * @param {Map} ssrcMap\n * @private\n */\nTraceablePeerConnection.prototype._processLocalSSRCsMap = function(ssrcMap) {\n for (const track of this.localTracks.values()) {\n const sourceIdentifier = this._usesUnifiedPlan ? track.getType() : track.storedMSID;\n\n if (ssrcMap.has(sourceIdentifier)) {\n const newSSRC = ssrcMap.get(sourceIdentifier);\n\n if (!newSSRC) {\n logger.error(`${this} No SSRC found for stream=${sourceIdentifier}`);\n\n return;\n }\n const oldSSRC = this.localSSRCs.get(track.rtcId);\n const newSSRCNum = this._extractPrimarySSRC(newSSRC);\n const oldSSRCNum = this._extractPrimarySSRC(oldSSRC);\n\n // eslint-disable-next-line no-negated-condition\n if (newSSRCNum !== oldSSRCNum) {\n oldSSRCNum && logger.error(`${this} Overwriting SSRC for track=${track}] with ssrc=${newSSRC}`);\n this.localSSRCs.set(track.rtcId, newSSRC);\n this.eventEmitter.emit(RTCEvents.LOCAL_TRACK_SSRC_UPDATED, track, newSSRCNum);\n }\n } else if (!track.isVideoTrack() && !track.isMuted()) {\n // It is normal to find no SSRCs for a muted video track in\n // the local SDP as the recv-only SSRC is no longer munged in.\n // So log the warning only if it's not a muted video track.\n logger.warn(`${this} No SSRCs found in the local SDP for track=${track}, stream=${sourceIdentifier}`);\n }\n }\n};\n\nTraceablePeerConnection.prototype.addIceCandidate = function(candidate) {\n this.trace('addIceCandidate', JSON.stringify({\n candidate: candidate.candidate,\n sdpMid: candidate.sdpMid,\n sdpMLineIndex: candidate.sdpMLineIndex,\n usernameFragment: candidate.usernameFragment\n }, null, ' '));\n\n return this.peerconnection.addIceCandidate(candidate);\n};\n\n/**\n * Returns the number of simulcast streams that are currently enabled on the peerconnection.\n *\n * @returns {number} The number of simulcast streams currently enabled or 1 when simulcast is disabled.\n */\nTraceablePeerConnection.prototype.getActiveSimulcastStreams = function() {\n let activeStreams = 1;\n\n if (this.isSimulcastOn() && this.encodingsEnabledState) {\n activeStreams = this.encodingsEnabledState.filter(stream => Boolean(stream))?.length;\n } else if (this.isSimulcastOn()) {\n activeStreams = SIM_LAYER_RIDS.length;\n }\n\n return activeStreams;\n};\n\n/**\n * Obtains call-related stats from the peer connection.\n *\n * @returns {Promise} Promise which resolves with data providing statistics about\n * the peerconnection.\n */\nTraceablePeerConnection.prototype.getStats = function() {\n return this.peerconnection.getStats();\n};\n\n/**\n * Generates and stores new SSRC info object for given local track.\n * The method should be called only for a video track being added to this TPC\n * in the muted state (given that the current browser uses this strategy).\n * @param {JitsiLocalTrack} track\n * @return {TPCSSRCInfo}\n */\nTraceablePeerConnection.prototype.generateNewStreamSSRCInfo = function(track) {\n const rtcId = track.rtcId;\n let ssrcInfo = this._getSSRC(rtcId);\n\n if (ssrcInfo) {\n logger.error(`${this} Overwriting local SSRCs for track id=${rtcId}`);\n }\n\n // Configure simulcast for camera tracks and desktop tracks that need simulcast.\n if (this.isSimulcastOn()\n && (track.getVideoType() === VideoType.CAMERA || !this.isSharingLowFpsScreen())) {\n ssrcInfo = {\n ssrcs: [],\n groups: []\n };\n for (let i = 0; i < SIM_LAYER_RIDS.length; i++) {\n ssrcInfo.ssrcs.push(SDPUtil.generateSsrc());\n }\n ssrcInfo.groups.push({\n ssrcs: ssrcInfo.ssrcs.slice(),\n semantics: 'SIM'\n });\n } else {\n ssrcInfo = {\n ssrcs: [ SDPUtil.generateSsrc() ],\n groups: []\n };\n }\n if (!this.options.disableRtx) {\n // Specifically use a for loop here because we'll\n // be adding to the list we're iterating over, so we\n // only want to iterate through the items originally\n // on the list\n const currNumSsrcs = ssrcInfo.ssrcs.length;\n\n for (let i = 0; i < currNumSsrcs; ++i) {\n const primarySsrc = ssrcInfo.ssrcs[i];\n const rtxSsrc = SDPUtil.generateSsrc();\n\n ssrcInfo.ssrcs.push(rtxSsrc);\n ssrcInfo.groups.push({\n ssrcs: [ primarySsrc, rtxSsrc ],\n semantics: 'FID'\n });\n }\n }\n ssrcInfo.msid = track.storedMSID;\n this.localSSRCs.set(rtcId, ssrcInfo);\n\n return ssrcInfo;\n};\n\n/**\n * Returns if the peer connection uses Unified plan implementation.\n *\n * @returns {boolean} True if the pc uses Unified plan, false otherwise.\n */\nTraceablePeerConnection.prototype.usesUnifiedPlan = function() {\n return this._usesUnifiedPlan;\n};\n\n/**\n * Creates a text representation of this TraceablePeerConnection\n * instance.\n * @return {string}\n */\nTraceablePeerConnection.prototype.toString = function() {\n return `TPC[id=${this.id},type=${this.isP2P ? 'P2P' : 'JVB'}]`;\n};\n","import { getLogger } from '@jitsi/logger';\n\nimport * as JitsiConferenceEvents from '../../JitsiConferenceEvents';\nimport BridgeVideoType from '../../service/RTC/BridgeVideoType';\nimport * as MediaType from '../../service/RTC/MediaType';\nimport RTCEvents from '../../service/RTC/RTCEvents';\nimport browser from '../browser';\nimport FeatureFlags from '../flags/FeatureFlags';\nimport Statistics from '../statistics/statistics';\nimport GlobalOnErrorHandler from '../util/GlobalOnErrorHandler';\nimport Listenable from '../util/Listenable';\nimport { safeCounterIncrement } from '../util/MathUtil';\n\nimport BridgeChannel from './BridgeChannel';\nimport JitsiLocalTrack from './JitsiLocalTrack';\nimport RTCUtils from './RTCUtils';\nimport TraceablePeerConnection from './TraceablePeerConnection';\n\n\nconst logger = getLogger(__filename);\n\n/**\n * The counter used to generated id numbers assigned to peer connections\n * @type {number}\n */\nlet peerConnectionIdCounter = 0;\n\n/**\n * The counter used to generate id number for the local\n * MediaStreamTracks.\n * @type {number}\n */\nlet rtcTrackIdCounter = 0;\n\n/**\n * Creates {@code JitsiLocalTrack} instances from the passed in meta information\n * about MedieaTracks.\n *\n * @param {Object[]} mediaStreamMetaData - An array of meta information with\n * MediaTrack instances. Each can look like:\n * {{\n * stream: MediaStream instance that holds a track with audio or video,\n * track: MediaTrack within the MediaStream,\n * videoType: \"camera\" or \"desktop\" or falsy,\n * sourceId: ID of the desktopsharing source,\n * sourceType: The desktopsharing source type,\n * effects: Array of effect types\n * }}\n */\nfunction _createLocalTracks(mediaStreamMetaData = []) {\n return mediaStreamMetaData.map(metaData => {\n const {\n sourceId,\n sourceType,\n stream,\n track,\n videoType,\n effects\n } = metaData;\n\n const { deviceId, facingMode } = track.getSettings();\n\n // FIXME Move rtcTrackIdCounter to a static method in JitsiLocalTrack\n // so RTC does not need to handle ID management. This move would be\n // safer to do once the old createLocalTracks is removed.\n rtcTrackIdCounter = safeCounterIncrement(rtcTrackIdCounter);\n\n return new JitsiLocalTrack({\n deviceId,\n facingMode,\n mediaType: track.kind,\n rtcId: rtcTrackIdCounter,\n sourceId,\n sourceType,\n stream,\n track,\n videoType: videoType || null,\n effects\n });\n });\n}\n\n/**\n *\n */\nexport default class RTC extends Listenable {\n /**\n *\n * @param conference\n * @param options\n */\n constructor(conference, options = {}) {\n super();\n this.conference = conference;\n\n /**\n * A map of active TraceablePeerConnection.\n * @type {Map.}\n */\n this.peerConnections = new Map();\n\n this.localTracks = [];\n\n this.options = options;\n\n // BridgeChannel instance.\n // @private\n // @type {BridgeChannel}\n this._channel = null;\n\n /**\n * The value specified to the last invocation of setLastN before the\n * channel completed opening. If non-null, the value will be sent\n * through a channel (once) as soon as it opens and will then be\n * discarded.\n * @private\n * @type {number}\n */\n this._lastN = undefined;\n\n /**\n * Defines the last N endpoints list. It can be null or an array once\n * initialised with a channel last N event.\n * @type {Array|null}\n * @private\n */\n this._lastNEndpoints = null;\n\n /**\n * Defines the forwarded sources list. It can be null or an array once initialised with a channel forwarded\n * sources event.\n *\n * @type {Array|null}\n * @private\n */\n this._forwardedSources = null;\n\n /**\n * The number representing the maximum video height the local client\n * should receive from the bridge.\n *\n * @type {number|undefined}\n * @private\n */\n this._maxFrameHeight = undefined;\n\n /**\n * The endpoint IDs of currently selected participants.\n *\n * @type {Array}\n * @private\n */\n this._selectedEndpoints = null;\n\n // The last N change listener.\n this._lastNChangeListener = this._onLastNChanged.bind(this);\n\n // The forwarded sources change listener.\n this._forwardedSourcesChangeListener = this._onForwardedSourcesChanged.bind(this);\n\n this._onDeviceListChanged = this._onDeviceListChanged.bind(this);\n this._updateAudioOutputForAudioTracks\n = this._updateAudioOutputForAudioTracks.bind(this);\n\n /**\n * The default video type assumed by the bridge.\n * @deprecated this will go away with multiple streams support\n * @type {BridgeVideoType}\n * @private\n */\n this._videoType = BridgeVideoType.NONE;\n\n // Switch audio output device on all remote audio tracks. Local audio\n // tracks handle this event by themselves.\n if (RTCUtils.isDeviceChangeAvailable('output')) {\n RTCUtils.addListener(\n RTCEvents.AUDIO_OUTPUT_DEVICE_CHANGED,\n this._updateAudioOutputForAudioTracks\n );\n\n RTCUtils.addListener(\n RTCEvents.DEVICE_LIST_CHANGED,\n this._onDeviceListChanged\n );\n }\n }\n\n /**\n * Removes any listeners and stored state from this {@code RTC} instance.\n *\n * @returns {void}\n */\n destroy() {\n RTCUtils.removeListener(RTCEvents.AUDIO_OUTPUT_DEVICE_CHANGED, this._updateAudioOutputForAudioTracks);\n RTCUtils.removeListener(RTCEvents.DEVICE_LIST_CHANGED, this._onDeviceListChanged);\n\n if (this._channelOpenListener) {\n this.removeListener(\n RTCEvents.DATA_CHANNEL_OPEN,\n this._channelOpenListener\n );\n }\n }\n\n /**\n * Exposes the private helper for converting a WebRTC MediaStream to a\n * JitsiLocalTrack.\n *\n * @param {Array} tracksInfo\n * @returns {Array}\n */\n static createLocalTracks(tracksInfo) {\n return _createLocalTracks(tracksInfo);\n }\n\n /**\n * Creates the local MediaStreams.\n * @param {object} [options] Optional parameters.\n * @param {array} options.devices The devices that will be requested.\n * @param {string} options.resolution Resolution constraints.\n * @param {string} options.cameraDeviceId\n * @param {string} options.micDeviceId\n * @returns {*} Promise object that will receive the new JitsiTracks\n */\n static obtainAudioAndVideoPermissions(options) {\n return RTCUtils.obtainAudioAndVideoPermissions(options)\n .then(tracksInfo => _createLocalTracks(tracksInfo));\n }\n\n /**\n * Initializes the bridge channel of this instance.\n * At least one of both, peerconnection or wsUrl parameters, must be\n * given.\n * @param {RTCPeerConnection} [peerconnection] WebRTC peer connection\n * instance.\n * @param {string} [wsUrl] WebSocket URL.\n */\n initializeBridgeChannel(peerconnection, wsUrl) {\n this._channel = new BridgeChannel(peerconnection, wsUrl, this.eventEmitter);\n\n this._channelOpenListener = () => {\n const logError = (error, msgType, value) => {\n GlobalOnErrorHandler.callErrorHandler(error);\n logger.error(`Cannot send ${msgType}(${JSON.stringify(value)}) endpoint message`, error);\n };\n\n // When the channel becomes available, tell the bridge about video selections so that it can do adaptive\n // simulcast, we want the notification to trigger even if userJid is undefined, or null.\n if (this._receiverVideoConstraints) {\n try {\n this._channel.sendNewReceiverVideoConstraintsMessage(this._receiverVideoConstraints);\n } catch (error) {\n logError(error, 'ReceiverVideoConstraints', this._receiverVideoConstraints);\n }\n }\n if (this._selectedEndpoints) {\n try {\n this._channel.sendSelectedEndpointsMessage(this._selectedEndpoints);\n } catch (error) {\n logError(error, 'SelectedEndpointsChangedEvent', this._selectedEndpoints);\n }\n }\n if (typeof this._maxFrameHeight !== 'undefined') {\n try {\n this._channel.sendReceiverVideoConstraintMessage(this._maxFrameHeight);\n } catch (error) {\n logError(error, 'ReceiverVideoConstraint', this._maxFrameHeight);\n }\n }\n if (typeof this._lastN !== 'undefined' && this._lastN !== -1) {\n try {\n this._channel.sendSetLastNMessage(this._lastN);\n } catch (error) {\n logError(error, 'LastNChangedEvent', this._lastN);\n }\n }\n if (!FeatureFlags.isSourceNameSignalingEnabled()) {\n try {\n this._channel.sendVideoTypeMessage(this._videoType);\n } catch (error) {\n logError(error, 'VideoTypeMessage', this._videoType);\n }\n }\n\n this.removeListener(RTCEvents.DATA_CHANNEL_OPEN, this._channelOpenListener);\n this._channelOpenListener = null;\n };\n this.addListener(RTCEvents.DATA_CHANNEL_OPEN, this._channelOpenListener);\n\n // Add Last N change listener.\n this.addListener(RTCEvents.LASTN_ENDPOINT_CHANGED, this._lastNChangeListener);\n\n if (FeatureFlags.isSourceNameSignalingEnabled()) {\n // Add forwarded sources change listener.\n this.addListener(RTCEvents.FORWARDED_SOURCES_CHANGED, this._forwardedSourcesChangeListener);\n }\n }\n\n /**\n * Callback invoked when the list of known audio and video devices has\n * been updated. Attempts to update the known available audio output\n * devices.\n *\n * @private\n * @returns {void}\n */\n _onDeviceListChanged() {\n this._updateAudioOutputForAudioTracks(RTCUtils.getAudioOutputDevice());\n }\n\n /**\n * Receives events when Last N had changed.\n * @param {array} lastNEndpoints The new Last N endpoints.\n * @private\n */\n _onLastNChanged(lastNEndpoints = []) {\n const oldLastNEndpoints = this._lastNEndpoints || [];\n let leavingLastNEndpoints = [];\n let enteringLastNEndpoints = [];\n\n this._lastNEndpoints = lastNEndpoints;\n\n leavingLastNEndpoints = oldLastNEndpoints.filter(\n id => !this.isInLastN(id));\n\n enteringLastNEndpoints = lastNEndpoints.filter(\n id => oldLastNEndpoints.indexOf(id) === -1);\n\n this.conference.eventEmitter.emit(\n JitsiConferenceEvents.LAST_N_ENDPOINTS_CHANGED,\n leavingLastNEndpoints,\n enteringLastNEndpoints);\n }\n\n /**\n * Receives events when forwarded sources had changed.\n *\n * @param {array} forwardedSources The new forwarded sources.\n * @private\n */\n _onForwardedSourcesChanged(forwardedSources = []) {\n const oldForwardedSources = this._forwardedSources || [];\n let leavingForwardedSources = [];\n let enteringForwardedSources = [];\n\n this._forwardedSources = forwardedSources;\n\n leavingForwardedSources = oldForwardedSources.filter(sourceName => !this.isInForwardedSources(sourceName));\n\n enteringForwardedSources = forwardedSources.filter(\n sourceName => oldForwardedSources.indexOf(sourceName) === -1);\n\n this.conference.eventEmitter.emit(\n JitsiConferenceEvents.FORWARDED_SOURCES_CHANGED,\n leavingForwardedSources,\n enteringForwardedSources,\n Date.now());\n }\n\n /**\n * Should be called when current media session ends and after the\n * PeerConnection has been closed using PeerConnection.close() method.\n */\n onCallEnded() {\n if (this._channel) {\n // The BridgeChannel is not explicitly closed as the PeerConnection\n // is closed on call ended which triggers datachannel onclose\n // events. If using a WebSocket, the channel must be closed since\n // it is not managed by the PeerConnection.\n // The reference is cleared to disable any logic related to the\n // channel.\n if (this._channel && this._channel.mode === 'websocket') {\n this._channel.close();\n }\n\n this._channel = null;\n }\n }\n\n /**\n * Sets the capture frame rate to be used for desktop tracks.\n *\n * @param {number} maxFps framerate to be used for desktop track capture.\n */\n setDesktopSharingFrameRate(maxFps) {\n RTCUtils.setDesktopSharingFrameRate(maxFps);\n }\n\n /**\n * Sets the receiver video constraints that determine how bitrate is allocated to each of the video streams\n * requested from the bridge. The constraints are cached and sent through the bridge channel once the channel\n * is established.\n * @param {*} constraints\n */\n setNewReceiverVideoConstraints(constraints) {\n this._receiverVideoConstraints = constraints;\n\n if (this._channel && this._channel.isOpen()) {\n this._channel.sendNewReceiverVideoConstraintsMessage(constraints);\n }\n }\n\n /**\n * Sets the maximum video size the local participant should receive from\n * remote participants. Will cache the value and send it through the channel\n * once it is created.\n *\n * @param {number} maxFrameHeightPixels the maximum frame height, in pixels,\n * this receiver is willing to receive.\n * @returns {void}\n */\n setReceiverVideoConstraint(maxFrameHeight) {\n this._maxFrameHeight = maxFrameHeight;\n\n if (this._channel && this._channel.isOpen()) {\n this._channel.sendReceiverVideoConstraintMessage(maxFrameHeight);\n }\n }\n\n /**\n * Sets the video type and availability for the local video source.\n *\n * @param {string} videoType 'camera' for camera, 'desktop' for screenshare and\n * 'none' for when local video source is muted or removed from the peerconnection.\n * @returns {void}\n */\n setVideoType(videoType) {\n if (this._videoType !== videoType) {\n this._videoType = videoType;\n\n if (this._channel && this._channel.isOpen()) {\n this._channel.sendVideoTypeMessage(videoType);\n }\n }\n }\n\n /**\n * Sends the track's video type to the JVB.\n * @param {SourceName} sourceName - the track's source name.\n * @param {BridgeVideoType} videoType - the track's video type.\n */\n sendSourceVideoType(sourceName, videoType) {\n if (this._channel && this._channel.isOpen()) {\n this._channel.sendSourceVideoTypeMessage(sourceName, videoType);\n }\n }\n\n /**\n * Elects the participants with the given ids to be the selected\n * participants in order to always receive video for this participant (even\n * when last n is enabled). If there is no channel we store it and send it\n * through the channel once it is created.\n *\n * @param {Array} ids - The user ids.\n * @throws NetworkError or InvalidStateError or Error if the operation\n * fails.\n * @returns {void}\n */\n selectEndpoints(ids) {\n this._selectedEndpoints = ids;\n\n if (this._channel && this._channel.isOpen()) {\n this._channel.sendSelectedEndpointsMessage(ids);\n }\n }\n\n /**\n *\n * @param eventType\n * @param listener\n */\n static addListener(eventType, listener) {\n RTCUtils.addListener(eventType, listener);\n }\n\n /**\n *\n * @param eventType\n * @param listener\n */\n static removeListener(eventType, listener) {\n RTCUtils.removeListener(eventType, listener);\n }\n\n /**\n *\n * @param options\n */\n static init(options = {}) {\n this.options = options;\n\n return RTCUtils.init(this.options);\n }\n\n /* eslint-disable max-params */\n\n /**\n * Creates new TraceablePeerConnection\n * @param {SignalingLayer} signaling The signaling layer that will provide information about the media or\n * participants which is not carried over SDP.\n * @param {object} pcConfig The {@code RTCConfiguration} to use for the WebRTC peer connection.\n * @param {boolean} isP2P Indicates whether or not the new TPC will be used in a peer to peer type of session.\n * @param {object} options The config options.\n * @param {boolean} options.enableInsertableStreams - Set to true when the insertable streams constraints is to be\n * enabled on the PeerConnection.\n * @param {boolean} options.disableSimulcast If set to 'true' will disable the simulcast.\n * @param {boolean} options.disableRtx If set to 'true' will disable the RTX.\n * @param {boolean} options.startSilent If set to 'true' no audio will be sent or received.\n * @return {TraceablePeerConnection}\n */\n createPeerConnection(signaling, pcConfig, isP2P, options) {\n const pcConstraints = JSON.parse(JSON.stringify(RTCUtils.pcConstraints));\n\n if (typeof options.abtestSuspendVideo !== 'undefined') {\n RTCUtils.setSuspendVideo(pcConstraints, options.abtestSuspendVideo);\n\n Statistics.analytics.addPermanentProperties(\n { abtestSuspendVideo: options.abtestSuspendVideo });\n }\n\n if (options.enableInsertableStreams) {\n logger.debug('E2EE - setting insertable streams constraints');\n pcConfig.encodedInsertableStreams = true;\n }\n\n const supportsSdpSemantics = browser.isReactNative()\n || (browser.isChromiumBased() && !options.usesUnifiedPlan);\n\n if (supportsSdpSemantics) {\n logger.debug('WebRTC application is running in plan-b mode');\n pcConfig.sdpSemantics = 'plan-b';\n }\n\n if (options.forceTurnRelay) {\n pcConfig.iceTransportPolicy = 'relay';\n }\n\n // Set the RTCBundlePolicy to max-bundle so that only one set of ice candidates is generated.\n // The default policy generates separate ice candidates for audio and video connections.\n // This change is necessary for Unified plan to work properly on Chrome and Safari.\n pcConfig.bundlePolicy = 'max-bundle';\n\n peerConnectionIdCounter = safeCounterIncrement(peerConnectionIdCounter);\n\n const newConnection\n = new TraceablePeerConnection(\n this,\n peerConnectionIdCounter,\n signaling,\n pcConfig, pcConstraints,\n isP2P, options);\n\n this.peerConnections.set(newConnection.id, newConnection);\n\n return newConnection;\n }\n\n /* eslint-enable max-params */\n\n /**\n * Removed given peer connection from this RTC module instance.\n * @param {TraceablePeerConnection} traceablePeerConnection\n * @return {boolean} true if the given peer connection was removed\n * successfully or false if there was no peer connection mapped in\n * this RTC instance.\n */\n _removePeerConnection(traceablePeerConnection) {\n const id = traceablePeerConnection.id;\n\n if (this.peerConnections.has(id)) {\n // NOTE Remote tracks are not removed here.\n this.peerConnections.delete(id);\n\n return true;\n }\n\n return false;\n\n }\n\n /**\n *\n * @param track\n */\n addLocalTrack(track) {\n if (!track) {\n throw new Error('track must not be null nor undefined');\n }\n\n this.localTracks.push(track);\n\n track.conference = this.conference;\n }\n\n /**\n * Get forwarded sources list.\n * @returns {Array|null}\n */\n getForwardedSources() {\n return this._forwardedSources;\n }\n\n /**\n * Get local video track.\n * @returns {JitsiLocalTrack|undefined}\n */\n getLocalVideoTrack() {\n const localVideo = this.getLocalTracks(MediaType.VIDEO);\n\n\n return localVideo.length ? localVideo[0] : undefined;\n }\n\n /**\n * Returns all the local video tracks.\n * @returns {Array}\n */\n getLocalVideoTracks() {\n return this.getLocalTracks(MediaType.VIDEO);\n }\n\n /**\n * Get local audio track.\n * @returns {JitsiLocalTrack|undefined}\n */\n getLocalAudioTrack() {\n const localAudio = this.getLocalTracks(MediaType.AUDIO);\n\n\n return localAudio.length ? localAudio[0] : undefined;\n }\n\n /**\n * Returns the endpoint id for the local user.\n * @returns {string}\n */\n getLocalEndpointId() {\n return this.conference.myUserId();\n }\n\n /**\n * Returns the local tracks of the given media type, or all local tracks if\n * no specific type is given.\n * @param {MediaType} [mediaType] Optional media type filter.\n * (audio or video).\n */\n getLocalTracks(mediaType) {\n let tracks = this.localTracks.slice();\n\n if (mediaType !== undefined) {\n tracks = tracks.filter(\n track => track.getType() === mediaType);\n }\n\n return tracks;\n }\n\n /**\n * Obtains all remote tracks currently known to this RTC module instance.\n * @param {MediaType} [mediaType] The remote tracks will be filtered\n * by their media type if this argument is specified.\n * @return {Array}\n */\n getRemoteTracks(mediaType) {\n let remoteTracks = [];\n\n for (const tpc of this.peerConnections.values()) {\n const pcRemoteTracks = tpc.getRemoteTracks(undefined, mediaType);\n\n if (pcRemoteTracks) {\n remoteTracks = remoteTracks.concat(pcRemoteTracks);\n }\n }\n\n return remoteTracks;\n }\n\n /**\n * Set mute for all local audio streams attached to the conference.\n * @param value The mute value.\n * @returns {Promise}\n */\n setAudioMute(value) {\n const mutePromises = [];\n\n this.getLocalTracks(MediaType.AUDIO).forEach(audioTrack => {\n // this is a Promise\n mutePromises.push(value ? audioTrack.mute() : audioTrack.unmute());\n });\n\n // We return a Promise from all Promises so we can wait for their\n // execution.\n return Promise.all(mutePromises);\n }\n\n /**\n * Set mute for all local video streams attached to the conference.\n * @param value The mute value.\n * @returns {Promise}\n */\n setVideoMute(value) {\n const mutePromises = [];\n\n this.getLocalTracks(MediaType.VIDEO).concat(this.getLocalTracks(MediaType.PRESENTER))\n .forEach(videoTrack => {\n // this is a Promise\n mutePromises.push(value ? videoTrack.mute() : videoTrack.unmute());\n });\n\n // We return a Promise from all Promises so we can wait for their\n // execution.\n return Promise.all(mutePromises);\n }\n\n /**\n *\n * @param track\n */\n removeLocalTrack(track) {\n const pos = this.localTracks.indexOf(track);\n\n if (pos === -1) {\n return;\n }\n\n this.localTracks.splice(pos, 1);\n }\n\n /**\n *\n * @param elSelector\n * @param stream\n */\n static attachMediaStream(elSelector, stream) {\n return RTCUtils.attachMediaStream(elSelector, stream);\n }\n\n /**\n * Returns the id of the given stream.\n * @param {MediaStream} stream\n */\n static getStreamID(stream) {\n return RTCUtils.getStreamID(stream);\n }\n\n /**\n * Returns the id of the given track.\n * @param {MediaStreamTrack} track\n */\n static getTrackID(track) {\n return RTCUtils.getTrackID(track);\n }\n\n /**\n * Returns true if retrieving the list of input devices is supported\n * and false if not.\n */\n static isDeviceListAvailable() {\n return RTCUtils.isDeviceListAvailable();\n }\n\n /**\n * Returns true if changing the input (camera / microphone) or output\n * (audio) device is supported and false if not.\n * @param {string} [deviceType] Type of device to change. Default is\n * undefined or 'input', 'output' - for audio output device change.\n * @returns {boolean} true if available, false otherwise.\n */\n static isDeviceChangeAvailable(deviceType) {\n return RTCUtils.isDeviceChangeAvailable(deviceType);\n }\n\n /**\n * Returns whether the current execution environment supports WebRTC (for\n * use within this library).\n *\n * @returns {boolean} {@code true} if WebRTC is supported in the current\n * execution environment (for use within this library); {@code false},\n * otherwise.\n */\n static isWebRtcSupported() {\n return browser.isSupported();\n }\n\n /**\n * Returns currently used audio output device id, '' stands for default\n * device\n * @returns {string}\n */\n static getAudioOutputDevice() {\n return RTCUtils.getAudioOutputDevice();\n }\n\n /**\n * Returns list of available media devices if its obtained, otherwise an\n * empty array is returned/\n * @returns {array} list of available media devices.\n */\n static getCurrentlyAvailableMediaDevices() {\n return RTCUtils.getCurrentlyAvailableMediaDevices();\n }\n\n /**\n * Returns whether available devices have permissions granted\n * @returns {Boolean}\n */\n static arePermissionsGrantedForAvailableDevices() {\n return RTCUtils.arePermissionsGrantedForAvailableDevices();\n }\n\n /**\n * Returns event data for device to be reported to stats.\n * @returns {MediaDeviceInfo} device.\n */\n static getEventDataForActiveDevice(device) {\n return RTCUtils.getEventDataForActiveDevice(device);\n }\n\n /**\n * Sets current audio output device.\n * @param {string} deviceId Id of 'audiooutput' device from\n * navigator.mediaDevices.enumerateDevices().\n * @returns {Promise} resolves when audio output is changed, is rejected\n * otherwise\n */\n static setAudioOutputDevice(deviceId) {\n return RTCUtils.setAudioOutputDevice(deviceId);\n }\n\n /**\n * Returns true if given WebRTC MediaStream is considered a valid\n * \"user\" stream which means that it's not a \"receive only\" stream nor a\n * \"mixed\" JVB stream.\n *\n * Clients that implement Unified Plan, such as Firefox use recvonly\n * \"streams/channels/tracks\" for receiving remote stream/tracks, as opposed\n * to Plan B where there are only 3 channels: audio, video and data.\n *\n * @param {MediaStream} stream The WebRTC MediaStream instance.\n * @returns {boolean}\n */\n static isUserStream(stream) {\n return RTC.isUserStreamById(RTCUtils.getStreamID(stream));\n }\n\n /**\n * Returns true if a WebRTC MediaStream identified by given stream\n * ID is considered a valid \"user\" stream which means that it's not a\n * \"receive only\" stream nor a \"mixed\" JVB stream.\n *\n * Clients that implement Unified Plan, such as Firefox use recvonly\n * \"streams/channels/tracks\" for receiving remote stream/tracks, as opposed\n * to Plan B where there are only 3 channels: audio, video and data.\n *\n * @param {string} streamId The id of WebRTC MediaStream.\n * @returns {boolean}\n */\n static isUserStreamById(streamId) {\n return streamId && streamId !== 'mixedmslabel'\n && streamId !== 'default';\n }\n\n /**\n * Allows to receive list of available cameras/microphones.\n * @param {function} callback Would receive array of devices as an\n * argument.\n */\n static enumerateDevices(callback) {\n RTCUtils.enumerateDevices(callback);\n }\n\n /**\n * A method to handle stopping of the stream.\n * One point to handle the differences in various implementations.\n * @param {MediaStream} mediaStream MediaStream object to stop.\n */\n static stopMediaStream(mediaStream) {\n RTCUtils.stopMediaStream(mediaStream);\n }\n\n /**\n * Returns whether the desktop sharing is enabled or not.\n * @returns {boolean}\n */\n static isDesktopSharingEnabled() {\n return RTCUtils.isDesktopSharingEnabled();\n }\n\n /**\n * Closes the currently opened bridge channel.\n */\n closeBridgeChannel() {\n if (this._channel) {\n this._channel.close();\n this._channel = null;\n\n this.removeListener(RTCEvents.LASTN_ENDPOINT_CHANGED, this._lastNChangeListener);\n }\n }\n\n /* eslint-disable max-params */\n /**\n *\n * @param {TraceablePeerConnection} tpc\n * @param {number} ssrc\n * @param {number} audioLevel\n * @param {boolean} isLocal\n */\n setAudioLevel(tpc, ssrc, audioLevel, isLocal) {\n const track = tpc.getTrackBySSRC(ssrc);\n\n if (!track) {\n return;\n } else if (!track.isAudioTrack()) {\n logger.warn(`Received audio level for non-audio track: ${ssrc}`);\n\n return;\n } else if (track.isLocal() !== isLocal) {\n logger.error(\n `${track} was expected to ${isLocal ? 'be' : 'not be'} local`);\n }\n\n track.setAudioLevel(audioLevel, tpc);\n }\n\n /**\n * Sends message via the bridge channel.\n * @param {string} to The id of the endpoint that should receive the\n * message. If \"\" the message will be sent to all participants.\n * @param {object} payload The payload of the message.\n * @throws NetworkError or InvalidStateError or Error if the operation\n * fails or there is no data channel created.\n */\n sendChannelMessage(to, payload) {\n if (this._channel) {\n this._channel.sendMessage(to, payload);\n } else {\n throw new Error('Channel support is disabled!');\n }\n }\n\n /**\n * Sends the local stats via the bridge channel.\n * @param {Object} payload The payload of the message.\n * @throws NetworkError/InvalidStateError/Error if the operation fails or if there is no data channel created.\n */\n sendEndpointStatsMessage(payload) {\n if (this._channel && this._channel.isOpen()) {\n this._channel.sendEndpointStatsMessage(payload);\n }\n }\n\n /**\n * Selects a new value for \"lastN\". The requested amount of videos are going\n * to be delivered after the value is in effect. Set to -1 for unlimited or\n * all available videos.\n * @param {number} value the new value for lastN.\n */\n setLastN(value) {\n if (this._lastN !== value) {\n this._lastN = value;\n if (this._channel && this._channel.isOpen()) {\n this._channel.sendSetLastNMessage(value);\n }\n this.eventEmitter.emit(RTCEvents.LASTN_VALUE_CHANGED, value);\n }\n }\n\n /**\n * Indicates if the endpoint id is currently included in the last N.\n * @param {string} id The endpoint id that we check for last N.\n * @returns {boolean} true if the endpoint id is in the last N or if we\n * don't have bridge channel support, otherwise we return false.\n */\n isInLastN(id) {\n return !this._lastNEndpoints // lastNEndpoints not initialised yet.\n || this._lastNEndpoints.indexOf(id) > -1;\n }\n\n /**\n * Indicates if the source name is currently included in the forwarded sources.\n *\n * @param {string} sourceName The source name that we check for forwarded sources.\n * @returns {boolean} true if the source name is in the forwarded sources or if we don't have bridge channel\n * support, otherwise we return false.\n */\n isInForwardedSources(sourceName) {\n return !this._forwardedSources // forwardedSources not initialised yet.\n || this._forwardedSources.indexOf(sourceName) > -1;\n }\n\n /**\n * Updates the target audio output device for all remote audio tracks.\n *\n * @param {string} deviceId - The device id of the audio ouput device to\n * use for all remote tracks.\n * @private\n * @returns {void}\n */\n _updateAudioOutputForAudioTracks(deviceId) {\n const remoteAudioTracks = this.getRemoteTracks(MediaType.AUDIO);\n\n for (const track of remoteAudioTracks) {\n track.setAudioOutput(deviceId);\n }\n }\n}\n","/**\n * Indicates that the local connection statistics were updated.\n */\nexport const LOCAL_STATS_UPDATED = 'cq.local_stats_updated';\n\n/**\n * Indicates that the connection statistics for a particular remote participant\n * were updated.\n */\nexport const REMOTE_STATS_UPDATED = 'cq.remote_stats_updated';\n","import { getLogger } from '@jitsi/logger';\n\nimport * as ConferenceEvents from '../../JitsiConferenceEvents';\nimport CodecMimeType from '../../service/RTC/CodecMimeType';\nimport * as RTCEvents from '../../service/RTC/RTCEvents';\nimport * as ConnectionQualityEvents from '../../service/connectivity/ConnectionQualityEvents';\nimport browser from '../browser';\n\nconst Resolutions = require('../../service/RTC/Resolutions');\nconst VideoType = require('../../service/RTC/VideoType');\nconst XMPPEvents = require('../../service/xmpp/XMPPEvents');\n\nconst logger = getLogger(__filename);\n\n/**\n * The value to use for the \"type\" field for messages sent by ConnectionQuality\n * over the data channel.\n */\nconst STATS_MESSAGE_TYPE = 'stats';\n\n/**\n * The value to use for the \"type\" field for messages sent\n * over the data channel that contain facial expression.\n */\nconst FACIAL_EXPRESSION_MESSAGE_TYPE = 'facial_expression';\n\nconst kSimulcastFormats = [\n { width: 1920,\n height: 1080,\n layers: 3,\n target: 'high',\n targetRN: 4000000 },\n { width: 1280,\n height: 720,\n layers: 3,\n target: 'high',\n targetRN: 2500000 },\n { width: 960,\n height: 540,\n layers: 3,\n target: 'standard',\n targetRN: 900000 },\n { width: 640,\n height: 360,\n layers: 2,\n target: 'standard',\n targetRN: 500000 },\n { width: 480,\n height: 270,\n layers: 2,\n target: 'low',\n targetRN: 350000 },\n { width: 320,\n height: 180,\n layers: 1,\n target: 'low',\n targetRN: 150000 }\n];\n\n/**\n * The maximum bitrate to use as a measurement against the participant's current\n * bitrate. This cap helps in the cases where the participant's bitrate is high\n * but not enough to fulfill high targets, such as with 1080p.\n */\nconst MAX_TARGET_BITRATE = 2500;\n\n/**\n * The initial bitrate for video in kbps.\n */\nlet startBitrate = 800;\n\n/**\n * Gets the expected bitrate (in kbps) in perfect network conditions.\n * @param simulcast {boolean} whether simulcast is enabled or not.\n * @param resolution {Resolution} the resolution.\n * @param millisSinceStart {number} the number of milliseconds since sending video started.\n * @param videoQualitySettings {Object} the bitrate and codec settings for the local video source.\n */\nfunction getTarget(simulcast, resolution, millisSinceStart, videoQualitySettings) {\n let target = 0;\n let height = Math.min(resolution.height, resolution.width);\n\n // Find the first format with height no bigger than ours.\n let simulcastFormat = kSimulcastFormats.find(f => f.height <= height);\n\n if (simulcastFormat && simulcast && videoQualitySettings.codec === CodecMimeType.VP8) {\n // Sum the target fields from all simulcast layers for the given\n // resolution (e.g. 720p + 360p + 180p) for VP8 simulcast.\n for (height = simulcastFormat.height; height >= 180; height /= 2) {\n const targetHeight = height;\n\n simulcastFormat = kSimulcastFormats.find(f => f.height === targetHeight);\n if (simulcastFormat) {\n target += browser.isReactNative()\n ? simulcastFormat.targetRN\n : videoQualitySettings[simulcastFormat.target];\n } else {\n break;\n }\n }\n } else if (simulcastFormat) {\n // For VP9 SVC, H.264 (simulcast automatically disabled) and p2p, target bitrate will be\n // same as that of the individual stream bitrate.\n target = browser.isReactNative()\n ? simulcastFormat.targetRN\n : videoQualitySettings[simulcastFormat.target];\n }\n\n // Allow for an additional 1 second for ramp up -- delay any initial drop\n // of connection quality by 1 second. Convert target from bps to kbps.\n return Math.min(target / 1000, rampUp(Math.max(0, millisSinceStart - 1000)));\n}\n\n/**\n * Gets the bitrate to which GCC would have ramped up in perfect network\n * conditions after millisSinceStart milliseconds.\n * @param millisSinceStart {number} the number of milliseconds since sending\n * video was enabled.\n */\nfunction rampUp(millisSinceStart) {\n if (millisSinceStart > 60000) {\n return Number.MAX_SAFE_INTEGER;\n }\n\n // According to GCC the send side bandwidth estimation grows with at most\n // 8% per second.\n // https://tools.ietf.org/html/draft-ietf-rmcat-gcc-02#section-5.5\n return startBitrate * Math.pow(1.08, millisSinceStart / 1000);\n}\n\n/**\n * A class which monitors the local statistics coming from the RTC modules, and\n * calculates a \"connection quality\" value, in percent, for the media\n * connection. A value of 100% indicates a very good network connection, and a\n * value of 0% indicates a poor connection.\n */\nexport default class ConnectionQuality {\n /**\n *\n * @param conference\n * @param eventEmitter\n * @param options\n */\n constructor(conference, eventEmitter, options) {\n this.eventEmitter = eventEmitter;\n\n /**\n * The owning JitsiConference.\n */\n this._conference = conference;\n\n /**\n * Holds statistics about the local connection quality.\n */\n this._localStats = {\n connectionQuality: 100,\n jvbRTT: undefined\n };\n\n /**\n * The time this._localStats.connectionQuality was last updated.\n */\n this._lastConnectionQualityUpdate = -1;\n\n /**\n * Conference options.\n */\n this._options = options;\n\n /**\n * Maps a participant ID to an object holding connection quality\n * statistics received from this participant.\n */\n this._remoteStats = {};\n\n /**\n * The time that the ICE state last changed to CONNECTED. We use this\n * to calculate how much time we as a sender have had to ramp-up.\n */\n this._timeIceConnected = -1;\n\n /**\n * The time that local video was unmuted. We use this to calculate how\n * much time we as a sender have had to ramp-up.\n */\n this._timeVideoUnmuted = -1;\n\n // We assume a global startBitrate value for the sake of simplicity.\n if (this._options.config?.startBitrate > 0) {\n startBitrate = this._options.config.startBitrate;\n }\n\n // TODO: consider ignoring these events and letting the user of\n // lib-jitsi-meet handle these separately.\n conference.on(\n ConferenceEvents.CONNECTION_INTERRUPTED,\n () => {\n this._updateLocalConnectionQuality(0);\n this.eventEmitter.emit(\n ConnectionQualityEvents.LOCAL_STATS_UPDATED,\n this._localStats);\n this._broadcastLocalStats();\n });\n\n conference.room.addListener(\n XMPPEvents.ICE_CONNECTION_STATE_CHANGED,\n (jingleSession, newState) => {\n if (!jingleSession.isP2P && newState === 'connected') {\n this._timeIceConnected = window.performance.now();\n }\n });\n\n // Listen to DataChannel message from other participants in the\n // conference, and update the _remoteStats field accordingly.\n // TODO - Delete this when all the mobile endpoints switch to using the new Colibri\n // message format for sending the endpoint stats.\n conference.on(\n ConferenceEvents.ENDPOINT_MESSAGE_RECEIVED,\n (participant, payload) => {\n if (payload.type === STATS_MESSAGE_TYPE) {\n this._updateRemoteStats(\n participant.getId(), payload.values);\n }\n });\n\n conference.on(\n ConferenceEvents.ENDPOINT_STATS_RECEIVED,\n (participant, payload) => {\n this._updateRemoteStats(participant.getId(), payload);\n });\n\n conference.on(\n ConferenceEvents.ENDPOINT_MESSAGE_RECEIVED,\n (participant, payload) => {\n if (payload.type === FACIAL_EXPRESSION_MESSAGE_TYPE) {\n this.eventEmitter.emit(\n ConferenceEvents.FACIAL_EXPRESSION_ADDED,\n participant.getId(),\n payload);\n }\n });\n\n // Listen to local statistics events originating from the RTC module and update the _localStats field.\n conference.statistics.addConnectionStatsListener(this._updateLocalStats.bind(this));\n\n // Save the last time we were unmuted.\n conference.on(\n ConferenceEvents.TRACK_MUTE_CHANGED,\n track => {\n if (track.isVideoTrack()) {\n if (track.isMuted()) {\n this._timeVideoUnmuted = -1;\n } else {\n this._maybeUpdateUnmuteTime();\n }\n }\n });\n conference.on(\n ConferenceEvents.TRACK_ADDED,\n track => {\n if (track.isVideoTrack() && !track.isMuted()) {\n this._maybeUpdateUnmuteTime();\n }\n });\n conference.rtc.on(\n RTCEvents.LOCAL_TRACK_MAX_ENABLED_RESOLUTION_CHANGED,\n track => {\n this._localStats.maxEnabledResolution = track.maxEnabledResolution;\n });\n\n conference.on(\n ConferenceEvents.SERVER_REGION_CHANGED,\n serverRegion => {\n this._localStats.serverRegion = serverRegion;\n });\n\n conference.on(\n ConferenceEvents.PROPERTIES_CHANGED,\n properties => {\n this._localStats.bridgeCount\n = Number((properties || {})['bridge-count']);\n }\n );\n }\n\n /**\n * Sets _timeVideoUnmuted if it was previously unset. If it was already set,\n * doesn't change it.\n */\n _maybeUpdateUnmuteTime() {\n if (this._timeVideoUnmuted < 0) {\n this._timeVideoUnmuted = window.performance.now();\n }\n }\n\n /**\n * Calculates a new \"connection quality\" value.\n * @param videoType {VideoType} the type of the video source (camera or a screen capture).\n * @param isMuted {boolean} whether the local video is muted.\n * @param resolutionName {Resolution} the input resolution used by the camera.\n * @returns {*} the newly calculated connection quality.\n */\n _calculateConnectionQuality(videoType, isMuted, resolutionName) {\n\n // resolutionName is an index into Resolutions (where \"720\" is\n // \"1280x720\" and \"960\" is \"960x720\" ...).\n const resolution = Resolutions[resolutionName];\n\n let quality = 100;\n let packetLoss;\n\n // TODO: take into account packet loss for received streams\n\n if (this._localStats.packetLoss) {\n packetLoss = this._localStats.packetLoss.upload;\n\n // Ugly Hack Alert (UHA):\n // The packet loss for the upload direction is calculated based on\n // incoming RTCP Receiver Reports. Since we don't have RTCP\n // termination for audio, these reports come from the actual\n // receivers in the conference and therefore the reported packet\n // loss includes loss from the bridge to the receiver.\n // When we are sending video this effect is small, because the\n // number of video packets is much larger than the number of audio\n // packets (and our calculation is based on the total number of\n // received and lost packets).\n // When video is muted, however, the effect might be significant,\n // but we don't know what it is. We do know that it is positive, so\n // as a temporary solution, until RTCP termination is implemented\n // for the audio streams, we relax the packet loss checks here.\n if (isMuted) {\n packetLoss *= 0.5;\n }\n }\n\n if (isMuted || !resolution || videoType === VideoType.DESKTOP\n || this._timeIceConnected < 0\n || this._timeVideoUnmuted < 0) {\n\n // Calculate a value based on packet loss only.\n if (packetLoss === undefined) {\n logger.error('Cannot calculate connection quality, unknown '\n + 'packet loss.');\n quality = 100;\n } else if (packetLoss <= 2) {\n quality = 100; // Full 5 bars.\n } else if (packetLoss <= 4) {\n quality = 70; // 4 bars\n } else if (packetLoss <= 6) {\n quality = 50; // 3 bars\n } else if (packetLoss <= 8) {\n quality = 30; // 2 bars\n } else if (packetLoss <= 12) {\n quality = 10; // 1 bars\n } else {\n quality = 0; // Still 1 bar, but slower climb-up.\n }\n } else {\n // Calculate a value based on the send video bitrate on the active TPC.\n const activeTPC = this._conference.getActivePeerConnection();\n\n if (activeTPC) {\n const isSimulcastOn = activeTPC.isSimulcastOn();\n const videoQualitySettings = activeTPC.getTargetVideoBitrates();\n\n // Add the codec info as well.\n videoQualitySettings.codec = activeTPC.getConfiguredVideoCodec();\n\n // Time since sending of video was enabled.\n const millisSinceStart = window.performance.now()\n - Math.max(this._timeVideoUnmuted, this._timeIceConnected);\n const statsInterval = this._options.config?.pcStatsInterval ?? 10000;\n\n // Expected sending bitrate in perfect conditions.\n let target = getTarget(isSimulcastOn, resolution, millisSinceStart, videoQualitySettings);\n\n target = Math.min(target, MAX_TARGET_BITRATE);\n\n // Calculate the quality only after the stats are available (after video was enabled).\n if (millisSinceStart > statsInterval) {\n quality = 100 * this._localStats.bitrate.upload / target;\n }\n }\n\n // Whatever the bitrate, drop early if there is significant loss\n if (packetLoss && packetLoss >= 10) {\n quality = Math.min(quality, 30);\n }\n }\n\n // Make sure that the quality doesn't climb quickly\n if (this._lastConnectionQualityUpdate > 0) {\n const maxIncreasePerSecond = 2;\n const prevConnectionQuality = this._localStats.connectionQuality;\n const diffSeconds = (window.performance.now() - this._lastConnectionQualityUpdate) / 1000;\n\n quality = Math.min(quality, prevConnectionQuality + (diffSeconds * maxIncreasePerSecond));\n }\n\n return Math.min(100, quality);\n }\n\n /**\n * Updates the localConnectionQuality value\n * @param values {number} the new value. Should be in [0, 100].\n */\n _updateLocalConnectionQuality(value) {\n this._localStats.connectionQuality = value;\n this._lastConnectionQualityUpdate = window.performance.now();\n }\n\n /**\n * Broadcasts the local statistics to all other participants in the\n * conference.\n */\n _broadcastLocalStats() {\n // Send only the data that remote participants care about.\n const data = {\n bitrate: this._localStats.bitrate,\n packetLoss: this._localStats.packetLoss,\n connectionQuality: this._localStats.connectionQuality,\n jvbRTT: this._localStats.jvbRTT,\n serverRegion: this._localStats.serverRegion,\n maxEnabledResolution: this._localStats.maxEnabledResolution,\n avgAudioLevels: this._localStats.localAvgAudioLevels\n };\n\n try {\n this._conference.sendEndpointStatsMessage(data);\n } catch (err) {\n // Ignore the error as we might hit it in the beginning of the call before the channel is ready.\n // The statistics will be sent again after few seconds and error is logged elseware as well.\n }\n }\n\n /**\n * Updates the local statistics\n * @param {TraceablePeerConnection} tpc the peerconnection which emitted\n * the stats\n * @param data new statistics\n */\n _updateLocalStats(tpc, data) {\n // Update jvbRTT\n if (!tpc.isP2P) {\n const jvbRTT\n = data.transport\n && data.transport.length && data.transport[0].rtt;\n\n this._localStats.jvbRTT = jvbRTT ? jvbRTT : undefined;\n }\n\n // Do not continue with processing of other stats if they do not\n // originate from the active peerconnection\n if (tpc !== this._conference.getActivePeerConnection()) {\n return;\n }\n\n let key;\n const updateLocalConnectionQuality\n = !this._conference.isConnectionInterrupted();\n const localVideoTrack\n = this._conference.getLocalVideoTrack();\n const videoType\n = localVideoTrack ? localVideoTrack.videoType : undefined;\n const isMuted = localVideoTrack ? localVideoTrack.isMuted() : true;\n const resolution = localVideoTrack\n ? Math.min(localVideoTrack.resolution, localVideoTrack.maxEnabledResolution) : null;\n\n if (!isMuted) {\n this._maybeUpdateUnmuteTime();\n }\n\n // Copy the fields already in 'data'.\n for (key in data) {\n if (data.hasOwnProperty(key)) {\n this._localStats[key] = data[key];\n }\n }\n\n // And re-calculate the connectionQuality field.\n if (updateLocalConnectionQuality) {\n this._updateLocalConnectionQuality(\n this._calculateConnectionQuality(\n videoType,\n isMuted,\n resolution));\n }\n\n this.eventEmitter.emit(\n ConnectionQualityEvents.LOCAL_STATS_UPDATED,\n this._localStats);\n this._broadcastLocalStats();\n }\n\n /**\n * Updates remote statistics\n * @param id the id of the remote participant\n * @param data the statistics received\n */\n _updateRemoteStats(id, data) {\n // Use only the fields we need\n this._remoteStats[id] = {\n bitrate: data.bitrate,\n packetLoss: data.packetLoss,\n connectionQuality: data.connectionQuality,\n jvbRTT: data.jvbRTT,\n serverRegion: data.serverRegion,\n maxEnabledResolution: data.maxEnabledResolution,\n avgAudioLevels: data.avgAudioLevels\n };\n\n this.eventEmitter.emit(\n ConnectionQualityEvents.REMOTE_STATS_UPDATED,\n id,\n this._remoteStats[id]);\n }\n\n /**\n * Returns the local statistics.\n * Exported only for use in jitsi-meet-torture.\n */\n getStats() {\n return this._localStats;\n }\n}\n","import { getLogger } from '@jitsi/logger';\n\nimport * as JitsiConferenceErrors from '../../JitsiConferenceErrors';\nimport * as JitsiConferenceEvents from '../../JitsiConferenceEvents';\n\nconst logger = getLogger(__filename);\n\n/**\n * This class deals with shenanigans around JVB media session's ICE failed status handling.\n *\n * If ICE restarts are NOT explicitly enabled by the {@code enableIceRestart} config option, then the conference will\n * delay emitting the {@JitsiConferenceErrors.ICE_FAILED} event by 15 seconds. If the network info module reports\n * the internet offline status then the time will start counting after the internet comes back online.\n *\n * If ICE restart are enabled, then a delayed ICE failed notification to Jicofo will be sent, only if the ICE connection\n * does not recover soon after or before the XMPP connection is restored (if it was ever broken). If ICE fails while\n * the XMPP connection is not broken then the notifications will be sent after 2 seconds delay.\n */\nexport default class IceFailedHandling {\n /**\n * Creates new {@code DelayedIceFailed} task.\n * @param {JitsiConference} conference\n */\n constructor(conference) {\n this._conference = conference;\n }\n\n /**\n * After making sure there's no way for the ICE connection to recover this method either sends ICE failed\n * notification to Jicofo or emits the ice failed conference event.\n * @private\n * @returns {void}\n */\n _actOnIceFailed() {\n const { enableForcedReload, enableIceRestart } = this._conference.options.config;\n const explicitlyDisabled = typeof enableIceRestart !== 'undefined' && !enableIceRestart;\n const supportsRestartByTerminate = this._conference.room.supportsRestartByTerminate();\n const useTerminateForRestart = supportsRestartByTerminate && !enableIceRestart;\n\n logger.info('ICE failed,'\n + ` enableForcedReload: ${enableForcedReload},`\n + ` enableIceRestart: ${enableIceRestart},`\n + ` supports restart by terminate: ${supportsRestartByTerminate}`);\n\n if (explicitlyDisabled || (!enableIceRestart && !supportsRestartByTerminate) || enableForcedReload) {\n logger.info('ICE failed, but ICE restarts are disabled');\n const reason = enableForcedReload\n ? JitsiConferenceErrors.CONFERENCE_RESTARTED\n : JitsiConferenceErrors.ICE_FAILED;\n\n this._conference.eventEmitter.emit(JitsiConferenceEvents.CONFERENCE_FAILED, reason);\n\n return;\n }\n\n const jvbConnection = this._conference.jvbJingleSession;\n const jvbConnIceState = jvbConnection && jvbConnection.getIceConnectionState();\n\n if (!jvbConnection) {\n logger.warn('Not sending ICE failed - no JVB connection');\n } else if (jvbConnIceState === 'connected') {\n logger.info('ICE connection restored - not sending ICE failed');\n } else {\n logger.info('Sending ICE failed - the connection did not recover, '\n + `ICE state: ${jvbConnIceState}, `\n + `use 'session-terminate': ${useTerminateForRestart}`);\n if (useTerminateForRestart) {\n this._conference.jvbJingleSession.terminate(\n () => {\n logger.info('session-terminate for ice restart - done');\n },\n error => {\n logger.error(`session-terminate for ice restart - error: ${error.message}`);\n }, {\n reason: 'connectivity-error',\n reasonDescription: 'ICE FAILED',\n requestRestart: true,\n sendSessionTerminate: true\n });\n } else {\n this._conference.jvbJingleSession.sendIceFailedNotification();\n }\n }\n }\n\n /**\n * Starts the task.\n */\n start() {\n // Using xmpp.ping allows to handle both XMPP being disconnected and internet offline cases. The ping function\n // uses sendIQ2 method which is resilient to XMPP connection disconnected state and will patiently wait until it\n // gets reconnected.\n // This also handles the case about waiting for the internet to come back online, because ping\n // will only succeed when the internet is online and then there's a chance for the ICE to recover from FAILED to\n // CONNECTED which is the extra 2 second timeout after ping.\n // The 65 second timeout is given on purpose as there's no chance for XMPP to recover after 65 seconds of no\n // communication with the server. Such resume attempt will result in unrecoverable conference failed event due\n // to 'item-not-found' error returned by the server.\n this._conference.xmpp.ping(65000).then(\n () => {\n if (!this._canceled) {\n this._iceFailedTimeout = window.setTimeout(() => {\n this._iceFailedTimeout = undefined;\n this._actOnIceFailed();\n }, 2000);\n }\n },\n error => {\n logger.error('PING error/timeout - not sending ICE failed', error);\n });\n }\n\n /**\n * Cancels the task.\n */\n cancel() {\n this._canceled = true;\n window.clearTimeout(this._iceFailedTimeout);\n }\n}\n","/**\n * Event triggered by a audio detector indicating that its active state has changed from active to inactive or vice\n * versa.\n * @event\n * @type {boolean} - true when service has changed to active false otherwise.\n */\nexport const DETECTOR_STATE_CHANGE = 'detector_state_change';\n\n/** Event triggered by {@link NoAudioSignalDetector} when the local audio device associated with a JitsiConference\n * starts receiving audio levels with the value of 0 meaning no audio is being captured on that device, or when\n * it starts receiving audio levels !== 0 after being in a state of no audio.\n * @event\n * @type {boolean} - true when the current conference audio track has audio input false otherwise.\n */\nexport const AUDIO_INPUT_STATE_CHANGE = 'audio_input_state_changed';\n\n/** Event triggered by NoAudioSignalDetector when the local audio device associated with a JitsiConference goes silent\n * for a period of time, meaning that the device is either broken or hardware/software muted.\n * @event\n * @type {void}\n */\nexport const NO_AUDIO_INPUT = 'no_audio_input_detected';\n\n/**\n * Event generated by {@link VADNoiseDetection} when the tracked device is considered noisy.\n * @event\n * @type {Object}\n */\nexport const VAD_NOISY_DEVICE = 'detection.vad_noise_device';\n\n/**\n * Event generated by VADReportingService when if finishes creating a VAD report for the monitored devices.\n * The generated objects are of type Array, one score for each monitored device.\n * @event VAD_REPORT_PUBLISHED\n * @type Array with the following structure:\n * @property {Date} timestamp - Timestamp at which the compute took place.\n * @property {number} avgVAD - Average VAD score over monitored period of time.\n * @property {string} deviceId - Associate local audio device ID.\n */\nexport const VAD_REPORT_PUBLISHED = 'vad-report-published';\n\n/**\n * Event generated by {@link TrackVADEmitter} when PCM sample VAD score is available.\n *\n * @event\n * @type {Object}\n * @property {Date} timestamp - Exact time at which processed PCM sample was generated.\n * @property {number} score - VAD score on a scale from 0 to 1 (i.e. 0.7)\n * @property {Float32Array} pcmData - Raw PCM data with which the VAD score was calculated.\n * @property {string} deviceId - Device id of the associated track.\n */\nexport const VAD_SCORE_PUBLISHED = 'detection.vad_score_published';\n\n/**\n * Event generated by {@link VADTalkMutedDetection} when a user is talking while the mic is muted.\n *\n * @event\n * @type {Object}\n */\nexport const VAD_TALK_WHILE_MUTED = 'detection.vad_talk_while_muted';\n","import EventEmitter from 'events';\n\nimport * as JitsiConferenceEvents from '../../JitsiConferenceEvents';\nimport * as JitsiTrackEvents from '../../JitsiTrackEvents';\nimport browser from '../browser';\n\nimport * as DetectionEvents from './DetectionEvents';\n\n// We wait a certain time interval for constant silence input from the current device to account for\n// potential abnormalities and for a better use experience i.e. don't generate event the instant\n// an audio track is added to the tcr.\n// Potential improvement - add this as a configurable parameter.\nconst SILENCE_PERIOD_MS = 4000;\n\n/**\n * Detect if there is no audio input on the current TraceAblePeerConnection selected track. The no audio\n * state must be constant for a configured amount of time in order for the event to be triggered.\n * @fires DetectionEvents.AUDIO_INPUT_STATE_CHANGE\n * @fires DetectionEvents.NO_AUDIO_INPUT\n */\nexport default class NoAudioSignalDetection extends EventEmitter {\n /**\n * Creates new NoAudioSignalDetection.\n *\n * @param conference the JitsiConference instance that created us.\n * @constructor\n */\n constructor(conference) {\n super();\n\n this._conference = conference;\n this._timeoutTrigger = null;\n this._hasAudioInput = null;\n\n if (!browser.supportsReceiverStats()) {\n conference.statistics.addAudioLevelListener(this._audioLevel.bind(this));\n }\n conference.on(JitsiConferenceEvents.TRACK_ADDED, this._trackAdded.bind(this));\n }\n\n /**\n * Clear the timeout state.\n */\n _clearTriggerTimeout() {\n clearTimeout(this._timeoutTrigger);\n this._timeoutTrigger = null;\n }\n\n\n /**\n * Generated event triggered by a change in the current conference audio input state.\n *\n * @param {*} audioLevel - The audio level of the ssrc.\n * @fires DetectionEvents.AUDIO_INPUT_STATE_CHANGE\n */\n _handleAudioInputStateChange(audioLevel) {\n // Current audio input state of the active local track in the conference, true for audio input false for no\n // audio input.\n const status = audioLevel !== 0;\n\n // If this is the first audio event picked up or the current status is different from the previous trigger\n // the event.\n if (this._hasAudioInput === null || this._hasAudioInput !== status) {\n this._hasAudioInput = status;\n this.emit(DetectionEvents.AUDIO_INPUT_STATE_CHANGE, this._hasAudioInput);\n }\n }\n\n /**\n * Generate event triggered by a prolonged period of no audio input.\n *\n * @param {number} audioLevel - The audio level of the ssrc.\n * @fires DetectionEvents.NO_AUDIO_INPUT\n */\n _handleNoAudioInputDetection(audioLevel) {\n if (this._eventFired) {\n return;\n }\n\n if (audioLevel === 0 && !this._timeoutTrigger) {\n this._timeoutTrigger = setTimeout(() => {\n this._eventFired = true;\n\n this.emit(DetectionEvents.NO_AUDIO_INPUT);\n }, SILENCE_PERIOD_MS);\n } else if (audioLevel !== 0 && this._timeoutTrigger) {\n this._clearTriggerTimeout();\n }\n }\n\n /**\n * Receives audio level events for all send and receive streams on the current TraceablePeerConnection.\n *\n * @param {TraceablePeerConnection} tpc - TraceablePeerConnection of the owning conference.\n * @param {number} ssrc - The synchronization source identifier (SSRC) of the endpoint/participant/stream\n * being reported.\n * @param {number} audioLevel - The audio level of the ssrc.\n * @param {boolean} isLocal - true for local/send streams or false for remote/receive streams.\n */\n _audioLevel(tpc, ssrc, audioLevel, isLocal) {\n // We are interested in the local audio streams\n if (!isLocal || !this._audioTrack) {\n return;\n }\n\n // Get currently active local tracks from the TraceablePeerConnection\n const localSSRCs = tpc.localSSRCs.get(this._audioTrack.rtcId);\n\n // Only target the current active track in the tpc. For some reason audio levels for previous\n // devices are also picked up from the PeerConnection so we filter them out.\n if (!localSSRCs || !localSSRCs.ssrcs.includes(ssrc)) {\n return;\n }\n\n // First handle audio input state change. In case the state changed to no input the no audio input event\n // can try to fire again.\n this._handleAudioInputStateChange(audioLevel);\n this._handleNoAudioInputDetection(audioLevel);\n }\n\n /**\n * Notifies NoAudioSignalDetection that a JitsiTrack was added to the associated JitsiConference.\n * Only take into account local audio tracks.\n *\n * @param {JitsiTrack} track - The added JitsiTrack.\n */\n _trackAdded(track) {\n if (track.isLocalAudioTrack()) {\n // Reset state for the new track.\n this._audioTrack = track;\n this._eventFired = false;\n this._clearTriggerTimeout();\n\n // Listen for the audio levels on the newly added audio track\n if (browser.supportsReceiverStats()) {\n track.on(\n JitsiTrackEvents.NO_AUDIO_INPUT,\n audioLevel => {\n this._handleNoAudioInputDetection(audioLevel);\n }\n );\n track.on(\n JitsiTrackEvents.TRACK_AUDIO_LEVEL_CHANGED,\n audioLevel => {\n this._handleNoAudioInputDetection(audioLevel);\n this._handleAudioInputStateChange(audioLevel);\n }\n );\n }\n }\n }\n}\n","import * as JitsiConferenceEvents from '../../JitsiConferenceEvents';\nimport RTCEvents from '../../service/RTC/RTCEvents';\n\n/**\n * The value which we use to say, every sound over this threshold\n * is talking on the mic.\n * @type {number}\n */\nconst SPEECH_DETECT_THRESHOLD = 0.6;\n\n/**\n * The P2PDominantSpeakerDetection is activated only when p2p is\n * currently used.\n * Listens for changes in the audio level changes of the local p2p audio track\n * or remote p2p one and fires dominant speaker events to be able to use\n * features depending on those events (speaker stats), to make them work without\n * the video bridge.\n */\nexport default class P2PDominantSpeakerDetection {\n /**\n * Creates P2PDominantSpeakerDetection\n * @param conference the JitsiConference instance that created us.\n * @constructor\n */\n constructor(conference) {\n this.conference = conference;\n\n conference.addEventListener(\n JitsiConferenceEvents.TRACK_AUDIO_LEVEL_CHANGED,\n this._audioLevel.bind(this));\n\n this.myUserID = this.conference.myUserId();\n }\n\n /**\n * Receives audio level events for all streams in the conference.\n *\n * @param {String} id - The participant id\n * @param {number} audioLevel - The audio level.\n */\n _audioLevel(id, audioLevel) {\n\n // we do not process if p2p is not active\n // or audio level is under certain threshold\n // or if the audio level is for local audio track which is muted\n if (!this.conference.isP2PActive()\n || audioLevel <= SPEECH_DETECT_THRESHOLD\n || (id === this.myUserID\n && this.conference.getLocalAudioTrack().isMuted())) {\n return;\n }\n\n this.conference.rtc.eventEmitter.emit(\n RTCEvents.DOMINANT_SPEAKER_CHANGED,\n id);\n }\n}\n","/**\n * Adapter that creates AudioContext objects depending on the browser.\n *\n * @returns {AudioContext} - Return a new AudioContext or undefined if the browser does not support it.\n */\nexport function createAudioContext(options) {\n const AudioContextImpl = window.AudioContext || window.webkitAudioContext;\n\n if (!AudioContextImpl) {\n return undefined;\n }\n\n return new AudioContextImpl(options);\n}\n","import EventEmitter from 'events';\n\nimport RTC from '../RTC/RTC';\nimport { createAudioContext } from '../webaudio/WebAudioUtils';\n\nimport { VAD_SCORE_PUBLISHED } from './DetectionEvents';\n\n/**\n * Connects an audio JitsiLocalTrack to a vadProcessor using WebAudio ScriptProcessorNode.\n * Once an object is created audio from the local track flows through the ScriptProcessorNode as raw PCM.\n * The PCM is processed by the injected vad module and a voice activity detection score is obtained, the\n * score is published to consumers via an EventEmitter.\n * After work is done with this service the destroy method needs to be called for a proper cleanup.\n *\n * @fires VAD_SCORE_PUBLISHED\n */\nexport default class TrackVADEmitter extends EventEmitter {\n /**\n * Constructor.\n *\n * @param {number} procNodeSampleRate - Sample rate of the ScriptProcessorNode. Possible values 256, 512, 1024,\n * 2048, 4096, 8192, 16384. Passing other values will default to closes neighbor.\n * @param {Object} vadProcessor - VAD processor that allows us to calculate VAD score for PCM samples.\n * @param {JitsiLocalTrack} jitsiLocalTrack - JitsiLocalTrack corresponding to micDeviceId.\n */\n constructor(procNodeSampleRate, vadProcessor, jitsiLocalTrack) {\n super();\n\n /**\n * Sample rate of the ScriptProcessorNode.\n */\n this._procNodeSampleRate = procNodeSampleRate;\n\n /**\n * VAD Processor that allows us to calculate VAD score for PCM samples\n */\n this._vadProcessor = vadProcessor;\n\n /**\n * The JitsiLocalTrack instance.\n */\n this._localTrack = jitsiLocalTrack;\n\n /**\n * Buffer to hold residue PCM resulting after a ScriptProcessorNode callback\n */\n this._bufferResidue = new Float32Array([]);\n\n /**\n * The AudioContext instance with the preferred sample frequency.\n */\n this._audioContext = createAudioContext({ sampleRate: vadProcessor.getRequiredPCMFrequency() });\n\n /**\n * PCM Sample size expected by the VAD Processor instance. We cache it here as this value is used extensively,\n * saves a couple of function calls.\n */\n this._vadSampleSize = vadProcessor.getSampleLength();\n\n /**\n * Event listener function that will be called by the ScriptProcessNode with raw PCM data, depending on the set\n * sample rate.\n */\n this._onAudioProcess = this._onAudioProcess.bind(this);\n\n this._initializeAudioContext();\n }\n\n /**\n * Factory method that sets up all the necessary components for the creation of the TrackVADEmitter.\n *\n * @param {string} micDeviceId - Target microphone device id.\n * @param {number} procNodeSampleRate - Sample rate of the proc node.\n * @param {Object} vadProcessor -Module that calculates the voice activity score for a certain audio PCM sample.\n * The processor needs to implement the following functions:\n * - getSampleLength() - Returns the sample size accepted by getSampleLength.\n * - getRequiredPCMFrequency() - Returns the PCM frequency at which the processor operates.\n * - calculateAudioFrameVAD(pcmSample) - Process a 32 float pcm sample of getSampleLength size.\n * @returns {Promise} - Promise resolving in a new instance of TrackVADEmitter.\n */\n static create(micDeviceId, procNodeSampleRate, vadProcessor) {\n return RTC.obtainAudioAndVideoPermissions({\n devices: [ 'audio' ],\n micDeviceId\n }).then(localTrack => {\n // We only expect one audio track when specifying a device id.\n if (!localTrack[0]) {\n throw new Error(`Failed to create jitsi local track for device id: ${micDeviceId}`);\n }\n\n return new TrackVADEmitter(procNodeSampleRate, vadProcessor, localTrack[0]);\n\n // We have no exception handling at this point as there is nothing to clean up, the vadProcessor\n // life cycle is handled by whoever created this instance.\n });\n }\n\n /**\n * Sets up the audio graph in the AudioContext.\n *\n * @returns {void}\n */\n _initializeAudioContext() {\n this._audioSource = this._audioContext.createMediaStreamSource(this._localTrack.stream);\n\n // TODO AudioProcessingNode is deprecated in the web audio specifications and the recommended replacement\n // is audio worklet, however at the point of implementation AudioProcessingNode was still de de facto way\n // of achieving this functionality and supported in all major browsers as opposed to audio worklet which\n // was only available in Chrome. This todo is just a reminder that we should replace AudioProcessingNode\n // with audio worklet when it's mature enough and has more browser support.\n // We don't need stereo for determining the VAD score so we create a single channel processing node.\n this._audioProcessingNode = this._audioContext.createScriptProcessor(this._procNodeSampleRate, 1, 1);\n }\n\n /**\n * ScriptProcessorNode callback, the input parameters contains the PCM audio that is then sent to rnnoise.\n * Rnnoise only accepts PCM samples of 480 bytes whereas the webaudio processor node can't sample at a multiple\n * of 480 thus after each _onAudioProcess callback there will remain and PCM buffer residue equal\n * to _procNodeSampleRate / 480 which will be added to the next sample buffer and so on.\\\n *\n *\n * @param {AudioProcessingEvent} audioEvent - Audio event.\n * @returns {void}\n * @fires VAD_SCORE_PUBLISHED\n */\n _onAudioProcess(audioEvent) {\n // Prepend the residue PCM buffer from the previous process callback.\n const inData = audioEvent.inputBuffer.getChannelData(0);\n const completeInData = [ ...this._bufferResidue, ...inData ];\n const sampleTimestamp = Date.now();\n\n let i = 0;\n\n for (; i + this._vadSampleSize < completeInData.length; i += this._vadSampleSize) {\n const pcmSample = completeInData.slice(i, i + this._vadSampleSize);\n\n // The VAD processor might change the values inside the array so we make a copy.\n const vadScore = this._vadProcessor.calculateAudioFrameVAD(pcmSample.slice());\n\n this.emit(VAD_SCORE_PUBLISHED, {\n timestamp: sampleTimestamp,\n score: vadScore,\n pcmData: pcmSample,\n deviceId: this._localTrack.getDeviceId()\n });\n }\n\n this._bufferResidue = completeInData.slice(i, completeInData.length);\n }\n\n /**\n * Connects the nodes in the AudioContext to start the flow of audio data.\n *\n * @returns {void}\n */\n _connectAudioGraph() {\n this._audioProcessingNode.onaudioprocess = this._onAudioProcess;\n this._audioSource.connect(this._audioProcessingNode);\n this._audioProcessingNode.connect(this._audioContext.destination);\n }\n\n /**\n * Disconnects the nodes in the AudioContext.\n *\n * @returns {void}\n */\n _disconnectAudioGraph() {\n // Even thought we disconnect the processing node it seems that some callbacks remain queued,\n // resulting in calls with and uninitialized context.\n // eslint-disable-next-line no-empty-function\n this._audioProcessingNode.onaudioprocess = () => {};\n this._audioProcessingNode.disconnect();\n this._audioSource.disconnect();\n }\n\n /**\n * Cleanup potentially acquired resources.\n *\n * @returns {void}\n */\n _cleanupResources() {\n this._disconnectAudioGraph();\n this._localTrack.stopStream();\n }\n\n /**\n * Get the associated track device ID.\n *\n * @returns {string}\n */\n getDeviceId() {\n return this._localTrack.getDeviceId();\n }\n\n\n /**\n * Get the associated track label.\n *\n * @returns {string}\n */\n getTrackLabel() {\n return this._localTrack.getDeviceLabel();\n }\n\n /**\n * Start the emitter by connecting the audio graph.\n *\n * @returns {void}\n */\n start() {\n this._connectAudioGraph();\n }\n\n /**\n * Stops the emitter by disconnecting the audio graph.\n *\n * @returns {void}\n */\n stop() {\n this._disconnectAudioGraph();\n this._bufferResidue = [];\n }\n\n /**\n * Destroy TrackVADEmitter instance (release resources and stop callbacks).\n *\n * @returns {void}\n */\n destroy() {\n if (this._destroyed) {\n return;\n }\n\n this._cleanupResources();\n this._destroyed = true;\n }\n}\n","import { getLogger } from '@jitsi/logger';\nimport { EventEmitter } from 'events';\n\nimport * as JitsiConferenceEvents from '../../JitsiConferenceEvents';\n\nimport { VAD_SCORE_PUBLISHED, DETECTOR_STATE_CHANGE } from './DetectionEvents';\nimport TrackVADEmitter from './TrackVADEmitter';\n\nconst logger = getLogger(__filename);\n\n/**\n * Sample rate of TrackVADEmitter, it defines how many audio samples are processed at a time.\n * @type {number}\n */\nconst VAD_EMITTER_SAMPLE_RATE = 4096;\n\n/**\n * Connects a TrackVADEmitter to the target conference local audio track and manages various services that use\n * the data to produce audio analytics (VADTalkMutedDetection and VADNoiseDetection).\n */\nexport default class VADAudioAnalyser extends EventEmitter {\n /**\n * Creates VADAudioAnalyser\n * @param {JitsiConference} conference - JitsiConference instance that created us.\n * @param {Object} createVADProcessor - Function that creates a Voice activity detection processor. The processor\n * needs to implement the following functions:\n * - getSampleLength() - Returns the sample size accepted by getSampleLength.\n * - getRequiredPCMFrequency() - Returns the PCM frequency at which the processor operates.\n * - calculateAudioFrameVAD(pcmSample) - Process a 32 float pcm sample of getSampleLength size.\n * @constructor\n */\n constructor(conference, createVADProcessor) {\n super();\n\n /**\n * Member function that instantiates a VAD processor.\n */\n this._createVADProcessor = createVADProcessor;\n\n /**\n * Current {@link TrackVADEmitter}. VAD Emitter uses a {@link JitsiLocalTrack} and VAD processor to generate\n * period voice probability scores.\n */\n this._vadEmitter = null;\n\n /**\n * Current state of the _vadEmitter\n */\n this._isVADEmitterRunning = false;\n\n /**\n * Array of currently attached VAD processing services.\n */\n this._detectionServices = [];\n\n /**\n * Promise used to chain create and destroy operations associated with TRACK_ADDED and TRACK_REMOVED events\n * coming from the conference.\n * Because we have an async created component (VAD Processor) we need to make sure that it's initialized before\n * we destroy it ( when changing the device for instance), or when we use it from an external point of entry\n * i.e. (TRACK_MUTE_CHANGED event callback).\n */\n this._vadInitTracker = Promise.resolve();\n\n /**\n * Listens for {@link TrackVADEmitter} events and processes them.\n */\n this._processVADScore = this._processVADScore.bind(this);\n\n conference.on(JitsiConferenceEvents.TRACK_ADDED, this._trackAdded.bind(this));\n conference.on(JitsiConferenceEvents.TRACK_REMOVED, this._trackRemoved.bind(this));\n conference.on(JitsiConferenceEvents.TRACK_MUTE_CHANGED, this._trackMuteChanged.bind(this));\n }\n\n /**\n * Attach a VAD detector service to the analyser and handle it's state changes.\n *\n * @param {Object} vadTMDetector\n */\n addVADDetectionService(vadService) {\n this._detectionServices.push(vadService);\n vadService.on(DETECTOR_STATE_CHANGE, () => {\n // When the state of a detector changes check if there are any active detectors attached so that\n // the _vadEmitter doesn't run needlessly.\n const activeDetector = this._detectionServices.filter(detector => detector.isActive() === true);\n\n // If there are no active detectors running and the vadEmitter is running then stop the emitter as it is\n // uses a considerable amount of CPU. Otherwise start the service if it's stopped and there is a detector\n // that needs it.\n if (!activeDetector.length && this._isVADEmitterRunning) {\n this._stopVADEmitter();\n } else if (!this._isVADEmitterRunning) {\n this._startVADEmitter();\n }\n });\n }\n\n /**\n * Start the {@link TrackVADEmitter} and attach the event listener.\n * @returns {void}\n */\n _startVADEmitter() {\n if (this._vadEmitter) {\n this._vadEmitter.on(VAD_SCORE_PUBLISHED, this._processVADScore);\n this._vadEmitter.start();\n this._isVADEmitterRunning = true;\n }\n }\n\n /**\n * Stop the {@link TrackVADEmitter} and detach the event listener.\n * @returns {void}\n */\n _stopVADEmitter() {\n if (this._vadEmitter) {\n this._vadEmitter.removeListener(VAD_SCORE_PUBLISHED, this._processVADScore);\n this._vadEmitter.stop();\n }\n this._isVADEmitterRunning = false;\n }\n\n /**\n * Listens for {@link TrackVADEmitter} events and directs them to attached services as needed.\n *\n * @param {Object} vadScore -VAD score emitted by {@link TrackVADEmitter}\n * @param {Date} vadScore.timestamp - Exact time at which processed PCM sample was generated.\n * @param {number} vadScore.score - VAD score on a scale from 0 to 1 (i.e. 0.7)\n * @param {Float32Array} pcmData - Raw PCM data with which the VAD score was calculated.\n * @param {string} vadScore.deviceId - Device id of the associated track.\n * @listens VAD_SCORE_PUBLISHED\n */\n _processVADScore(vadScore) {\n for (const detector of this._detectionServices) {\n detector.processVADScore(vadScore);\n }\n }\n\n /**\n * Change the isMuted state of all attached detection services.\n *\n * @param {boolean} isMuted\n */\n _changeDetectorsMuteState(isMuted) {\n for (const detector of this._detectionServices) {\n detector.changeMuteState(isMuted);\n }\n }\n\n /**\n * Notifies the detector that a track was added to the associated {@link JitsiConference}.\n * Only take into account local audio tracks.\n * @param {JitsiTrack} track - The added track.\n * @returns {void}\n * @listens TRACK_ADDED\n */\n _trackAdded(track) {\n if (track.isLocalAudioTrack()) {\n // Keep a track promise so we take into account successive TRACK_ADD events being generated so that we\n // destroy/create the processing context in the proper order.\n this._vadInitTracker = this._vadInitTracker.then(() => this._createVADProcessor())\n .then(vadProcessor =>\n TrackVADEmitter.create(track.getDeviceId(), VAD_EMITTER_SAMPLE_RATE, vadProcessor)\n )\n .then(vadEmitter => {\n logger.debug('Created VAD emitter for track: ', track.getTrackLabel());\n\n this._vadEmitter = vadEmitter;\n\n // Iterate through the detection services and set their appropriate mute state, depending on\n // service this will trigger a DETECTOR_STATE_CHANGE which in turn might start the _vadEmitter.\n this._changeDetectorsMuteState(track.isMuted());\n })\n .catch(error => {\n logger.warn('Failed to start VADAudioAnalyser', error);\n });\n }\n }\n\n /**\n * Notifies the detector that the mute state of a {@link JitsiConference} track has changed. Only takes into account\n * local audio tracks.\n * @param {JitsiTrack} track - The track whose mute state has changed.\n * @returns {void}\n * @listens TRACK_MUTE_CHANGED\n */\n _trackMuteChanged(track) {\n if (track.isLocalAudioTrack()) {\n // On a mute toggle reset the state.\n this._vadInitTracker = this._vadInitTracker.then(() => {\n // Set mute status for the attached detection services.\n this._changeDetectorsMuteState(track.isMuted());\n });\n }\n }\n\n /**\n * Notifies the detector that a track associated with the {@link JitsiConference} was removed. Only takes into\n * account local audio tracks. Cleans up resources associated with the track and resets the processing context.\n *\n * @param {JitsiTrack} track - The removed track.\n * @returns {void}\n * @listens TRACK_REMOVED\n */\n _trackRemoved(track) {\n if (track.isLocalAudioTrack()) {\n // Use the promise to make sure operations are in sequence.\n this._vadInitTracker = this._vadInitTracker.then(() => {\n logger.debug('Removing track from VAD detection - ', track.getTrackLabel());\n\n // Track was removed, clean up and set appropriate states.\n if (this._vadEmitter) {\n this._stopVADEmitter();\n this._vadEmitter.destroy();\n this._vadEmitter = null;\n }\n\n // Reset state of detectors when active track is removed.\n for (const detector of this._detectionServices) {\n detector.reset();\n }\n });\n }\n }\n\n\n}\n","import { EventEmitter } from 'events';\n\nimport { calculateAverage, filterPositiveValues } from '../util/MathUtil';\n\nimport { VAD_NOISY_DEVICE, DETECTOR_STATE_CHANGE } from './DetectionEvents';\n\n/**\n * The average value VAD needs to be under over a period of time to be considered noise.\n * @type {number}\n */\nconst VAD_NOISE_AVG_THRESHOLD = 0.2;\n\n/**\n * The average values that audio input need to be over to be considered loud.\n * @type {number}\n */\nconst NOISY_AUDIO_LEVEL_THRESHOLD = 0.040;\n\n/**\n * The value that a VAD score needs to be under in order for processing to begin.\n * @type {number}\n */\nconst VAD_SCORE_TRIGGER = 0.2;\n\n/**\n * The value that a VAD score needs to be under in order for processing to begin.\n * @type {number}\n */\nconst AUDIO_LEVEL_SCORE_TRIGGER = 0.020;\n\n/**\n * Time span over which we calculate an average score used to determine if we trigger the event.\n * @type {number}\n */\nconst PROCESS_TIME_FRAME_SPAN_MS = 1500;\n\n/**\n * Detect if provided VAD score and PCM data is considered noise.\n */\nexport default class VADNoiseDetection extends EventEmitter {\n /**\n * Creates VADNoiseDetection\n *\n * @constructor\n */\n constructor() {\n super();\n\n /**\n * Flag which denotes the current state of the detection service i.e.if there is already a processing operation\n * ongoing.\n */\n this._processing = false;\n\n /**\n * Buffer that keeps the VAD scores for a period of time.\n */\n this._scoreArray = [];\n\n /**\n * Buffer that keeps audio level samples for a period of time.\n */\n this._audioLvlArray = [];\n\n /**\n * Current state of the service, if it's not active no processing will occur.\n */\n this._active = false;\n\n this._calculateNoisyScore = this._calculateNoisyScore.bind(this);\n }\n\n /**\n * Compute cumulative VAD score and PCM audio levels once the PROCESS_TIME_FRAME_SPAN_MS timeout has elapsed.\n * If the score is above the set threshold fire the event.\n * @returns {void}\n * @fires VAD_NOISY_DEVICE\n */\n _calculateNoisyScore() {\n const scoreAvg = calculateAverage(this._scoreArray);\n const audioLevelAvg = calculateAverage(this._audioLvlArray);\n\n if (scoreAvg < VAD_NOISE_AVG_THRESHOLD && audioLevelAvg > NOISY_AUDIO_LEVEL_THRESHOLD) {\n this.emit(VAD_NOISY_DEVICE);\n\n this._setActiveState(false);\n }\n\n // We reset the context in case a new process phase needs to be triggered.\n this.reset();\n }\n\n /**\n * Record the vad score and average volume in the appropriate buffers.\n *\n * @param {number} vadScore\n * @param {number} avgAudioLvl - average audio level of the PCM sample associated with the VAD score.s\n */\n _recordValues(vadScore, avgAudioLvl) {\n this._scoreArray.push(vadScore);\n this._audioLvlArray.push(avgAudioLvl);\n }\n\n /**\n * Set the active state of the detection service and notify any listeners.\n *\n * @param {boolean} active\n * @fires DETECTOR_STATE_CHANGE\n */\n _setActiveState(active) {\n this._active = active;\n this.emit(DETECTOR_STATE_CHANGE, this._active);\n }\n\n /**\n * Change the state according to the muted status of the tracked device.\n *\n * @param {boolean} isMuted - Is the device muted or not.\n */\n changeMuteState(isMuted) {\n // This service only needs to run when the microphone is not muted.\n this._setActiveState(!isMuted);\n this.reset();\n }\n\n /**\n * Check whether or not the service is active or not.\n *\n * @returns {boolean}\n */\n isActive() {\n return this._active;\n }\n\n /**\n * Reset the processing context, clear buffers, cancel the timeout trigger.\n *\n * @returns {void}\n */\n reset() {\n this._processing = false;\n this._scoreArray = [];\n this._audioLvlArray = [];\n clearTimeout(this._processTimeout);\n }\n\n /**\n * Listens for {@link TrackVADEmitter} events and processes them.\n *\n * @param {Object} vadScore -VAD score emitted by {@link TrackVADEmitter}\n * @param {Date} vadScore.timestamp - Exact time at which processed PCM sample was generated.\n * @param {number} vadScore.score - VAD score on a scale from 0 to 1 (i.e. 0.7)\n * @param {Float32Array} vadScore.pcmData - Raw PCM Data associated with the VAD score.\n * @param {string} vadScore.deviceId - Device id of the associated track.\n * @listens VAD_SCORE_PUBLISHED\n */\n processVADScore(vadScore) {\n if (!this._active) {\n return;\n }\n\n // There is a processing phase on going, add score to buffer array.\n if (this._processing) {\n // Filter and calculate sample average so we don't have to process one large array at a time.\n const posAudioLevels = filterPositiveValues(vadScore.pcmData);\n\n this._recordValues(vadScore.score, calculateAverage(posAudioLevels));\n\n return;\n }\n\n // If the VAD score for the sample is low and audio level has a high enough level we can start listening for\n // noise\n if (vadScore.score < VAD_SCORE_TRIGGER) {\n const posAudioLevels = filterPositiveValues(vadScore.pcmData);\n const avgAudioLvl = calculateAverage(posAudioLevels);\n\n if (avgAudioLvl > AUDIO_LEVEL_SCORE_TRIGGER) {\n this._processing = true;\n this._recordValues(vadScore.score, avgAudioLvl);\n\n // Once the preset timeout executes the final score will be calculated.\n this._processTimeout = setTimeout(this._calculateNoisyScore, PROCESS_TIME_FRAME_SPAN_MS);\n }\n }\n }\n}\n","import { EventEmitter } from 'events';\n\nimport { calculateAverage } from '../util/MathUtil';\n\nimport { VAD_TALK_WHILE_MUTED, DETECTOR_STATE_CHANGE } from './DetectionEvents';\n\n\n/**\n * The threshold which the average VAD values for a span of time needs to exceed to trigger an event.\n * @type {number}\n */\nconst VAD_AVG_THRESHOLD = 0.6;\n\n/**\n * The VAD score needed to trigger the processing algorithm, i.e. if a sample has the VAD score >= VAD_VOICE_LEVEL\n * we start processing all scores for a time span defined by const PROCESS_TIME_FRAME_SPAN_MS.\n * @type {number}\n */\nconst VAD_VOICE_LEVEL = 0.9;\n\n/**\n * Sample rate of TrackVADEmitter, it defines how many audio samples are processed at a time.\n * @type {number}\n */\n\n/**\n * Time span over which we calculate an average score used to determine if we trigger the event.\n * @type {number}\n */\nconst PROCESS_TIME_FRAME_SPAN_MS = 700;\n\n/**\n * Detect if provided VAD score which is generated on a muted device is voice and fires an event.\n */\nexport default class VADTalkMutedDetection extends EventEmitter {\n /**\n * Creates VADTalkMutedDetection\n * @constructor\n */\n constructor() {\n super();\n\n /**\n * Flag which denotes the current state of the detection service i.e.if there is already a processing operation\n * ongoing.\n */\n this._processing = false;\n\n /**\n * Buffer that keeps the VAD scores for a period of time.\n */\n this._scoreArray = [];\n\n /**\n * Current mute state of the audio track being monitored.\n */\n this._active = false;\n\n this._calculateVADScore = this._calculateVADScore.bind(this);\n }\n\n /**\n * Compute cumulative VAD score function called once the PROCESS_TIME_FRAME_SPAN_MS timeout has elapsed.\n * @returns {void}\n * @fires VAD_TALK_WHILE_MUTED\n */\n _calculateVADScore() {\n const score = calculateAverage(this._scoreArray);\n\n if (score > VAD_AVG_THRESHOLD) {\n this.emit(VAD_TALK_WHILE_MUTED);\n\n // Event was fired. Stop event emitter and remove listeners so no residue events kick off after this point\n // and a single VAD_TALK_WHILE_MUTED is generated per mic muted state.\n this._setActiveState(false);\n }\n\n // We reset the context in case a new process phase needs to be triggered.\n this.reset();\n }\n\n /**\n * Set the active state of the detection service and notify any listeners.\n *\n * @param {boolean} active\n * @fires DETECTOR_STATE_CHANGE\n */\n _setActiveState(active) {\n this._active = active;\n this.emit(DETECTOR_STATE_CHANGE, this._active);\n }\n\n /**\n * Change the state according to the muted status of the tracked device.\n *\n * @param {boolean} isMuted - Is the device muted or not.\n */\n changeMuteState(isMuted) {\n // This service only needs to run when the microphone is muted.\n this._setActiveState(isMuted);\n this.reset();\n }\n\n /**\n * Check whether or not the service is active or not.\n *\n * @returns {boolean}\n */\n isActive() {\n return this._active;\n }\n\n /**\n * Listens for {@link TrackVADEmitter} events and processes them.\n *\n * @param {Object} vadScore -VAD score emitted by {@link TrackVADEmitter}\n * @param {Date} vadScore.timestamp - Exact time at which processed PCM sample was generated.\n * @param {number} vadScore.score - VAD score on a scale from 0 to 1 (i.e. 0.7)\n * @param {string} vadScore.deviceId - Device id of the associated track.\n * @listens VAD_SCORE_PUBLISHED\n */\n processVADScore(vadScore) {\n if (!this._active) {\n return;\n }\n\n // There is a processing phase on going, add score to buffer array.\n if (this._processing) {\n this._scoreArray.push(vadScore.score);\n\n return;\n }\n\n // Because we remove all listeners on the vadEmitter once the main event is triggered,\n // there is no need to check for rogue events.\n if (vadScore.score > VAD_VOICE_LEVEL) {\n this._processing = true;\n this._scoreArray.push(vadScore.score);\n\n // Start gathering VAD scores for the configured period of time.\n this._processTimeout = setTimeout(this._calculateVADScore, PROCESS_TIME_FRAME_SPAN_MS);\n }\n }\n\n /**\n * Reset the processing context, clear buffer, cancel the timeout trigger.\n *\n * @returns {void}\n */\n reset() {\n this._processing = false;\n this._scoreArray = [];\n clearTimeout(this._processTimeout);\n }\n}\n","export enum E2ePingEvents {\n /**\n * Indicates that the end-to-end round-trip-time for a participant has changed.\n */\n E2E_RTT_CHANGED = 'e2eping.e2e_rtt_changed'\n};\n\n// exported for backward compatibility\nexport const E2E_RTT_CHANGED = E2ePingEvents.E2E_RTT_CHANGED;\n","import { getLogger } from '@jitsi/logger';\n\nimport * as JitsiConferenceEvents from '../../JitsiConferenceEvents';\nimport * as E2ePingEvents\n from '../../service/e2eping/E2ePingEvents';\nimport { createE2eRttEvent } from '../../service/statistics/AnalyticsEvents';\nimport Statistics from '../statistics/statistics';\n\nconst logger = getLogger(__filename);\n\n/**\n * The 'type' of a message which designates an e2e ping request.\n * @type {string}\n */\nconst E2E_PING_REQUEST = 'e2e-ping-request';\n\n/**\n * The 'type' of a message which designates an e2e ping response.\n * @type {string}\n */\nconst E2E_PING_RESPONSE = 'e2e-ping-response';\n\n/**\n * Saves e2e ping related state for a single JitsiParticipant.\n */\nclass ParticipantWrapper {\n /**\n * Creates a ParticipantWrapper\n * @param {JitsiParticipant} participant - The remote participant that this\n * object wraps.\n * @param {E2ePing} e2eping\n */\n constructor(participant, e2eping) {\n // The JitsiParticipant\n this.participant = participant;\n\n // The E2ePing\n this.e2eping = e2eping;\n\n // Caches the ID\n this.id = participant.getId();\n\n // Recently sent requests\n this.requests = {};\n\n // The ID of the last sent request. We just increment it for each new\n // request. Start at 1 so we can consider only thruthy values valid.\n this.lastRequestId = 1;\n\n this.clearIntervals = this.clearIntervals.bind(this);\n this.sendRequest = this.sendRequest.bind(this);\n this.handleResponse = this.handleResponse.bind(this);\n this.maybeSendAnalytics = this.maybeSendAnalytics.bind(this);\n this.sendAnalytics = this.sendAnalytics.bind(this);\n\n // If the data channel was already open (this is likely a participant\n // joining an existing conference) send a request immediately.\n if (e2eping.isDataChannelOpen) {\n this.sendRequest();\n }\n\n this.pingInterval = window.setInterval(\n this.sendRequest, e2eping.pingIntervalMs);\n this.analyticsInterval = window.setTimeout(\n this.maybeSendAnalytics, this.e2eping.analyticsIntervalMs);\n }\n\n /**\n * Clears the interval which sends pings.\n * @type {*}\n */\n clearIntervals() {\n if (this.pingInterval) {\n window.clearInterval(this.pingInterval);\n }\n if (this.analyticsInterval) {\n window.clearInterval(this.analyticsInterval);\n }\n }\n\n /**\n * Sends the next ping request.\n * @type {*}\n */\n sendRequest() {\n const requestId = this.lastRequestId++;\n const requestMessage = {\n type: E2E_PING_REQUEST,\n id: requestId\n };\n\n this.e2eping.sendMessage(requestMessage, this.id);\n this.requests[requestId] = {\n id: requestId,\n timeSent: window.performance.now()\n };\n }\n\n /**\n * Handles a response from this participant.\n * @type {*}\n */\n handleResponse(response) {\n const request = this.requests[response.id];\n\n if (request) {\n request.rtt = window.performance.now() - request.timeSent;\n this.e2eping.eventEmitter.emit(\n E2ePingEvents.E2E_RTT_CHANGED,\n this.participant,\n request.rtt);\n }\n\n this.maybeSendAnalytics();\n }\n\n /**\n * Goes over the requests, clearing ones which we don't need anymore, and\n * if it finds at least one request with a valid RTT in the last\n * 'analyticsIntervalMs' then sends an analytics event.\n * @type {*}\n */\n maybeSendAnalytics() {\n const now = window.performance.now();\n\n // The RTT we'll report is the minimum RTT measured in the last\n // analyticsInterval\n let rtt = Infinity;\n let request, requestId;\n\n // It's time to send analytics. Clean up all requests and find the\n for (requestId in this.requests) {\n if (this.requests.hasOwnProperty(requestId)) {\n request = this.requests[requestId];\n\n if (request.timeSent < now - this.e2eping.analyticsIntervalMs) {\n // An old request. We don't care about it anymore.\n delete this.requests[requestId];\n } else if (request.rtt) {\n rtt = Math.min(rtt, request.rtt);\n }\n }\n }\n\n if (rtt < Infinity) {\n this.sendAnalytics(rtt);\n }\n }\n\n /**\n * Sends an analytics event for this participant with the given RTT.\n * @type {*}\n */\n sendAnalytics(rtt) {\n Statistics.sendAnalytics(createE2eRttEvent(\n this.id,\n this.participant.getProperty('region'),\n rtt));\n }\n}\n\n/**\n * Implements end-to-end ping (from one conference participant to another) via\n * the jitsi-videobridge channel (either WebRTC data channel or web socket).\n *\n * TODO: use a broadcast message instead of individual pings to each remote\n * participant.\n *\n * This class:\n * 1. Sends periodic ping requests to all other participants in the\n * conference.\n * 2. Responds to ping requests from other participants.\n * 3. Fires events with the end-to-end RTT to each participant whenever a\n * response is received.\n * 4. Fires analytics events with the end-to-end RTT periodically.\n */\nexport default class E2ePing {\n /**\n * @param {JitsiConference} conference - The conference.\n * @param {Function} sendMessage - The function to use to send a message.\n * @param {Object} options\n */\n constructor(conference, options, sendMessage) {\n this.conference = conference;\n this.eventEmitter = conference.eventEmitter;\n this.sendMessage = sendMessage;\n\n // The interval at which pings will be sent (<= 0 disables sending).\n this.pingIntervalMs = 10000;\n\n // The interval at which analytics events will be sent.\n this.analyticsIntervalMs = 60000;\n\n // Maps a participant ID to its ParticipantWrapper\n this.participants = {};\n\n // Whether the WebRTC channel has been opened or not.\n this.isDataChannelOpen = false;\n\n if (options && options.e2eping) {\n if (typeof options.e2eping.pingInterval === 'number') {\n this.pingIntervalMs = options.e2eping.pingInterval;\n }\n if (typeof options.e2eping.analyticsInterval === 'number') {\n this.analyticsIntervalMs = options.e2eping.analyticsInterval;\n }\n\n // We want to report at most once a ping interval.\n if (this.analyticsIntervalMs > 0 && this.analyticsIntervalMs\n < this.pingIntervalMs) {\n this.analyticsIntervalMs = this.pingIntervalMs;\n }\n }\n logger.info(\n `Initializing e2e ping; pingInterval=${\n this.pingIntervalMs}, analyticsInterval=${\n this.analyticsIntervalMs}.`);\n\n this.participantJoined = this.participantJoined.bind(this);\n conference.on(\n JitsiConferenceEvents.USER_JOINED,\n this.participantJoined);\n\n this.participantLeft = this.participantLeft.bind(this);\n conference.on(\n JitsiConferenceEvents.USER_LEFT,\n this.participantLeft);\n\n this.messageReceived = this.messageReceived.bind(this);\n conference.on(\n JitsiConferenceEvents.ENDPOINT_MESSAGE_RECEIVED,\n this.messageReceived);\n\n this.dataChannelOpened = this.dataChannelOpened.bind(this);\n conference.on(\n JitsiConferenceEvents.DATA_CHANNEL_OPENED,\n this.dataChannelOpened);\n }\n\n /**\n * Notifies this instance that the communications channel has been opened\n * and it can now send messages via sendMessage.\n */\n dataChannelOpened() {\n this.isDataChannelOpen = true;\n\n // We don't want to wait the whole interval before sending the first\n // request, but we can't send it immediately after the participant joins\n // either, because our data channel might not have initialized.\n // So once the data channel initializes, send requests to everyone.\n // Wait an additional 200ms to give a chance to the remote side (if it\n // also just connected as is the case for the first 2 participants in a\n // conference) to open its data channel.\n for (const id in this.participants) {\n if (this.participants.hasOwnProperty(id)) {\n const participantWrapper = this.participants[id];\n\n window.setTimeout(participantWrapper.sendRequest, 200);\n }\n }\n }\n\n /**\n * Handles a message that was received.\n *\n * @param participant - The message sender.\n * @param payload - The payload of the message.\n */\n messageReceived(participant, payload) {\n // Listen to E2E PING requests and responses from other participants\n // in the conference.\n if (payload.type === E2E_PING_REQUEST) {\n this.handleRequest(participant.getId(), payload);\n } else if (payload.type === E2E_PING_RESPONSE) {\n this.handleResponse(participant.getId(), payload);\n }\n }\n\n /**\n * Handles a participant joining the conference. Starts to send ping\n * requests to the participant.\n *\n * @param {String} id - The ID of the participant.\n * @param {JitsiParticipant} participant - The participant that joined.\n */\n participantJoined(id, participant) {\n if (this.pingIntervalMs <= 0) {\n return;\n }\n\n if (this.participants[id]) {\n logger.info(\n `Participant wrapper already exists for ${id}. Clearing.`);\n this.participants[id].clearIntervals();\n delete this.participants[id];\n }\n\n this.participants[id] = new ParticipantWrapper(participant, this);\n }\n\n /**\n * Handles a participant leaving the conference. Stops sending requests.\n *\n * @param {String} id - The ID of the participant.\n */\n participantLeft(id) {\n if (this.pingIntervalMs <= 0) {\n return;\n }\n\n if (this.participants[id]) {\n this.participants[id].clearIntervals();\n delete this.participants[id];\n }\n }\n\n /**\n * Handles a ping request coming from another participant.\n *\n * @param {string} participantId - The ID of the participant who sent the\n * request.\n * @param {Object} request - The request.\n */\n handleRequest(participantId, request) {\n // If it's a valid request, just send a response.\n if (request && request.id) {\n const response = {\n type: E2E_PING_RESPONSE,\n id: request.id\n };\n\n this.sendMessage(response, participantId);\n } else {\n logger.info(\n `Received an invalid e2e ping request from ${participantId}.`);\n }\n }\n\n /**\n * Handles a ping response coming from another participant\n * @param {string} participantId - The ID of the participant who sent the\n * response.\n * @param {Object} response - The response.\n */\n handleResponse(participantId, response) {\n const participantWrapper = this.participants[participantId];\n\n if (participantWrapper) {\n participantWrapper.handleResponse(response);\n }\n }\n\n /**\n * Stops this E2ePing (i.e. stop sending requests).\n */\n stop() {\n logger.info('Stopping e2eping');\n\n this.conference.off(\n JitsiConferenceEvents.USER_JOINED,\n this.participantJoined);\n this.conference.off(\n JitsiConferenceEvents.USER_LEFT,\n this.participantLeft);\n this.conference.off(\n JitsiConferenceEvents.ENDPOINT_MESSAGE_RECEIVED,\n this.messageReceived);\n this.conference.off(\n JitsiConferenceEvents.DATA_CHANNEL_OPENED,\n this.dataChannelOpened);\n\n for (const id in this.participants) {\n if (this.participants.hasOwnProperty(id)) {\n this.participants[id].clearIntervals();\n }\n }\n\n this.participants = {};\n }\n}\n\n","import { getLogger } from '@jitsi/logger';\n\nimport * as JitsiConferenceEvents from '../../JitsiConferenceEvents';\n\nconst logger = getLogger(__filename);\n\n/**\n * Emits {@link JitsiConferenceEvents.JVB121_STATUS} events based on the current\n * P2P status and the conference participants count. See the event description\n * for more info.\n */\nexport default class Jvb121EventGenerator {\n /**\n * Creates new Jvb121EventGenerator for the given conference.\n * @param {JitsiConference} conference\n */\n constructor(conference) {\n this._conference = conference;\n\n /**\n * Indicates whether it's a one to one JVB conference (true)\n * or a multiparty (false). Will be also false if\n * the conference is currently in the P2P mode.\n * @type {boolean}\n * @private\n */\n this._jvb121 = true;\n\n this._conference.addEventListener(\n JitsiConferenceEvents.USER_JOINED, () => this.evaluateStatus());\n this._conference.addEventListener(\n JitsiConferenceEvents.USER_LEFT, () => this.evaluateStatus());\n this._conference.addEventListener(\n JitsiConferenceEvents.P2P_STATUS, () => this.evaluateStatus());\n }\n\n /**\n * Checks whether the JVB121 value should be updated and a new event\n * emitted.\n */\n evaluateStatus() {\n const oldStatus = this._jvb121;\n const newStatus\n = !this._conference.isP2PActive()\n && this._conference.getParticipantCount() <= 2;\n\n if (oldStatus !== newStatus) {\n this._jvb121 = newStatus;\n logger.debug(`JVB121 status ${oldStatus} => ${newStatus}`);\n this._conference.eventEmitter.emit(\n JitsiConferenceEvents.JVB121_STATUS, oldStatus, newStatus);\n }\n }\n}\n","import { getLogger } from '@jitsi/logger';\nimport isEqual from 'lodash.isequal';\n\nimport * as JitsiConferenceEvents from '../../JitsiConferenceEvents';\nimport FeatureFlags from '../flags/FeatureFlags';\n\nconst logger = getLogger(__filename);\nconst MAX_HEIGHT_ONSTAGE = 2160;\nconst MAX_HEIGHT_THUMBNAIL = 180;\nconst LASTN_UNLIMITED = -1;\n\n/**\n * This class translates the legacy signaling format between the client and the bridge (that affects bandwidth\n * allocation) to the new format described here https://github.com/jitsi/jitsi-videobridge/blob/master/doc/allocation.md\n */\nclass ReceiverVideoConstraints {\n /**\n * Creates a new instance.\n */\n constructor() {\n // Default constraints used for endpoints that are not explicitly included in constraints.\n // These constraints are used for endpoints that are thumbnails in the stage view.\n this._defaultConstraints = { 'maxHeight': MAX_HEIGHT_THUMBNAIL };\n\n // The number of videos requested from the bridge.\n this._lastN = LASTN_UNLIMITED;\n\n // The number representing the maximum video height the local client should receive from the bridge.\n this._maxFrameHeight = MAX_HEIGHT_ONSTAGE;\n\n // The endpoint IDs of the participants that are currently selected.\n this._selectedEndpoints = [];\n\n this._receiverVideoConstraints = {\n constraints: {},\n defaultConstraints: this.defaultConstraints,\n lastN: this._lastN,\n onStageEndpoints: [],\n selectedEndpoints: this._selectedEndpoints\n };\n }\n\n /**\n * Returns the receiver video constraints that need to be sent on the bridge channel.\n */\n get constraints() {\n this._receiverVideoConstraints.lastN = this._lastN;\n\n if (!this._selectedEndpoints.length) {\n return this._receiverVideoConstraints;\n }\n\n // The client is assumed to be in TileView if it has selected more than one endpoint, otherwise it is\n // assumed to be in StageView.\n this._receiverVideoConstraints.constraints = {};\n if (this._selectedEndpoints.length > 1) {\n /**\n * Tile view.\n * Only the default constraints are specified here along with lastN (if it is set).\n * {\n * 'colibriClass': 'ReceiverVideoConstraints',\n * 'defaultConstraints': { 'maxHeight': 360 }\n * }\n */\n this._receiverVideoConstraints.defaultConstraints = { 'maxHeight': this._maxFrameHeight };\n this._receiverVideoConstraints.onStageEndpoints = [];\n this._receiverVideoConstraints.selectedEndpoints = [];\n } else {\n /**\n * Stage view.\n * The participant on stage is specified in onStageEndpoints and a higher maxHeight is specified\n * for that endpoint while a default maxHeight of 180 is applied to all the other endpoints.\n * {\n * 'colibriClass': 'ReceiverVideoConstraints',\n * 'onStageEndpoints': ['A'],\n * 'defaultConstraints': { 'maxHeight': 180 },\n * 'constraints': {\n * 'A': { 'maxHeight': 720 }\n * }\n * }\n */\n this._receiverVideoConstraints.constraints[this._selectedEndpoints[0]] = {\n 'maxHeight': this._maxFrameHeight\n };\n this._receiverVideoConstraints.defaultConstraints = this._defaultConstraints;\n this._receiverVideoConstraints.onStageEndpoints = this._selectedEndpoints;\n this._receiverVideoConstraints.selectedEndpoints = [];\n }\n\n return this._receiverVideoConstraints;\n }\n\n /**\n * Updates the lastN field of the ReceiverVideoConstraints sent to the bridge.\n *\n * @param {number} value\n * @returns {boolean} Returns true if the the value has been updated, false otherwise.\n */\n updateLastN(value) {\n const changed = this._lastN !== value;\n\n if (changed) {\n this._lastN = value;\n logger.debug(`Updating ReceiverVideoConstraints lastN(${value})`);\n }\n\n return changed;\n }\n\n /**\n * Updates the resolution (height requested) in the contraints field of the ReceiverVideoConstraints\n * sent to the bridge.\n *\n * @param {number} maxFrameHeight\n * @requires {boolean} Returns true if the the value has been updated, false otherwise.\n */\n updateReceiveResolution(maxFrameHeight) {\n const changed = this._maxFrameHeight !== maxFrameHeight;\n\n if (changed) {\n this._maxFrameHeight = maxFrameHeight;\n logger.debug(`Updating receive maxFrameHeight: ${maxFrameHeight}`);\n }\n\n return changed;\n }\n\n /**\n * Updates the receiver constraints sent to the bridge.\n *\n * @param {Object} videoConstraints\n * @returns {boolean} Returns true if the the value has been updated, false otherwise.\n */\n updateReceiverVideoConstraints(videoConstraints) {\n const changed = !isEqual(this._receiverVideoConstraints, videoConstraints);\n\n if (changed) {\n this._receiverVideoConstraints = videoConstraints;\n logger.debug(`Updating ReceiverVideoConstraints ${JSON.stringify(videoConstraints)}`);\n }\n\n return changed;\n }\n\n /**\n * Updates the list of selected endpoints.\n *\n * @param {Array} ids\n * @returns {void}\n */\n updateSelectedEndpoints(ids) {\n logger.debug(`Updating selected endpoints: ${JSON.stringify(ids)}`);\n this._selectedEndpoints = ids;\n }\n}\n\n/**\n * This class manages the receive video contraints for a given {@link JitsiConference}. These constraints are\n * determined by the application based on how the remote video streams need to be displayed. This class is responsible\n * for communicating these constraints to the bridge over the bridge channel.\n */\nexport default class ReceiveVideoController {\n /**\n * Creates a new instance for a given conference.\n *\n * @param {JitsiConference} conference the conference instance for which the new instance will be managing\n * the receive video quality constraints.\n * @param {RTC} rtc the rtc instance which is responsible for initializing the bridge channel.\n */\n constructor(conference, rtc) {\n this._conference = conference;\n this._rtc = rtc;\n\n const { config } = conference.options;\n\n // The number of videos requested from the bridge, -1 represents unlimited or all available videos.\n this._lastN = config?.startLastN ?? (config?.channelLastN || LASTN_UNLIMITED);\n\n // The number representing the maximum video height the local client should receive from the bridge.\n this._maxFrameHeight = MAX_HEIGHT_ONSTAGE;\n\n // Enable new receiver constraints by default unless it is explicitly disabled through config.js.\n const useNewReceiverConstraints = config?.useNewBandwidthAllocationStrategy ?? true;\n\n if (useNewReceiverConstraints) {\n this._receiverVideoConstraints = new ReceiverVideoConstraints();\n const lastNUpdated = this._receiverVideoConstraints.updateLastN(this._lastN);\n\n lastNUpdated && this._rtc.setNewReceiverVideoConstraints(this._receiverVideoConstraints.constraints);\n } else {\n this._rtc.setLastN(this._lastN);\n }\n\n // The endpoint IDs of the participants that are currently selected.\n this._selectedEndpoints = [];\n\n this._conference.on(\n JitsiConferenceEvents._MEDIA_SESSION_STARTED,\n session => this._onMediaSessionStarted(session));\n }\n\n /**\n * Handles the {@link JitsiConferenceEvents.MEDIA_SESSION_STARTED}, that is when the conference creates new media\n * session. The preferred receive frameHeight is applied on the media session.\n *\n * @param {JingleSessionPC} mediaSession - the started media session.\n * @returns {void}\n * @private\n */\n _onMediaSessionStarted(mediaSession) {\n if (mediaSession.isP2P || !this._receiverVideoConstraints) {\n mediaSession.setReceiverVideoConstraint(this._maxFrameHeight);\n } else {\n this._receiverVideoConstraints.updateReceiveResolution(this._maxFrameHeight);\n this._rtc.setNewReceiverVideoConstraints(this._receiverVideoConstraints.constraints);\n }\n }\n\n /**\n * Returns the lastN value for the conference.\n *\n * @returns {number}\n */\n getLastN() {\n return this._lastN;\n }\n\n /**\n * Elects the participants with the given ids to be the selected participants in order to always receive video\n * for this participant (even when last n is enabled).\n *\n * @param {Array} ids - The user ids.\n * @returns {void}\n */\n selectEndpoints(ids) {\n this._selectedEndpoints = ids;\n\n if (this._receiverVideoConstraints) {\n // Filter out the local endpointId from the list of selected endpoints.\n const remoteEndpointIds = ids.filter(id => id !== this._conference.myUserId());\n const oldConstraints = JSON.parse(JSON.stringify(this._receiverVideoConstraints.constraints));\n\n remoteEndpointIds.length && this._receiverVideoConstraints.updateSelectedEndpoints(remoteEndpointIds);\n const newConstraints = this._receiverVideoConstraints.constraints;\n\n // Send bridge message only when the constraints change.\n if (!isEqual(newConstraints, oldConstraints)) {\n this._rtc.setNewReceiverVideoConstraints(newConstraints);\n }\n\n return;\n }\n this._rtc.selectEndpoints(ids);\n }\n\n /**\n * Selects a new value for \"lastN\". The requested amount of videos are going to be delivered after the value is\n * in effect. Set to -1 for unlimited or all available videos.\n *\n * @param {number} value the new value for lastN.\n * @returns {void}\n */\n setLastN(value) {\n if (this._lastN !== value) {\n this._lastN = value;\n\n if (this._receiverVideoConstraints) {\n const lastNUpdated = this._receiverVideoConstraints.updateLastN(value);\n\n // Send out the message on the bridge channel if lastN was updated.\n lastNUpdated && this._rtc.setNewReceiverVideoConstraints(this._receiverVideoConstraints.constraints);\n\n return;\n }\n this._rtc.setLastN(value);\n }\n }\n\n /**\n * Sets the maximum video resolution the local participant should receive from remote participants.\n *\n * @param {number|undefined} maxFrameHeight - the new value.\n * @returns {void}\n */\n setPreferredReceiveMaxFrameHeight(maxFrameHeight) {\n this._maxFrameHeight = maxFrameHeight;\n\n for (const session of this._conference.getMediaSessions()) {\n if (session.isP2P || !this._receiverVideoConstraints) {\n maxFrameHeight && session.setReceiverVideoConstraint(maxFrameHeight);\n } else {\n const resolutionUpdated = this._receiverVideoConstraints.updateReceiveResolution(maxFrameHeight);\n\n resolutionUpdated\n && this._rtc.setNewReceiverVideoConstraints(this._receiverVideoConstraints.constraints);\n }\n }\n }\n\n /**\n * Sets the receiver constraints for the conference.\n *\n * @param {Object} constraints The video constraints.\n */\n setReceiverConstraints(constraints) {\n if (!this._receiverVideoConstraints) {\n this._receiverVideoConstraints = new ReceiverVideoConstraints();\n }\n\n const isEndpointsFormat = Object.keys(constraints).includes('onStageEndpoints', 'selectedEndpoints');\n const isSourcesFormat = Object.keys(constraints).includes('onStageSources', 'selectedSources');\n\n if (!FeatureFlags.isSourceNameSignalingEnabled() && isSourcesFormat) {\n throw new Error(\n '\"onStageSources\" and \"selectedSources\" are not supported when sourceNameSignaling is disabled.'\n );\n }\n\n if (FeatureFlags.isSourceNameSignalingEnabled() && isEndpointsFormat) {\n throw new Error(\n '\"onStageEndpoints\" and \"selectedEndpoints\" are not supported when sourceNameSignaling is enabled.'\n );\n }\n const constraintsChanged = this._receiverVideoConstraints.updateReceiverVideoConstraints(constraints);\n\n if (constraintsChanged) {\n this._lastN = constraints.lastN ?? this._lastN;\n this._selectedEndpoints = constraints.selectedEndpoints ?? this._selectedEndpoints;\n this._rtc.setNewReceiverVideoConstraints(constraints);\n\n const p2pSession = this._conference.getMediaSessions().find(session => session.isP2P);\n\n if (p2pSession) {\n let maxFrameHeight = Object.values(constraints.constraints)[0]?.maxHeight;\n\n if (!maxFrameHeight) {\n maxFrameHeight = constraints.defaultConstraints?.maxHeight;\n }\n maxFrameHeight && p2pSession.setReceiverVideoConstraint(maxFrameHeight);\n }\n }\n }\n}\n","import { getLogger } from '@jitsi/logger';\n\nimport * as JitsiConferenceEvents from '../../JitsiConferenceEvents';\nimport RTCEvents from '../../service/RTC/RTCEvents';\nimport FeatureFlags from '../flags/FeatureFlags';\nimport MediaSessionEvents from '../xmpp/MediaSessionEvents';\n\nconst logger = getLogger(__filename);\nconst MAX_LOCAL_RESOLUTION = 2160;\n\n/**\n * The class manages send video constraints across media sessions({@link JingleSessionPC}) which belong to\n * {@link JitsiConference}. It finds the lowest common value, between the local user's send preference and\n * the remote party's receive preference. Also this module will consider only the active session's receive value,\n * because local tracks are shared and while JVB may have no preference, the remote p2p may have and they may be totally\n * different.\n */\nexport default class SendVideoController {\n /**\n * Creates new instance for a given conference.\n *\n * @param {JitsiConference} conference - the conference instance for which the new instance will be managing\n * the send video quality constraints.\n * @param {RTC} rtc - the rtc instance that is responsible for sending the messages on the bridge channel.\n */\n constructor(conference, rtc) {\n this._conference = conference;\n this._preferredSendMaxFrameHeight = MAX_LOCAL_RESOLUTION;\n this._rtc = rtc;\n\n /**\n * Source name based sender constraints.\n * @type {Map};\n */\n\n this._sourceSenderConstraints = new Map();\n this._conference.on(\n JitsiConferenceEvents._MEDIA_SESSION_STARTED,\n session => this._onMediaSessionStarted(session));\n this._conference.on(\n JitsiConferenceEvents._MEDIA_SESSION_ACTIVE_CHANGED,\n () => this._configureConstraintsForLocalSources());\n this._rtc.on(\n RTCEvents.SENDER_VIDEO_CONSTRAINTS_CHANGED,\n videoConstraints => this._onSenderConstraintsReceived(videoConstraints));\n }\n\n /**\n * Configures the video encodings on the local sources when a media connection is established or becomes active.\n *\n * @returns {Promise}\n * @private\n */\n _configureConstraintsForLocalSources() {\n if (FeatureFlags.isSourceNameSignalingEnabled()) {\n for (const track of this._rtc.getLocalVideoTracks()) {\n const sourceName = track.getSourceName();\n\n sourceName && this._propagateSendMaxFrameHeight(sourceName);\n }\n } else {\n this._propagateSendMaxFrameHeight();\n }\n }\n\n /**\n * Handles the {@link JitsiConferenceEvents.MEDIA_SESSION_STARTED}, that is when the conference creates new media\n * session. It doesn't mean it's already active though. For example the JVB connection may be created after\n * the conference has entered the p2p mode already.\n *\n * @param {JingleSessionPC} mediaSession - the started media session.\n * @private\n */\n _onMediaSessionStarted(mediaSession) {\n mediaSession.addListener(\n MediaSessionEvents.REMOTE_VIDEO_CONSTRAINTS_CHANGED,\n session => {\n if (session === this._conference.getActiveMediaSession()) {\n this._configureConstraintsForLocalSources();\n }\n });\n }\n\n /**\n * Propagates the video constraints if they have changed.\n *\n * @param {Object} videoConstraints - The sender video constraints received from the bridge.\n * @returns {Promise}\n * @private\n */\n _onSenderConstraintsReceived(videoConstraints) {\n if (FeatureFlags.isSourceNameSignalingEnabled()) {\n const { maxHeight, sourceName } = videoConstraints;\n const localVideoTracks = this._conference.getLocalVideoTracks() ?? [];\n\n for (const track of localVideoTracks) {\n // Propagate the sender constraint only if it has changed.\n if (track.getSourceName() === sourceName\n && (!this._sourceSenderConstraints.has(sourceName)\n || this._sourceSenderConstraints.get(sourceName) !== maxHeight)) {\n this._sourceSenderConstraints.set(sourceName, maxHeight);\n logger.debug(`Sender constraints for source:${sourceName} changed to maxHeight:${maxHeight}`);\n this._propagateSendMaxFrameHeight(sourceName);\n }\n }\n } else if (this._senderVideoConstraints?.idealHeight !== videoConstraints.idealHeight) {\n this._senderVideoConstraints = videoConstraints;\n this._propagateSendMaxFrameHeight();\n }\n }\n\n /**\n * Figures out the send video constraint as specified by {@link _selectSendMaxFrameHeight} and sets it on all media\n * sessions for the reasons mentioned in this class description.\n *\n * @param {string} sourceName - The source for which sender constraints have changed.\n * @returns {Promise}\n * @private\n */\n _propagateSendMaxFrameHeight(sourceName = null) {\n if (FeatureFlags.isSourceNameSignalingEnabled() && !sourceName) {\n throw new Error('sourceName missing for calculating the sendMaxHeight for video tracks');\n }\n const sendMaxFrameHeight = this._selectSendMaxFrameHeight(sourceName);\n const promises = [];\n\n if (sendMaxFrameHeight >= 0) {\n for (const session of this._conference.getMediaSessions()) {\n promises.push(session.setSenderVideoConstraint(sendMaxFrameHeight, sourceName));\n }\n }\n\n return Promise.all(promises);\n }\n\n /**\n * Selects the lowest common value for the local video send constraint by looking at local user's preference and\n * the active media session's receive preference set by the remote party.\n *\n * @param {string} sourceName - The source for which sender constraints have changed.\n * @returns {number|undefined}\n * @private\n */\n _selectSendMaxFrameHeight(sourceName = null) {\n if (FeatureFlags.isSourceNameSignalingEnabled() && !sourceName) {\n throw new Error('sourceName missing for calculating the sendMaxHeight for video tracks');\n }\n const activeMediaSession = this._conference.getActiveMediaSession();\n const remoteRecvMaxFrameHeight = activeMediaSession\n ? activeMediaSession.isP2P\n ? activeMediaSession.getRemoteRecvMaxFrameHeight()\n : sourceName ? this._sourceSenderConstraints.get(sourceName) : this._senderVideoConstraints?.idealHeight\n : undefined;\n\n if (this._preferredSendMaxFrameHeight >= 0 && remoteRecvMaxFrameHeight >= 0) {\n return Math.min(this._preferredSendMaxFrameHeight, remoteRecvMaxFrameHeight);\n } else if (remoteRecvMaxFrameHeight >= 0) {\n return remoteRecvMaxFrameHeight;\n }\n\n return this._preferredSendMaxFrameHeight;\n }\n\n /**\n * Sets local preference for max send video frame height.\n *\n * @param {number} maxFrameHeight - the new value to set.\n * @returns {Promise} - resolved when the operation is complete.\n */\n setPreferredSendMaxFrameHeight(maxFrameHeight) {\n this._preferredSendMaxFrameHeight = maxFrameHeight;\n\n if (FeatureFlags.isSourceNameSignalingEnabled()) {\n const promises = [];\n\n for (const sourceName of this._sourceSenderConstraints.keys()) {\n promises.push(this._propagateSendMaxFrameHeight(sourceName));\n }\n\n return Promise.allSettled(promises);\n }\n\n return this._propagateSendMaxFrameHeight();\n }\n}\n","/**\n * A collection of utility functions for taking in XML and parsing it to return\n * certain values.\n */\nexport default {\n /**\n * Parses the presence update of the focus and returns an object with the\n * statuses related to recording.\n *\n * @param {Node} presence - An XMPP presence update.\n * @returns {Object} The current presence values related to recording.\n */\n getFocusRecordingUpdate(presence) {\n const jibriStatus = presence\n && presence.getElementsByTagName('jibri-recording-status')[0];\n\n if (!jibriStatus) {\n return;\n }\n\n return {\n error: jibriStatus.getAttribute('failure_reason'),\n initiator: jibriStatus.getAttribute('initiator'),\n recordingMode: jibriStatus.getAttribute('recording_mode'),\n sessionID: jibriStatus.getAttribute('session_id'),\n status: jibriStatus.getAttribute('status')\n };\n },\n\n /**\n * Parses the presence update from a hidden domain participant and returns\n * an object with the statuses related to recording.\n *\n * @param {Node} presence - An XMPP presence update.\n * @returns {Object} The current presence values related to recording.\n */\n getHiddenDomainUpdate(presence) {\n const liveStreamViewURLContainer\n = presence.getElementsByTagName('live-stream-view-url')[0];\n const liveStreamViewURL = liveStreamViewURLContainer\n && liveStreamViewURLContainer.textContent;\n const modeContainer\n = presence.getElementsByTagName('mode')[0];\n const mode = modeContainer\n && modeContainer.textContent\n && modeContainer.textContent.toLowerCase();\n const sessionIDContainer\n = presence.getElementsByTagName('session_id')[0];\n const sessionID\n = sessionIDContainer && sessionIDContainer.textContent;\n\n return {\n liveStreamViewURL,\n mode,\n sessionID\n };\n },\n\n /**\n * Returns the recording session ID from a successful IQ.\n *\n * @param {Node} response - The response from the IQ.\n * @returns {string} The session ID of the recording session.\n */\n getSessionIdFromIq(response) {\n const jibri = response && response.getElementsByTagName('jibri')[0];\n\n return jibri && jibri.getAttribute('session_id');\n },\n\n /**\n * Returns the recording session ID from a presence, if it exists.\n *\n * @param {Node} presence - An XMPP presence update.\n * @returns {string|undefined} The session ID of the recording session.\n */\n getSessionId(presence) {\n const sessionIdContainer\n = presence.getElementsByTagName('session_id')[0];\n const sessionId = sessionIdContainer && sessionIdContainer.textContent;\n\n return sessionId;\n },\n\n /**\n * Returns whether or not a presence is from the focus.\n *\n * @param {Node} presence - An XMPP presence update.\n * @returns {boolean} True if the presence is from the focus.\n */\n isFromFocus(presence) {\n return presence.getAttribute('from').includes('focus');\n }\n};\n","import { $iq } from 'strophe.js';\n\nimport recordingXMLUtils from './recordingXMLUtils';\n\n/**\n * Represents a recording session.\n */\nexport default class JibriSession {\n /**\n * Initializes a new JibriSession instance.\n *\n * @constructor\n */\n constructor(options = {}) {\n this._connection = options.connection;\n this._mode = options.mode;\n\n this._setSessionID(options.sessionID);\n this.setStatus(options.status);\n }\n\n /**\n * Returns the error related to the session instance, if any.\n *\n * @returns {string|undefined}\n */\n getError() {\n return this._error;\n }\n\n /**\n * Returns the session ID of the session instance.\n *\n * @returns {string|undefined}\n */\n getID() {\n return this._sessionID;\n }\n\n /**\n * Returns the initiator of the session instance.\n *\n * @returns {JitsiParticipant|string} The participant that started the session.\n */\n getInitiator() {\n return this._initiator;\n }\n\n /**\n * Returns the streaming URL of the session.\n *\n * @returns {string|undefined}\n */\n getLiveStreamViewURL() {\n return this._liveStreamViewURL;\n }\n\n /**\n * Returns the current status of the session.\n *\n * @returns {string|undefined}\n */\n getStatus() {\n return this._status;\n }\n\n /**\n * Returns the jid of the participant that stopped the session.\n *\n * @returns {JitsiParticipant|string} The participant that stopped the session.\n */\n getTerminator() {\n return this._terminator;\n }\n\n /**\n * Returns the current recording mode of the session, such as \"file\".\n *\n * @returns {string}\n */\n getMode() {\n return this._mode;\n }\n\n /**\n * Sets the last known error message related to the session.\n *\n * @param {string} error - The error string explaining why the session\n * entered an error state.\n * @returns {void}\n */\n setError(error) {\n this._error = error;\n }\n\n /**\n * Sets the last live stream URL for the session instance. Usually this is\n * a YouTube URL and usually this is only set for \"stream\" sessions.\n *\n * @param {string} url - The live stream URL associated with the session.\n * @returns {void}\n */\n setLiveStreamViewURL(url) {\n this._liveStreamViewURL = url;\n }\n\n /**\n * Sets the last known status for this recording session.\n *\n * @param {string} status - The new status to set.\n * @returns {void}\n */\n setStatus(status) {\n this._status = status;\n }\n\n /**\n * Sets the participant that started the session.\n * @param {JitsiParticipant | string} participant - The participant or resource id\n * if local participant.\n */\n setInitiator(participant) {\n this._initiator = participant;\n }\n\n /**\n * Sets the participant that stopped the session.\n * @param {JitsiParticipant | string} participant - The participant or the resource id\n * if local participant.\n */\n setTerminator(participant) {\n this._terminator = participant;\n }\n\n /**\n * Sends a message to start the actual recording.\n *\n * @param {Object} options - Additional arguments for starting the\n * recording.\n * @param {string} [options.appData] - Data specific to the app/service that\n * the result file will be uploaded.\n * @param {string} [options.broadcastId] - The broadcast ID of an\n * associated YouTube stream, used for knowing the URL from which the stream\n * can be viewed.\n * @param {string} options.focusMucJid - The JID of the focus participant\n * that controls recording.\n * @param {streamId} options.streamId - Necessary for live streaming, this\n * is the stream key needed to start a live streaming session with the\n * streaming service provider.\n * @returns Promise\n */\n start({ appData, broadcastId, focusMucJid, streamId }) {\n return new Promise((resolve, reject) => {\n this._connection.sendIQ(\n this._createIQ({\n action: 'start',\n appData,\n focusMucJid,\n broadcastId,\n streamId\n }),\n result => {\n // All users will eventually receive the 'pending' status\n // from the backend, but for the user initiating the session\n // it's better to give some instant feedback that recording\n // is starting so fire 'pending' here manually.\n this.setStatus('pending');\n this._setSessionID(\n recordingXMLUtils.getSessionIdFromIq(result));\n\n resolve();\n },\n error => {\n this._setErrorFromIq(error);\n\n reject(error);\n });\n });\n }\n\n /**\n * Sends a message to actually stop the recording session.\n *\n * @param {Object} options - Additional arguments for stopping the\n * recording.\n * @param {Object} options.focusMucJid - The JID of the focus participant\n * that controls recording.\n * @returns Promise\n */\n stop({ focusMucJid }) {\n return new Promise((resolve, reject) => {\n this._connection.sendIQ(\n this._createIQ({\n action: 'stop',\n focusMucJid\n }),\n resolve,\n reject);\n });\n }\n\n /**\n * Generates the message to change the status of the recording session.\n *\n * @param {string} status - The new status to which the recording session\n * should transition.\n * @param {string} [options.appData] - Data specific to the app/service that\n * the result file will be uploaded.\n * @param {string} [options.broadcastId] - The broadcast ID of an\n * associated YouTube stream, used for knowing the URL from which the stream\n * can be viewed.\n * @param {string} options.focusMucJid - The JID of the focus participant\n * that controls recording.\n * @param {streamId} options.streamId - Necessary for live streaming, this\n * is the stream key needed to start a live streaming session with the\n * streaming service provider.\n * @returns Object - The XMPP IQ message.\n */\n _createIQ({ action, appData, broadcastId, focusMucJid, streamId }) {\n return $iq({\n to: focusMucJid,\n type: 'set'\n })\n .c('jibri', {\n 'xmlns': 'http://jitsi.org/protocol/jibri',\n 'action': action,\n 'app_data': appData,\n 'recording_mode': this._mode,\n 'streamid': streamId,\n 'you_tube_broadcast_id': broadcastId\n })\n .up();\n }\n\n /**\n * Handles the error from an iq and stores the error.\n *\n * @param {Node} errorIq - The error response from an Iq.\n * @private\n * @returns {void}\n */\n _setErrorFromIq(errorIq) {\n const error = errorIq.getElementsByTagName('error')[0];\n\n this.setError(error.children[0].tagName);\n }\n\n /**\n * Sets the known session ID for this recording session.\n *\n * @param {string} sessionID\n * @private\n * @returns {void}\n */\n _setSessionID(sessionID) {\n this._sessionID = sessionID;\n }\n}\n","import { getLogger } from '@jitsi/logger';\n\nimport XMPPEvents from '../../service/xmpp/XMPPEvents';\n\nimport JibriSession from './JibriSession';\nimport recordingXMLUtils from './recordingXMLUtils';\n\nconst logger = getLogger(__filename);\n\n/**\n * A class responsible for starting and stopping recording sessions and emitting\n * state updates for them.\n */\nclass RecordingManager {\n /**\n * Initialize {@code RecordingManager} with other objects that are necessary\n * for starting a recording.\n *\n * @param {ChatRoom} chatRoom - The chat room to handle.\n * @returns {void}\n */\n constructor(chatRoom) {\n /**\n * All known recording sessions from the current conference.\n */\n this._sessions = {};\n\n this._chatRoom = chatRoom;\n\n this.onPresence = this.onPresence.bind(this);\n\n this._chatRoom.eventEmitter.addListener(\n XMPPEvents.PRESENCE_RECEIVED, this.onPresence);\n }\n\n /**\n * Finds an existing recording session by session ID.\n *\n * @param {string} sessionID - The session ID associated with the recording.\n * @returns {JibriSession|undefined}\n */\n getSession(sessionID) {\n return this._sessions[sessionID];\n }\n\n /**\n * Callback to invoke to parse through a presence update to find recording\n * related updates (from Jibri participant doing the recording and the\n * focus which controls recording).\n *\n * @param {Object} event - The presence data from the pubsub event.\n * @param {Node} event.presence - An XMPP presence update.\n * @param {boolean} event.fromHiddenDomain - Whether or not the update comes\n * from a participant that is trusted but not visible, as would be the case\n * with the Jibri recorder participant.\n * @returns {void}\n */\n onPresence({ fromHiddenDomain, presence }) {\n if (recordingXMLUtils.isFromFocus(presence)) {\n this._handleFocusPresence(presence);\n } else if (fromHiddenDomain) {\n this._handleJibriPresence(presence);\n }\n }\n\n /**\n * Start a recording session.\n *\n * @param {Object} options - Configuration for the recording.\n * @param {string} [options.appData] - Data specific to the app/service that\n * the result file will be uploaded.\n * @param {string} [optional] options.broadcastId - The channel on which a\n * live stream will occur.\n * @param {string} options.mode - The mode in which recording should be\n * started. Recognized values are \"file\" and \"stream\".\n * @param {string} [optional] options.streamId - The stream key to be used\n * for live stream broadcasting. Required for live streaming.\n * @returns {Promise} A promise for starting a recording, which will pass\n * back the session on success. The promise resolves after receiving an\n * acknowledgment of the start request success or fail.\n */\n startRecording(options) {\n const session = new JibriSession({\n ...options,\n connection: this._chatRoom.connection\n });\n\n return session.start({\n appData: options.appData,\n broadcastId: options.broadcastId,\n focusMucJid: this._chatRoom.focusMucJid,\n streamId: options.streamId\n })\n .then(() => {\n // Only store the session and emit if the session has not been\n // added already. This is a workaround for the session getting\n // created due to a presence update to announce a \"pending\"\n // recording being received before JibriSession#start finishes.\n if (!this.getSession(session.getID())) {\n this._addSession(session);\n this._emitSessionUpdate(session);\n }\n\n return session;\n })\n .catch(error => {\n this._emitSessionUpdate(session);\n\n return Promise.reject(error);\n });\n }\n\n /**\n * Stop a recording session.\n *\n * @param {string} sessionID - The ID associated with the recording session\n * to be stopped.\n * @returns {Promise} The promise resolves after receiving an\n * acknowledgment of the stop request success or fail.\n */\n stopRecording(sessionID) {\n const session = this.getSession(sessionID);\n\n if (session) {\n return session.stop({ focusMucJid: this._chatRoom.focusMucJid });\n }\n\n return Promise.reject(new Error('Could not find session'));\n }\n\n /**\n * Stores a reference to the passed in JibriSession.\n *\n * @param {string} session - The JibriSession instance to store.\n * @returns {void}\n */\n _addSession(session) {\n this._sessions[session.getID()] = session;\n }\n\n /**\n * Create a new instance of a recording session and stores a reference to\n * it.\n *\n * @param {string} sessionID - The session ID of the recording in progress.\n * @param {string} status - The current status of the recording session.\n * @param {string} mode - The recording mode of the session.\n * @returns {JibriSession}\n */\n _createSession(sessionID, status, mode) {\n const session = new JibriSession({\n connection: this._chatRoom.connection,\n focusMucJid: this._chatRoom.focusMucJid,\n mode,\n sessionID,\n status\n });\n\n this._addSession(session);\n\n return session;\n }\n\n /**\n * Notifies listeners of an update to a recording session.\n *\n * @param {JibriSession} session - The session that has been updated.\n * @param {string|undefined} initiator - The jid of the initiator of the update.\n */\n _emitSessionUpdate(session, initiator) {\n this._chatRoom.eventEmitter.emit(\n XMPPEvents.RECORDER_STATE_CHANGED, session, initiator);\n }\n\n /**\n * Parses presence to update an existing JibriSession or to create a new\n * JibriSession.\n *\n * @param {Node} presence - An XMPP presence update.\n * @returns {void}\n */\n _handleFocusPresence(presence) {\n const jibriStatus = recordingXMLUtils.getFocusRecordingUpdate(presence);\n\n if (!jibriStatus) {\n return;\n }\n\n const { error, initiator, recordingMode, sessionID, status } = jibriStatus;\n\n // We'll look for an existing session or create one (in case we're a\n // participant joining a call with an existing recording going on).\n let session = this.getSession(sessionID);\n\n // Handle the case where a status update is received in presence but\n // the local participant has joined while the JibriSession has already\n // ended.\n if (!session && status === 'off') {\n logger.warn(\n 'Ignoring recording presence update',\n 'Received a new session with status off.');\n\n return;\n }\n\n // Jicofo sends updates via presence, and any extension in presence\n // is sent until it is explicitly removed. It's difficult for\n // Jicofo to know when a presence has been sent once, so it won't\n // remove jibri status extension. This means we may receive the same\n // status update more than once, so check for that here\n if (session\n && session.getStatus() === status\n && session.getError() === error) {\n logger.warn('Ignoring duplicate presence update: ',\n JSON.stringify(jibriStatus));\n\n return;\n }\n\n if (!session) {\n session = this._createSession(sessionID, status, recordingMode);\n }\n\n session.setStatus(status);\n\n if (error) {\n session.setError(error);\n }\n\n this._emitSessionUpdate(session, initiator);\n }\n\n /**\n * Handles updates from the Jibri which can broadcast a YouTube URL that\n * needs to be updated in a JibriSession.\n *\n * @param {Node} presence - An XMPP presence update.\n * @returns {void}\n */\n _handleJibriPresence(presence) {\n const { liveStreamViewURL, mode, sessionID }\n = recordingXMLUtils.getHiddenDomainUpdate(presence);\n\n if (!sessionID) {\n logger.warn(\n 'Ignoring potential jibri presence due to no session id.');\n\n return;\n }\n\n let session = this.getSession(sessionID);\n\n if (!session) {\n session = this._createSession(sessionID, '', mode);\n }\n\n session.setLiveStreamViewURL(liveStreamViewURL);\n\n this._emitSessionUpdate(session);\n }\n}\n\nexport default RecordingManager;\n","import { getLogger } from '@jitsi/logger';\n\nimport * as ConferenceEvents from '../../JitsiConferenceEvents';\nimport * as MediaType from '../../service/RTC/MediaType';\nimport * as ConnectionQualityEvents from '../../service/connectivity/ConnectionQualityEvents';\nimport { createAudioOutputProblemEvent } from '../../service/statistics/AnalyticsEvents';\n\nimport Statistics from './statistics';\n\nconst logger = getLogger(__filename);\n\n/**\n * Number of local samples that will be used for comparison before and after the remote sample is received.\n */\nconst NUMBER_OF_LOCAL_SAMPLES = 2;\n\n/**\n * Collects the average audio levels per participant from the local stats and the stats received by every remote\n * participant and compares them to detect potential audio problem for a participant.\n */\nexport default class AudioOutputProblemDetector {\n\n /**\n * Creates new AudioOutputProblemDetector instance.\n *\n * @param {JitsiCofnerence} conference - The conference instance to be monitored.\n */\n constructor(conference) {\n this._conference = conference;\n this._localAudioLevelCache = {};\n this._reportedParticipants = [];\n this._audioProblemCandidates = {};\n this._numberOfRemoteAudioLevelsReceived = {};\n this._onLocalAudioLevelsReport = this._onLocalAudioLevelsReport.bind(this);\n this._onRemoteAudioLevelReceived = this._onRemoteAudioLevelReceived.bind(this);\n this._clearUserData = this._clearUserData.bind(this);\n this._conference.on(ConnectionQualityEvents.REMOTE_STATS_UPDATED, this._onRemoteAudioLevelReceived);\n this._conference.statistics.addConnectionStatsListener(this._onLocalAudioLevelsReport);\n this._conference.on(ConferenceEvents.USER_LEFT, this._clearUserData);\n }\n\n /**\n * A listener for audio level data received by a remote participant.\n *\n * @param {string} userID - The user id of the participant that sent the data.\n * @param {number} audioLevel - The average audio level value.\n * @returns {void}\n */\n _onRemoteAudioLevelReceived(userID, { avgAudioLevels }) {\n const numberOfReports = (this._numberOfRemoteAudioLevelsReceived[userID] + 1) || 0;\n\n this._numberOfRemoteAudioLevelsReceived[userID] = numberOfReports;\n\n if (this._reportedParticipants.indexOf(userID) !== -1 || (userID in this._audioProblemCandidates)\n || avgAudioLevels <= 0 || numberOfReports < 3) {\n return;\n }\n\n const participant = this._conference.getParticipantById(userID);\n\n if (participant) {\n const tracks = participant.getTracksByMediaType(MediaType.AUDIO);\n\n if (tracks.length > 0 && participant.isAudioMuted()) {\n // We don't need to report an error if everything seems fine with the participant and its tracks but\n // the participant is audio muted. Since those are average audio levels we potentially can receive non\n // zero values for muted track.\n return;\n }\n }\n\n const localAudioLevels = this._localAudioLevelCache[userID];\n\n if (!Array.isArray(localAudioLevels) || localAudioLevels.every(audioLevel => audioLevel === 0)) {\n this._audioProblemCandidates[userID] = {\n remoteAudioLevels: avgAudioLevels,\n localAudioLevels: []\n };\n }\n }\n\n /**\n * A listener for audio level data retrieved by the local stats.\n *\n * @param {TraceablePeerConnection} tpc - The TraceablePeerConnection instance used to gather the data.\n * @param {Object} avgAudioLevels - The average audio levels per participant.\n * @returns {void}\n */\n _onLocalAudioLevelsReport(tpc, { avgAudioLevels }) {\n if (tpc !== this._conference.getActivePeerConnection()) {\n return;\n }\n\n Object.keys(avgAudioLevels).forEach(userID => {\n if (this._reportedParticipants.indexOf(userID) !== -1) {\n return;\n }\n\n const localAudioLevels = this._localAudioLevelCache[userID];\n\n if (!Array.isArray(localAudioLevels)) {\n this._localAudioLevelCache[userID] = [ ];\n } else if (localAudioLevels.length >= NUMBER_OF_LOCAL_SAMPLES) {\n localAudioLevels.shift();\n }\n\n this._localAudioLevelCache[userID].push(avgAudioLevels[userID]);\n });\n\n\n Object.keys(this._audioProblemCandidates).forEach(userID => {\n const { localAudioLevels, remoteAudioLevels } = this._audioProblemCandidates[userID];\n\n localAudioLevels.push(avgAudioLevels[userID]);\n\n if (localAudioLevels.length === NUMBER_OF_LOCAL_SAMPLES) {\n if (localAudioLevels.every(audioLevel => typeof audioLevel === 'undefined' || audioLevel === 0)) {\n const localAudioLevelsString = JSON.stringify(localAudioLevels);\n\n Statistics.sendAnalytics(\n createAudioOutputProblemEvent(userID, localAudioLevelsString, remoteAudioLevels));\n logger.warn(`A potential problem is detected with the audio output for participant ${\n userID}, local audio levels: ${localAudioLevelsString}, remote audio levels: ${\n remoteAudioLevels}`);\n this._reportedParticipants.push(userID);\n this._clearUserData(userID);\n }\n\n delete this._audioProblemCandidates[userID];\n }\n });\n }\n\n /**\n * Clears the data stored for a participant.\n *\n * @param {string} userID - The id of the participant.\n * @returns {void}\n */\n _clearUserData(userID) {\n delete this._localAudioLevelCache[userID];\n }\n\n /**\n * Disposes the allocated resources.\n *\n * @returns {void}\n */\n dispose() {\n this._conference.off(ConnectionQualityEvents.REMOTE_STATS_UPDATED, this._onRemoteAudioLevelReceived);\n this._conference.off(ConferenceEvents.USER_LEFT, this._clearUserData);\n this._conference.statistics.removeConnectionStatsListener(this._onLocalAudioLevelsReport);\n this._localAudioLevelCache = undefined;\n this._audioProblemCandidates = undefined;\n this._reportedParticipants = undefined;\n this._numberOfRemoteAudioLevelsReceived = undefined;\n this._conference = undefined;\n }\n}\n","import { getLogger } from '@jitsi/logger';\nimport isEqual from 'lodash.isequal';\n\nimport * as ConferenceEvents from '../../JitsiConferenceEvents';\nimport * as MediaType from '../../service/RTC/MediaType';\nimport * as VideoType from '../../service/RTC/VideoType';\nimport * as ConnectionQualityEvents\n from '../../service/connectivity/ConnectionQualityEvents';\nimport {\n createRtpStatsEvent,\n createTransportStatsEvent\n} from '../../service/statistics/AnalyticsEvents';\nimport browser from '../browser';\n\nimport Statistics from './statistics';\n\n\nconst logger = getLogger(__filename);\n\n/**\n * This will calculate an average for one, named stat and submit it to\n * the analytics module when requested. It automatically counts the samples.\n */\nclass AverageStatReport {\n /**\n * Creates new AverageStatReport for given name.\n * @param {string} name that's the name of the event that will be reported\n * to the analytics module.\n */\n constructor(name) {\n this.name = name;\n this.count = 0;\n this.sum = 0;\n this.samples = [];\n }\n\n /**\n * Adds the next value that will be included in the average when\n * {@link calculate} is called.\n * @param {number} nextValue\n */\n addNext(nextValue) {\n if (typeof nextValue !== 'number') {\n logger.error(\n `${this.name} - invalid value for idx: ${this.count}`,\n nextValue);\n } else if (!isNaN(nextValue)) {\n this.sum += nextValue;\n this.samples.push(nextValue);\n this.count += 1;\n }\n }\n\n /**\n * Calculates an average for the samples collected using {@link addNext}.\n * @return {number|NaN} an average of all collected samples or NaN\n * if no samples were collected.\n */\n calculate() {\n return this.sum / this.count;\n }\n\n /**\n * Appends the report to the analytics \"data\" object. The object will be\n * set under prefix + {@link this.name} key.\n * @param {Object} report the analytics \"data\" object\n */\n appendReport(report) {\n report[`${this.name}_avg`] = this.calculate();\n report[`${this.name}_samples`] = JSON.stringify(this.samples);\n }\n\n /**\n * Clears all memory of any samples collected, so that new average can be\n * calculated using this instance.\n */\n reset() {\n this.samples = [];\n this.sum = 0;\n this.count = 0;\n }\n}\n\n/**\n * Class gathers the stats that are calculated and reported for a\n * {@link TraceablePeerConnection} even if it's not currently active. For\n * example we want to monitor RTT for the JVB connection while in P2P mode.\n */\nclass ConnectionAvgStats {\n /**\n * Creates new ConnectionAvgStats\n * @param {AvgRTPStatsReporter} avgRtpStatsReporter\n * @param {boolean} isP2P\n * @param {number} n the number of samples, before arithmetic mean is to be\n * calculated and values submitted to the analytics module.\n */\n constructor(avgRtpStatsReporter, isP2P, n) {\n /**\n * Is this instance for JVB or P2P connection ?\n * @type {boolean}\n */\n this.isP2P = isP2P;\n\n /**\n * How many samples are to be included in arithmetic mean calculation.\n * @type {number}\n * @private\n */\n this._n = n;\n\n /**\n * The current sample index. Starts from 0 and goes up to {@link _n})\n * when analytics report will be submitted.\n * @type {number}\n * @private\n */\n this._sampleIdx = 0;\n\n /**\n * Average round trip time reported by the ICE candidate pair.\n * @type {AverageStatReport}\n */\n this._avgRTT = new AverageStatReport('rtt');\n\n /**\n * Map stores average RTT to the JVB reported by remote participants.\n * Mapped per participant id {@link JitsiParticipant.getId}.\n *\n * This is used only when {@link ConnectionAvgStats.isP2P} equals to\n * false.\n *\n * @type {Map}\n * @private\n */\n this._avgRemoteRTTMap = new Map();\n\n /**\n * The conference for which stats will be collected and reported.\n * @type {JitsiConference}\n * @private\n */\n this._avgRtpStatsReporter = avgRtpStatsReporter;\n\n /**\n * The latest average E2E RTT for the JVB connection only.\n *\n * This is used only when {@link ConnectionAvgStats.isP2P} equals to\n * false.\n *\n * @type {number}\n */\n this._avgEnd2EndRTT = undefined;\n\n this._onConnectionStats = (tpc, stats) => {\n if (this.isP2P === tpc.isP2P) {\n this._calculateAvgStats(stats);\n }\n };\n\n const conference = avgRtpStatsReporter._conference;\n\n conference.statistics.addConnectionStatsListener(\n this._onConnectionStats);\n\n if (!this.isP2P) {\n this._onUserLeft = id => this._avgRemoteRTTMap.delete(id);\n conference.on(ConferenceEvents.USER_LEFT, this._onUserLeft);\n\n this._onRemoteStatsUpdated\n = (id, data) => this._processRemoteStats(id, data);\n conference.on(\n ConnectionQualityEvents.REMOTE_STATS_UPDATED,\n this._onRemoteStatsUpdated);\n }\n }\n\n /**\n * Processes next batch of stats.\n * @param {go figure} data\n * @private\n */\n _calculateAvgStats(data) {\n if (!data) {\n logger.error('No stats');\n\n return;\n }\n\n if (browser.supportsRTTStatistics()) {\n if (data.transport && data.transport.length) {\n this._avgRTT.addNext(data.transport[0].rtt);\n }\n }\n\n this._sampleIdx += 1;\n\n if (this._sampleIdx >= this._n) {\n if (browser.supportsRTTStatistics()) {\n const conference = this._avgRtpStatsReporter._conference;\n\n const batchReport = {\n p2p: this.isP2P,\n 'conference_size': conference.getParticipantCount()\n };\n\n if (data.transport && data.transport.length) {\n Object.assign(batchReport, {\n 'local_candidate_type':\n data.transport[0].localCandidateType,\n 'remote_candidate_type':\n data.transport[0].remoteCandidateType,\n 'transport_type': data.transport[0].type\n });\n }\n\n this._avgRTT.appendReport(batchReport);\n\n if (this.isP2P) {\n // Report RTT diff only for P2P.\n const jvbEnd2EndRTT = this\n ._avgRtpStatsReporter.jvbStatsMonitor._avgEnd2EndRTT;\n\n if (!isNaN(jvbEnd2EndRTT)) {\n // eslint-disable-next-line dot-notation\n batchReport['rtt_diff']\n = this._avgRTT.calculate() - jvbEnd2EndRTT;\n }\n } else {\n // Report end to end RTT only for JVB.\n const avgRemoteRTT = this._calculateAvgRemoteRTT();\n const avgLocalRTT = this._avgRTT.calculate();\n\n this._avgEnd2EndRTT = avgLocalRTT + avgRemoteRTT;\n\n if (!isNaN(avgLocalRTT) && !isNaN(avgRemoteRTT)) {\n // eslint-disable-next-line dot-notation\n batchReport['end2end_rtt_avg'] = this._avgEnd2EndRTT;\n }\n }\n\n Statistics.sendAnalytics(createRtpStatsEvent(batchReport));\n }\n\n this._resetAvgStats();\n }\n }\n\n /**\n * Calculates arithmetic mean of all RTTs towards the JVB reported by\n * participants.\n * @return {number|NaN} NaN if not available (not enough data)\n * @private\n */\n _calculateAvgRemoteRTT() {\n let count = 0, sum = 0;\n\n // FIXME should we ignore RTT for participant\n // who \"is having connectivity issues\" ?\n for (const remoteAvg of this._avgRemoteRTTMap.values()) {\n const avg = remoteAvg.calculate();\n\n if (!isNaN(avg)) {\n sum += avg;\n count += 1;\n remoteAvg.reset();\n }\n }\n\n return sum / count;\n }\n\n /**\n * Processes {@link ConnectionQualityEvents.REMOTE_STATS_UPDATED} to analyse\n * RTT towards the JVB reported by each participant.\n * @param {string} id {@link JitsiParticipant.getId}\n * @param {go figure in ConnectionQuality.js} data\n * @private\n */\n _processRemoteStats(id, data) {\n const validData = typeof data.jvbRTT === 'number';\n let rttAvg = this._avgRemoteRTTMap.get(id);\n\n if (!rttAvg && validData) {\n rttAvg = new AverageStatReport(`${id}_stat_rtt`);\n this._avgRemoteRTTMap.set(id, rttAvg);\n }\n\n if (validData) {\n rttAvg.addNext(data.jvbRTT);\n } else if (rttAvg) {\n this._avgRemoteRTTMap.delete(id);\n }\n }\n\n /**\n * Reset cache of all averages and {@link _sampleIdx}.\n * @private\n */\n _resetAvgStats() {\n this._avgRTT.reset();\n if (this._avgRemoteRTTMap) {\n this._avgRemoteRTTMap.clear();\n }\n this._sampleIdx = 0;\n }\n\n /**\n *\n */\n dispose() {\n\n const conference = this._avgRtpStatsReporter._conference;\n\n conference.statistics.removeConnectionStatsListener(\n this._onConnectionStats);\n if (!this.isP2P) {\n conference.off(\n ConnectionQualityEvents.REMOTE_STATS_UPDATED,\n this._onRemoteStatsUpdated);\n conference.off(\n ConferenceEvents.USER_LEFT,\n this._onUserLeft);\n }\n }\n}\n\n/**\n * Reports average RTP statistics values (arithmetic mean) to the analytics\n * module for things like bit rate, bandwidth, packet loss etc. It keeps track\n * of the P2P vs JVB conference modes and submits the values under different\n * namespaces (the events for P2P mode have 'p2p.' prefix). Every switch between\n * P2P mode resets the data collected so far and averages are calculated from\n * scratch.\n */\nexport default class AvgRTPStatsReporter {\n /**\n * Creates new instance of AvgRTPStatsReporter\n * @param {JitsiConference} conference\n * @param {number} n the number of samples, before arithmetic mean is to be\n * calculated and values submitted to the analytics module.\n */\n constructor(conference, n) {\n /**\n * How many {@link ConnectionQualityEvents.LOCAL_STATS_UPDATED} samples\n * are to be included in arithmetic mean calculation.\n * @type {number}\n * @private\n */\n this._n = n;\n\n if (n > 0) {\n logger.info(`Avg RTP stats will be calculated every ${n} samples`);\n } else {\n logger.info('Avg RTP stats reports are disabled.');\n\n // Do not initialize\n return;\n }\n\n /**\n * The current sample index. Starts from 0 and goes up to {@link _n})\n * when analytics report will be submitted.\n * @type {number}\n * @private\n */\n this._sampleIdx = 0;\n\n /**\n * The conference for which stats will be collected and reported.\n * @type {JitsiConference}\n * @private\n */\n this._conference = conference;\n\n /**\n * Average audio upload bitrate\n * XXX What are the units?\n * @type {AverageStatReport}\n * @private\n */\n this._avgAudioBitrateUp\n = new AverageStatReport('bitrate_audio_upload');\n\n /**\n * Average audio download bitrate\n * XXX What are the units?\n * @type {AverageStatReport}\n * @private\n */\n this._avgAudioBitrateDown\n = new AverageStatReport('bitrate_audio_download');\n\n /**\n * Average video upload bitrate\n * XXX What are the units?\n * @type {AverageStatReport}\n * @private\n */\n this._avgVideoBitrateUp\n = new AverageStatReport('bitrate_video_upload');\n\n /**\n * Average video download bitrate\n * XXX What are the units?\n * @type {AverageStatReport}\n * @private\n */\n this._avgVideoBitrateDown\n = new AverageStatReport('bitrate_video_download');\n\n /**\n * Average upload bandwidth\n * XXX What are the units?\n * @type {AverageStatReport}\n * @private\n */\n this._avgBandwidthUp\n = new AverageStatReport('bandwidth_upload');\n\n /**\n * Average download bandwidth\n * XXX What are the units?\n * @type {AverageStatReport}\n * @private\n */\n this._avgBandwidthDown\n = new AverageStatReport('bandwidth_download');\n\n /**\n * Average total packet loss\n * XXX What are the units?\n * @type {AverageStatReport}\n * @private\n */\n this._avgPacketLossTotal\n = new AverageStatReport('packet_loss_total');\n\n /**\n * Average upload packet loss\n * XXX What are the units?\n * @type {AverageStatReport}\n * @private\n */\n this._avgPacketLossUp\n = new AverageStatReport('packet_loss_upload');\n\n /**\n * Average download packet loss\n * XXX What are the units?\n * @type {AverageStatReport}\n * @private\n */\n this._avgPacketLossDown\n = new AverageStatReport('packet_loss_download');\n\n /**\n * Average FPS for remote videos\n * @type {AverageStatReport}\n * @private\n */\n this._avgRemoteFPS = new AverageStatReport('framerate_remote');\n\n /**\n * Average FPS for remote screen streaming videos (reported only if not\n * a NaN).\n * @type {AverageStatReport}\n * @private\n */\n this._avgRemoteScreenFPS\n = new AverageStatReport('framerate_screen_remote');\n\n /**\n * Average FPS for local video (camera)\n * @type {AverageStatReport}\n * @private\n */\n this._avgLocalFPS = new AverageStatReport('framerate_local');\n\n /**\n * Average FPS for local screen streaming video (reported only if not\n * a NaN).\n * @type {AverageStatReport}\n * @private\n */\n this._avgLocalScreenFPS\n = new AverageStatReport('framerate_screen_local');\n\n /**\n * Average pixels for remote screen streaming videos (reported only if\n * not a NaN).\n * @type {AverageStatReport}\n * @private\n */\n this._avgRemoteCameraPixels\n = new AverageStatReport('pixels_remote');\n\n /**\n * Average pixels for remote screen streaming videos (reported only if\n * not a NaN).\n * @type {AverageStatReport}\n * @private\n */\n this._avgRemoteScreenPixels\n = new AverageStatReport('pixels_screen_remote');\n\n /**\n * Average pixels for local video (camera)\n * @type {AverageStatReport}\n * @private\n */\n this._avgLocalCameraPixels\n = new AverageStatReport('pixels_local');\n\n /**\n * Average pixels for local screen streaming video (reported only if not\n * a NaN).\n * @type {AverageStatReport}\n * @private\n */\n this._avgLocalScreenPixels\n = new AverageStatReport('pixels_screen_local');\n\n /**\n * Average connection quality as defined by\n * the {@link ConnectionQuality} module.\n * @type {AverageStatReport}\n * @private\n */\n this._avgCQ = new AverageStatReport('connection_quality');\n\n this._cachedTransportStats = undefined;\n\n this._onLocalStatsUpdated = data => {\n this._calculateAvgStats(data);\n this._maybeSendTransportAnalyticsEvent(data);\n };\n conference.on(\n ConnectionQualityEvents.LOCAL_STATS_UPDATED,\n this._onLocalStatsUpdated);\n\n this._onP2PStatusChanged = () => {\n logger.debug('Resetting average stats calculation');\n this._resetAvgStats();\n this.jvbStatsMonitor._resetAvgStats();\n this.p2pStatsMonitor._resetAvgStats();\n };\n conference.on(\n ConferenceEvents.P2P_STATUS,\n this._onP2PStatusChanged);\n\n this._onJvb121StatusChanged = (oldStatus, newStatus) => {\n // We want to reset only on the transition from false => true,\n // because otherwise those stats are resetted on JVB <=> P2P\n // transition.\n if (newStatus === true) {\n logger.info('Resetting JVB avg RTP stats');\n this._resetAvgJvbStats();\n }\n };\n conference.on(\n ConferenceEvents.JVB121_STATUS,\n this._onJvb121StatusChanged);\n\n this.jvbStatsMonitor\n = new ConnectionAvgStats(this, false /* JVB */, n);\n\n this.p2pStatsMonitor\n = new ConnectionAvgStats(this, true /* P2P */, n);\n }\n\n /**\n * Processes next batch of stats reported on\n * {@link ConnectionQualityEvents.LOCAL_STATS_UPDATED}.\n * @param {go figure} data\n * @private\n */\n _calculateAvgStats(data) {\n\n if (!data) {\n logger.error('No stats');\n\n return;\n }\n\n const isP2P = this._conference.isP2PActive();\n const confSize = this._conference.getParticipantCount();\n\n if (!isP2P && confSize < 2) {\n\n // There's no point in collecting stats for a JVB conference of 1.\n // That happens for short period of time after everyone leaves\n // the room, until Jicofo terminates the session.\n return;\n }\n\n /* Uncomment to figure out stats structure\n for (const key in data) {\n if (data.hasOwnProperty(key)) {\n logger.info(`local stat ${key}: `, data[key]);\n }\n } */\n\n const bitrate = data.bitrate;\n const bandwidth = data.bandwidth;\n const packetLoss = data.packetLoss;\n const frameRate = data.framerate;\n const resolution = data.resolution;\n\n if (!bitrate) {\n logger.error('No \"bitrate\"');\n\n return;\n } else if (!bandwidth) {\n logger.error('No \"bandwidth\"');\n\n return;\n } else if (!packetLoss) {\n logger.error('No \"packetloss\"');\n\n return;\n } else if (!frameRate) {\n logger.error('No \"framerate\"');\n\n return;\n } else if (!resolution) {\n logger.error('No resolution');\n\n return;\n }\n\n this._avgAudioBitrateUp.addNext(bitrate.audio.upload);\n this._avgAudioBitrateDown.addNext(bitrate.audio.download);\n\n this._avgVideoBitrateUp.addNext(bitrate.video.upload);\n this._avgVideoBitrateDown.addNext(bitrate.video.download);\n\n if (browser.supportsBandwidthStatistics()) {\n this._avgBandwidthUp.addNext(bandwidth.upload);\n this._avgBandwidthDown.addNext(bandwidth.download);\n }\n\n this._avgPacketLossUp.addNext(packetLoss.upload);\n this._avgPacketLossDown.addNext(packetLoss.download);\n this._avgPacketLossTotal.addNext(packetLoss.total);\n\n this._avgCQ.addNext(data.connectionQuality);\n\n if (frameRate) {\n this._avgRemoteFPS.addNext(\n this._calculateAvgVideoFps(\n frameRate, false /* remote */, VideoType.CAMERA));\n this._avgRemoteScreenFPS.addNext(\n this._calculateAvgVideoFps(\n frameRate, false /* remote */, VideoType.DESKTOP));\n\n this._avgLocalFPS.addNext(\n this._calculateAvgVideoFps(\n frameRate, true /* local */, VideoType.CAMERA));\n this._avgLocalScreenFPS.addNext(\n this._calculateAvgVideoFps(\n frameRate, true /* local */, VideoType.DESKTOP));\n }\n\n if (resolution) {\n this._avgRemoteCameraPixels.addNext(\n this._calculateAvgVideoPixels(\n resolution, false /* remote */, VideoType.CAMERA));\n\n this._avgRemoteScreenPixels.addNext(\n this._calculateAvgVideoPixels(\n resolution, false /* remote */, VideoType.DESKTOP));\n\n this._avgLocalCameraPixels.addNext(\n this._calculateAvgVideoPixels(\n resolution, true /* local */, VideoType.CAMERA));\n\n this._avgLocalScreenPixels.addNext(\n this._calculateAvgVideoPixels(\n resolution, true /* local */, VideoType.DESKTOP));\n }\n\n this._sampleIdx += 1;\n\n if (this._sampleIdx >= this._n) {\n\n const batchReport = {\n p2p: isP2P,\n 'conference_size': confSize\n };\n\n if (data.transport && data.transport.length) {\n Object.assign(batchReport, {\n 'local_candidate_type':\n data.transport[0].localCandidateType,\n 'remote_candidate_type':\n data.transport[0].remoteCandidateType,\n 'transport_type': data.transport[0].type\n });\n }\n\n this._avgAudioBitrateUp.appendReport(batchReport);\n this._avgAudioBitrateDown.appendReport(batchReport);\n\n this._avgVideoBitrateUp.appendReport(batchReport);\n this._avgVideoBitrateDown.appendReport(batchReport);\n\n if (browser.supportsBandwidthStatistics()) {\n this._avgBandwidthUp.appendReport(batchReport);\n this._avgBandwidthDown.appendReport(batchReport);\n }\n this._avgPacketLossUp.appendReport(batchReport);\n this._avgPacketLossDown.appendReport(batchReport);\n this._avgPacketLossTotal.appendReport(batchReport);\n\n this._avgRemoteFPS.appendReport(batchReport);\n if (!isNaN(this._avgRemoteScreenFPS.calculate())) {\n this._avgRemoteScreenFPS.appendReport(batchReport);\n }\n this._avgLocalFPS.appendReport(batchReport);\n if (!isNaN(this._avgLocalScreenFPS.calculate())) {\n this._avgLocalScreenFPS.appendReport(batchReport);\n }\n\n this._avgRemoteCameraPixels.appendReport(batchReport);\n if (!isNaN(this._avgRemoteScreenPixels.calculate())) {\n this._avgRemoteScreenPixels.appendReport(batchReport);\n }\n this._avgLocalCameraPixels.appendReport(batchReport);\n if (!isNaN(this._avgLocalScreenPixels.calculate())) {\n this._avgLocalScreenPixels.appendReport(batchReport);\n }\n\n this._avgCQ.appendReport(batchReport);\n\n Statistics.sendAnalytics(createRtpStatsEvent(batchReport));\n\n this._resetAvgStats();\n }\n }\n\n /**\n * Calculates average number of pixels for the report\n *\n * @param {map} peerResolutions a map of peer resolutions\n * @param {boolean} isLocal if the average is to be calculated for the local\n * video or false if for remote videos.\n * @param {VideoType} videoType\n * @return {number|NaN} average number of pixels or NaN if there\n * are no samples.\n * @private\n */\n _calculateAvgVideoPixels(peerResolutions, isLocal, videoType) {\n let peerPixelsSum = 0;\n let peerCount = 0;\n const myID = this._conference.myUserId();\n\n for (const peerID of Object.keys(peerResolutions)) {\n if (isLocal ? peerID === myID : peerID !== myID) {\n const participant\n = isLocal\n ? null\n : this._conference.getParticipantById(peerID);\n const videosResolution = peerResolutions[peerID];\n\n // Do not continue without participant for non local peerID\n if ((isLocal || participant) && videosResolution) {\n const peerAvgPixels = this._calculatePeerAvgVideoPixels(\n videosResolution, participant, videoType);\n\n if (!isNaN(peerAvgPixels)) {\n peerPixelsSum += peerAvgPixels;\n peerCount += 1;\n }\n }\n }\n }\n\n return peerPixelsSum / peerCount;\n }\n\n /**\n * Calculate average pixels for either remote or local participant\n * @param {object} videos maps resolution per video SSRC\n * @param {JitsiParticipant|null} participant remote participant or\n * null for local video pixels calculation.\n * @param {VideoType} videoType the type of the video for which an average\n * will be calculated.\n * @return {number|NaN} average video pixels of all participant's videos or\n * NaN if currently not available\n * @private\n */\n _calculatePeerAvgVideoPixels(videos, participant, videoType) {\n let ssrcs = Object.keys(videos).map(ssrc => Number(ssrc));\n let videoTracks = null;\n\n // NOTE that this method is supposed to be called for the stats\n // received from the current peerconnection.\n const tpc = this._conference.getActivePeerConnection();\n\n if (participant) {\n videoTracks = participant.getTracksByMediaType(MediaType.VIDEO);\n if (videoTracks) {\n ssrcs\n = ssrcs.filter(\n ssrc => videoTracks.find(\n track =>\n !track.isMuted()\n && track.getSSRC() === ssrc\n && track.videoType === videoType));\n }\n } else {\n videoTracks = this._conference.getLocalTracks(MediaType.VIDEO);\n ssrcs\n = ssrcs.filter(\n ssrc => videoTracks.find(\n track =>\n !track.isMuted()\n && tpc.getLocalSSRC(track) === ssrc\n && track.videoType === videoType));\n }\n\n let peerPixelsSum = 0;\n let peerSsrcCount = 0;\n\n for (const ssrc of ssrcs) {\n const peerSsrcPixels\n = Number(videos[ssrc].height) * Number(videos[ssrc].width);\n\n // FPS is reported as 0 for users with no video\n if (!isNaN(peerSsrcPixels) && peerSsrcPixels > 0) {\n peerPixelsSum += peerSsrcPixels;\n peerSsrcCount += 1;\n }\n }\n\n return peerPixelsSum / peerSsrcCount;\n }\n\n\n /**\n * Calculates average FPS for the report\n * @param {go figure} frameRate\n * @param {boolean} isLocal if the average is to be calculated for the local\n * video or false if for remote videos.\n * @param {VideoType} videoType\n * @return {number|NaN} average FPS or NaN if there are no samples.\n * @private\n */\n _calculateAvgVideoFps(frameRate, isLocal, videoType) {\n let peerFpsSum = 0;\n let peerCount = 0;\n const myID = this._conference.myUserId();\n\n for (const peerID of Object.keys(frameRate)) {\n if (isLocal ? peerID === myID : peerID !== myID) {\n const participant\n = isLocal\n ? null : this._conference.getParticipantById(peerID);\n const videosFps = frameRate[peerID];\n\n // Do not continue without participant for non local peerID\n if ((isLocal || participant) && videosFps) {\n const peerAvgFPS\n = this._calculatePeerAvgVideoFps(\n videosFps, participant, videoType);\n\n if (!isNaN(peerAvgFPS)) {\n peerFpsSum += peerAvgFPS;\n peerCount += 1;\n }\n }\n }\n }\n\n return peerFpsSum / peerCount;\n }\n\n /**\n * Calculate average FPS for either remote or local participant\n * @param {object} videos maps FPS per video SSRC\n * @param {JitsiParticipant|null} participant remote participant or\n * null for local FPS calculation.\n * @param {VideoType} videoType the type of the video for which an average\n * will be calculated.\n * @return {number|NaN} average FPS of all participant's videos or\n * NaN if currently not available\n * @private\n */\n _calculatePeerAvgVideoFps(videos, participant, videoType) {\n let ssrcs = Object.keys(videos).map(ssrc => Number(ssrc));\n let videoTracks = null;\n\n // NOTE that this method is supposed to be called for the stats\n // received from the current peerconnection.\n const tpc = this._conference.getActivePeerConnection();\n\n if (participant) {\n videoTracks = participant.getTracksByMediaType(MediaType.VIDEO);\n if (videoTracks) {\n ssrcs\n = ssrcs.filter(\n ssrc => videoTracks.find(\n track => !track.isMuted()\n && track.getSSRC() === ssrc\n && track.videoType === videoType));\n }\n } else {\n videoTracks = this._conference.getLocalTracks(MediaType.VIDEO);\n ssrcs\n = ssrcs.filter(\n ssrc => videoTracks.find(\n track => !track.isMuted()\n && tpc.getLocalSSRC(track) === ssrc\n && track.videoType === videoType));\n }\n\n let peerFpsSum = 0;\n let peerSsrcCount = 0;\n\n for (const ssrc of ssrcs) {\n const peerSsrcFps = Number(videos[ssrc]);\n\n // FPS is reported as 0 for users with no video\n if (!isNaN(peerSsrcFps) && peerSsrcFps > 0) {\n peerFpsSum += peerSsrcFps;\n peerSsrcCount += 1;\n }\n }\n\n return peerFpsSum / peerSsrcCount;\n }\n\n /**\n * Sends the 'transport.stats' analytics event whenever we detect that\n * there is a change in the local or remote candidate type on the transport\n * that is currently selected.\n * @param {*} data\n * @private\n */\n _maybeSendTransportAnalyticsEvent(data) {\n if (!data || !data.transport || !data.transport.length) {\n return;\n }\n const transportStats = {\n p2p: data.transport[0].p2p,\n 'local_candidate_type': data.transport[0].localCandidateType,\n 'remote_candidate_type': data.transport[0].remoteCandidateType,\n 'transport_type': data.transport[0].type\n };\n\n if (!this._cachedTransportStats || !isEqual(transportStats, this._cachedTransportStats)) {\n this._cachedTransportStats = transportStats;\n Statistics.sendAnalytics(createTransportStatsEvent(transportStats));\n }\n }\n\n /**\n * Resets the stats related to JVB connection. Must not be called when in\n * P2P mode, because then the {@link AverageStatReport} instances are\n * tracking P2P stats. Note that this should never happen unless something\n * is wrong with the P2P and JVB121 events.\n * @private\n */\n _resetAvgJvbStats() {\n this._resetAvgStats();\n this.jvbStatsMonitor._resetAvgStats();\n }\n\n /**\n * Reset cache of all averages and {@link _sampleIdx}.\n * @private\n */\n _resetAvgStats() {\n this._avgAudioBitrateUp.reset();\n this._avgAudioBitrateDown.reset();\n\n this._avgVideoBitrateUp.reset();\n this._avgVideoBitrateDown.reset();\n\n this._avgBandwidthUp.reset();\n this._avgBandwidthDown.reset();\n\n this._avgPacketLossUp.reset();\n this._avgPacketLossDown.reset();\n this._avgPacketLossTotal.reset();\n\n this._avgRemoteFPS.reset();\n this._avgRemoteScreenFPS.reset();\n this._avgLocalFPS.reset();\n this._avgLocalScreenFPS.reset();\n\n this._avgRemoteCameraPixels.reset();\n this._avgRemoteScreenPixels.reset();\n this._avgLocalCameraPixels.reset();\n this._avgLocalScreenPixels.reset();\n\n this._avgCQ.reset();\n\n this._sampleIdx = 0;\n }\n\n /**\n * Unregisters all event listeners and stops working.\n */\n dispose() {\n this._conference.off(\n ConferenceEvents.P2P_STATUS,\n this._onP2PStatusChanged);\n this._conference.off(\n ConnectionQualityEvents.LOCAL_STATS_UPDATED,\n this._onLocalStatsUpdated);\n this._conference.off(\n ConferenceEvents.JVB121_STATUS,\n this._onJvb121StatusChanged);\n this.jvbStatsMonitor.dispose();\n this.p2pStatsMonitor.dispose();\n }\n}\n","import * as JitsiConferenceEvents from '../../JitsiConferenceEvents';\nimport XMPPEvents from '../../service/xmpp/XMPPEvents';\n\nimport SpeakerStats from './SpeakerStats';\n\n/**\n * A collection for tracking speaker stats. Attaches listeners\n * to the conference to automatically update on tracked events.\n */\nexport default class SpeakerStatsCollector {\n /**\n * Initializes a new SpeakerStatsCollector instance.\n *\n * @constructor\n * @param {JitsiConference} conference - The conference to track.\n * @returns {void}\n */\n constructor(conference) {\n this.stats = {\n users: {\n\n // userId: SpeakerStats\n },\n dominantSpeakerId: null\n };\n\n const userId = conference.myUserId();\n\n this.stats.users[userId] = new SpeakerStats(userId, null, true);\n this.conference = conference;\n\n conference.addEventListener(\n JitsiConferenceEvents.DOMINANT_SPEAKER_CHANGED,\n this._onDominantSpeaker.bind(this));\n conference.addEventListener(\n JitsiConferenceEvents.USER_JOINED,\n this._onUserJoin.bind(this));\n conference.addEventListener(\n JitsiConferenceEvents.USER_LEFT,\n this._onUserLeave.bind(this));\n conference.addEventListener(\n JitsiConferenceEvents.DISPLAY_NAME_CHANGED,\n this._onDisplayNameChange.bind(this));\n conference.addEventListener(\n JitsiConferenceEvents.FACIAL_EXPRESSION_ADDED,\n this._onFacialExpressionAdd.bind(this));\n if (conference.xmpp) {\n conference.xmpp.addListener(\n XMPPEvents.SPEAKER_STATS_RECEIVED,\n this._updateStats.bind(this));\n }\n }\n\n /**\n * Reacts to dominant speaker change events by changing its speaker stats\n * models to reflect the current dominant speaker.\n *\n * @param {string} dominantSpeakerId - The user id of the new\n * dominant speaker.\n * @returns {void}\n * @private\n */\n _onDominantSpeaker(dominantSpeakerId) {\n const oldDominantSpeaker\n = this.stats.users[this.stats.dominantSpeakerId];\n const newDominantSpeaker = this.stats.users[dominantSpeakerId];\n\n oldDominantSpeaker && oldDominantSpeaker.setDominantSpeaker(false);\n newDominantSpeaker && newDominantSpeaker.setDominantSpeaker(true);\n this.stats.dominantSpeakerId = dominantSpeakerId;\n }\n\n /**\n * Reacts to user join events by creating a new SpeakerStats model.\n *\n * @param {string} userId - The user id of the new user.\n * @param {JitsiParticipant} - The JitsiParticipant model for the new user.\n * @returns {void}\n * @private\n */\n _onUserJoin(userId, participant) {\n if (participant.isHidden()) {\n return;\n }\n\n if (!this.stats.users[userId]) {\n this.stats.users[userId] = new SpeakerStats(userId, participant.getDisplayName());\n }\n }\n\n /**\n * Reacts to user leave events by updating the associated user's\n * SpeakerStats model.\n *\n * @param {string} userId - The user id of the user that left.\n * @returns {void}\n * @private\n */\n _onUserLeave(userId) {\n const savedUser = this.stats.users[userId];\n\n if (savedUser) {\n savedUser.markAsHasLeft();\n }\n }\n\n /**\n * Reacts to user name change events by updating the last known name\n * tracked in the associated SpeakerStats model.\n *\n * @param {string} userId - The user id of the user that left.\n * @returns {void}\n * @private\n */\n _onDisplayNameChange(userId, newName) {\n const savedUser = this.stats.users[userId];\n\n if (savedUser) {\n savedUser.setDisplayName(newName);\n }\n }\n\n /**\n * Adds a new facial expression with its duration of a remote user.\n *\n * @param {string} userId - The user id of the user that left.\n * @param {Object} data - The facial expression with its duration.\n * @returns {void}\n * @private\n */\n _onFacialExpressionAdd(userId, data) {\n const savedUser = this.stats.users[userId];\n\n if (savedUser) {\n savedUser.addFacialExpression(data.facialExpression, data.duration);\n }\n }\n\n /**\n * Return a copy of the tracked SpeakerStats models.\n *\n * @returns {Object} The keys are the user ids and the values are the\n * associated user's SpeakerStats model.\n */\n getStats() {\n return this.stats.users;\n }\n\n /**\n * Updates of the current stats is requested, passing the new values.\n *\n * @param {Object} newStats - The new values used to update current one.\n * @private\n */\n _updateStats(newStats) {\n for (const userId in newStats) { // eslint-disable-line guard-for-in\n let speakerStatsToUpdate;\n const newParticipant = this.conference.getParticipantById(userId);\n\n // we want to ignore hidden participants\n if (!newParticipant || !newParticipant.isHidden()) {\n if (this.stats.users[userId]) {\n speakerStatsToUpdate = this.stats.users[userId];\n\n if (!speakerStatsToUpdate.getDisplayName()) {\n speakerStatsToUpdate\n .setDisplayName(newStats[userId].displayName);\n }\n } else {\n speakerStatsToUpdate = new SpeakerStats(\n userId, newStats[userId].displayName);\n this.stats.users[userId] = speakerStatsToUpdate;\n speakerStatsToUpdate.markAsHasLeft();\n }\n }\n\n speakerStatsToUpdate.totalDominantSpeakerTime\n = newStats[userId].totalDominantSpeakerTime;\n\n speakerStatsToUpdate.setFacialExpressions(newStats[userId].facialExpressions);\n }\n }\n}\n","/* eslint-disable max-params */\n\n/**\n * This object stores variables needed around the recording of an audio stream\n * and passing this recording along with additional information along to\n * different processes\n */\nexport default class RecordingResult {\n /**\n * @param blob the recording audio stream as a single blob\n * @param name the name of the person of the audio stream\n * @param startTime the time in UTC when recording of the audiostream started\n * @param wordArray the recorder audio stream transcribed as an array of Word objects\n */\n constructor(blob, name, startTime, wordArray) {\n this.blob = blob;\n this.name = name;\n this.startTime = startTime;\n this.wordArray = wordArray;\n }\n}\n","/**\n * A TrackRecorder object holds all the information needed for recording a\n * single JitsiTrack (either remote or local)\n * @param track The JitsiTrack the object is going to hold\n */\nexport default class TrackRecorder {\n /**\n * @param track The JitsiTrack the object is going to hold\n */\n constructor(track) {\n // The JitsiTrack holding the stream\n this.track = track;\n\n // The MediaRecorder recording the stream\n this.recorder = null;\n\n // The array of data chunks recorded from the stream\n // acts as a buffer until the data is stored on disk\n this.data = null;\n\n // the name of the person of the JitsiTrack. This can be undefined and/or\n // not unique\n this.name = null;\n\n // the time of the start of the recording\n this.startTime = null;\n }\n}\n","import RecordingResult from './recordingResult';\nimport TrackRecorder from './trackRecorder';\n\n/**\n * Possible audio formats MIME types\n */\nconst AUDIO_WEBM = 'audio/webm'; // Supported in chrome\nconst AUDIO_OGG = 'audio/ogg'; // Supported in firefox\n\n/**\n * Starts the recording of a JitsiTrack in a TrackRecorder object.\n * This will also define the timestamp and try to update the name\n * @param trackRecorder the TrackRecorder to start\n */\nfunction startRecorder(trackRecorder) {\n if (trackRecorder.recorder === undefined) {\n throw new Error('Passed an object to startRecorder which is not a '\n + 'TrackRecorder object');\n }\n trackRecorder.recorder.start();\n trackRecorder.startTime = new Date();\n}\n\n/**\n * Stops the recording of a JitsiTrack in a TrackRecorder object.\n * This will also try to update the name\n * @param trackRecorder the TrackRecorder to stop\n */\nfunction stopRecorder(trackRecorder) {\n if (trackRecorder.recorder === undefined) {\n throw new Error('Passed an object to stopRecorder which is not a '\n + 'TrackRecorder object');\n }\n trackRecorder.recorder.stop();\n}\n\n/**\n * Determines which kind of audio recording the browser supports\n * chrome supports \"audio/webm\" and firefox supports \"audio/ogg\"\n */\nfunction determineCorrectFileType() {\n if (MediaRecorder.isTypeSupported(AUDIO_WEBM)) {\n return AUDIO_WEBM;\n } else if (MediaRecorder.isTypeSupported(AUDIO_OGG)) {\n return AUDIO_OGG;\n }\n throw new Error(\n 'unable to create a MediaRecorder with the right mimetype!');\n}\n\n/**\n * main exported object of the file, holding all\n * relevant functions and variables for the outside world\n * @param jitsiConference the jitsiConference which this object\n * is going to record\n */\nfunction AudioRecorder(jitsiConference) {\n // array of TrackRecorders, where each trackRecorder\n // holds the JitsiTrack, MediaRecorder and recorder data\n this.recorders = [];\n\n // get which file type is supported by the current browser\n this.fileType = determineCorrectFileType();\n\n // boolean flag for active recording\n this.isRecording = false;\n\n // the jitsiconference the object is recording\n this.jitsiConference = jitsiConference;\n}\n\n/**\n * Add the exported module so that it can be accessed by other files\n */\nAudioRecorder.determineCorrectFileType = determineCorrectFileType;\n\n/**\n * Adds a new TrackRecorder object to the array.\n *\n * @param track the track potentially holding an audio stream\n */\nAudioRecorder.prototype.addTrack = function(track) {\n if (track.isAudioTrack()) {\n // create the track recorder\n const trackRecorder = this.instantiateTrackRecorder(track);\n\n // push it to the local array of all recorders\n\n this.recorders.push(trackRecorder);\n\n // update the name of the trackRecorders\n this.updateNames();\n\n // If we're already recording, immediately start recording this new\n // track.\n if (this.isRecording) {\n startRecorder(trackRecorder);\n }\n }\n};\n\n/**\n * Creates a TrackRecorder object. Also creates the MediaRecorder and\n * data array for the trackRecorder.\n * @param track the JitsiTrack holding the audio MediaStream(s)\n */\nAudioRecorder.prototype.instantiateTrackRecorder = function(track) {\n const trackRecorder = new TrackRecorder(track);\n\n // Create a new stream which only holds the audio track\n const originalStream = trackRecorder.track.getOriginalStream();\n const stream = new MediaStream();\n\n originalStream.getAudioTracks().forEach(t => stream.addTrack(t));\n\n // Create the MediaRecorder\n trackRecorder.recorder = new MediaRecorder(stream,\n { mimeType: this.fileType });\n\n // array for holding the recorder data. Resets it when\n // audio already has been recorder once\n trackRecorder.data = [];\n\n // function handling a dataEvent, e.g the stream gets new data\n trackRecorder.recorder.ondataavailable = function(dataEvent) {\n if (dataEvent.data.size > 0) {\n trackRecorder.data.push(dataEvent.data);\n }\n };\n\n return trackRecorder;\n};\n\n/**\n * Notifies the module that a specific track has stopped, e.g participant left\n * the conference.\n * if the recording has not started yet, the TrackRecorder will be removed from\n * the array. If the recording has started, the recorder will stop recording\n * but not removed from the array so that the recorded stream can still be\n * accessed\n *\n * @param {JitsiTrack} track the JitsiTrack to remove from the recording session\n */\nAudioRecorder.prototype.removeTrack = function(track) {\n if (track.isVideoTrack()) {\n return;\n }\n\n const array = this.recorders;\n let i;\n\n for (i = 0; i < array.length; i++) {\n if (array[i].track.getParticipantId() === track.getParticipantId()) {\n const recorderToRemove = array[i];\n\n if (this.isRecording) {\n stopRecorder(recorderToRemove);\n } else {\n // remove the TrackRecorder from the array\n array.splice(i, 1);\n }\n }\n }\n\n // make sure the names are up to date\n this.updateNames();\n};\n\n/**\n * Tries to update the name value of all TrackRecorder in the array.\n * If it hasn't changed,it will keep the exiting name. If it changes to a\n * undefined value, the old value will also be kept.\n */\nAudioRecorder.prototype.updateNames = function() {\n const conference = this.jitsiConference;\n\n this.recorders.forEach(trackRecorder => {\n if (trackRecorder.track.isLocal()) {\n trackRecorder.name = 'the transcriber';\n } else {\n const id = trackRecorder.track.getParticipantId();\n const participant = conference.getParticipantById(id);\n const newName = participant.getDisplayName();\n\n if (newName !== 'undefined') {\n trackRecorder.name = newName;\n }\n }\n });\n};\n\n/**\n * Starts the audio recording of every local and remote track\n */\nAudioRecorder.prototype.start = function() {\n if (this.isRecording) {\n throw new Error('audiorecorder is already recording');\n }\n\n // set boolean isRecording flag to true so if new participants join the\n // conference, that track can instantly start recording as well\n this.isRecording = true;\n\n // start all the mediaRecorders\n this.recorders.forEach(trackRecorder => startRecorder(trackRecorder));\n\n // log that recording has started\n console.log(\n `Started the recording of the audio. There are currently ${\n this.recorders.length} recorders active.`);\n};\n\n/**\n * Stops the audio recording of every local and remote track\n */\nAudioRecorder.prototype.stop = function() {\n // set the boolean flag to false\n this.isRecording = false;\n\n // stop all recorders\n this.recorders.forEach(trackRecorder => stopRecorder(trackRecorder));\n console.log('stopped recording');\n};\n\n/**\n * link hacking to download all recorded audio streams\n */\nAudioRecorder.prototype.download = function() {\n this.recorders.forEach(trackRecorder => {\n const blob = new Blob(trackRecorder.data, { type: this.fileType });\n const url = URL.createObjectURL(blob);\n const a = document.createElement('a');\n\n document.body.appendChild(a);\n a.style = 'display: none';\n a.href = url;\n a.download = `test.${this.fileType.split('/')[1]}`;\n a.click();\n window.URL.revokeObjectURL(url);\n });\n};\n\n/**\n * returns the audio files of all recorders as an array of objects,\n * which include the name of the owner of the track and the starting time stamp\n * @returns {Array} an array of RecordingResult objects\n */\nAudioRecorder.prototype.getRecordingResults = function() {\n if (this.isRecording) {\n throw new Error(\n 'cannot get blobs because the AudioRecorder is still recording!');\n }\n\n // make sure the names are up to date before sending them off\n this.updateNames();\n\n const array = [];\n\n this.recorders.forEach(\n recorder =>\n array.push(\n new RecordingResult(\n new Blob(recorder.data, { type: this.fileType }),\n recorder.name,\n recorder.startTime)));\n\n return array;\n};\n\n/**\n * Gets the mime type of the recorder audio\n * @returns {String} the mime type of the recorder audio\n */\nAudioRecorder.prototype.getFileType = function() {\n return this.fileType;\n};\n\n/**\n * export the main object AudioRecorder\n */\nexport default AudioRecorder;\n","/**\n * An object representing a transcribed word, with some additional information\n * @param word the word\n * @param begin the time the word was started being uttered\n * @param end the time the word stopped being uttered\n */\nexport default class Word {\n /**\n * @param word the word\n * @param begin the time the word was started being uttered\n * @param end the time the word stopped being uttered\n */\n constructor(word, begin, end) {\n this.word = word;\n this.begin = begin;\n this.end = end;\n }\n\n /**\n * Get the string representation of the word\n * @returns {*} the word as a string\n */\n getWord() {\n return this.word;\n }\n\n /**\n * Get the time the word started being uttered\n * @returns {*} the start time as an integer\n */\n getBeginTime() {\n return this.begin;\n }\n\n /**\n * Get the time the word stopped being uttered\n * @returns {*} the end time as an integer\n */\n getEndTime() {\n return this.end;\n }\n}\n","/* global config */\n\nimport Word from '../word';\n\nimport audioRecorder from './../audioRecorder';\nimport AbstractTranscriptionService from './AbstractTranscriptionService';\n\n/**\n * Implements a TranscriptionService for a Sphinx4 http server\n */\nexport default class SphinxService extends AbstractTranscriptionService {\n /**\n * Implements a TranscriptionService for a Sphinx4 http server\n */\n constructor() {\n super();\n\n // set the correct url\n this.url = getURL();\n }\n\n /**\n * Overrides the sendRequest method from AbstractTranscriptionService\n * it will send the audio stream the a Sphinx4 server to get the transcription\n *\n * @param audioFileBlob the recorder audio stream an a single Blob\n * @param callback the callback function retrieving the server response\n */\n sendRequest(audioFileBlob, callback) {\n console.log(`sending an audio file to ${this.url}`);\n console.log(`the audio file being sent: ${audioFileBlob}`);\n const request = new XMLHttpRequest();\n\n request.onreadystatechange = function() {\n if (request.readyState === XMLHttpRequest.DONE\n && request.status === 200) {\n callback(request.responseText);\n } else if (request.readyState === XMLHttpRequest.DONE) {\n throw new Error(\n `unable to accept response from sphinx server. status: ${request.status}`);\n }\n\n // if not ready no point to throw an error\n };\n request.open('POST', this.url);\n request.setRequestHeader('Content-Type',\n audioRecorder.determineCorrectFileType());\n request.send(audioFileBlob);\n console.log(`send ${audioFileBlob}`);\n }\n\n /**\n * Overrides the formatResponse method from AbstractTranscriptionService\n * It will parse the answer from the server in the expected format\n *\n * @param response the JSON body retrieved from the Sphinx4 server\n */\n formatResponse(response) {\n const result = JSON.parse(response).objects;\n\n // make sure to delete the session id object, which is always\n // the first value in the JSON array\n result.shift();\n const array = [];\n\n result.forEach(\n word => word.filler\n || array.push(new Word(word.word, word.start, word.end)));\n\n return array;\n }\n\n /**\n * checks wether the reply is empty, or doesn't contain a correct JSON object\n * @param response the server response\n * @return {boolean} whether the response is valid\n */\n verify(response) {\n console.log(`response from server:${response.toString()}`);\n\n // test if server responded with a string object\n if (typeof response !== 'string') {\n return false;\n }\n\n // test if the string can be parsed into valid JSON\n let json;\n\n try {\n json = JSON.parse(response);\n } catch (error) {\n console.log(error);\n\n return false;\n }\n\n // check if the JSON has a \"objects\" value\n if (json.objects === undefined) {\n return false;\n }\n\n // get the \"objects\" value and check for a session ID\n const array = json.objects;\n\n if (!(array[0] && array[0]['session-id'])) {\n return false;\n }\n\n // everything seems to be in order\n return true;\n }\n}\n\n/**\n * Gets the URL to the Sphinx4 server from the config file. If it's not there,\n * it will throw an error\n *\n * @returns {string} the URL to the sphinx4 server\n */\nfunction getURL() {\n const message = 'config does not contain an url to a Sphinx4 https server';\n\n if (config.sphinxURL === undefined) {\n console.log(message);\n } else {\n const toReturn = config.sphinxURL;\n\n if (toReturn.includes !== undefined && toReturn.includes('https://')) {\n return toReturn;\n }\n console.log(message);\n\n }\n}\n","/**\n * Abstract class representing an interface to implement a speech-to-text\n * service on.\n */\nexport default class TranscriptionService {\n /**\n * Abstract class representing an interface to implement a speech-to-text\n * service on.\n */\n constructor() {\n throw new Error('TranscriptionService is abstract and cannot be created');\n }\n\n /**\n * This method can be used to send the recorder audio stream and\n * retrieve the answer from the transcription service from the callback\n *\n * @param {RecordingResult} recordingResult a recordingResult object which\n * includes the recorded audio stream as a blob\n * @param {Function} callback which will retrieve the a RecordingResult with\n * the answer as a WordArray\n */\n send(recordingResult, callback) {\n this.sendRequest(recordingResult.blob, response => {\n if (this.verify(response)) {\n recordingResult.wordArray = this.formatResponse(response);\n } else {\n console.log('the retrieved response from the server is not valid!');\n recordingResult.wordArray = [];\n }\n callback(recordingResult);\n });\n }\n\n /**\n * Abstract method which will rend the recorder audio stream to the implemented\n * transcription service and will retrieve an answer, which will be\n * called on the given callback method\n *\n * @param {Blob} audioBlob the recorded audio stream as a single Blob\n * @param {function} callback function which will retrieve the answer\n * from the service\n */\n sendRequest(audioBlob, callback) { // eslint-disable-line no-unused-vars\n throw new Error('TranscriptionService.sendRequest is abstract');\n }\n\n /**\n * Abstract method which will parse the output from the implemented\n * transcription service to the expected format\n *\n * The transcriber class expect an array of word objects, where each word\n * object is one transcribed word by the service.\n *\n * The expected output of this method is an array of word objects, in\n * the correct order. That is, the first object in the array is the first word\n * being said, and the last word in the array is the last word being said\n *\n * @param response the answer from the speech-to-text server which needs to be\n * formatted\n * @return {Array} an array of Word objects\n */\n formatResponse(response) { // eslint-disable-line no-unused-vars\n throw new Error('TranscriptionService.format is abstract');\n }\n\n /**\n * Abstract method which will verify that the response from the server is valid\n *\n * @param response the response from the server\n * @return {boolean} true if response is valid, false otherwise\n */\n verify(response) { // eslint-disable-line no-unused-vars\n throw new Error('TranscriptionService.verify is abstract');\n }\n}\n","import AudioRecorder from './audioRecorder';\nimport SphinxService from './transcriptionServices/SphinxTranscriptionService';\n\nconst BEFORE_STATE = 'before';\nconst RECORDING_STATE = 'recording';\nconst TRANSCRIBING_STATE = 'transcribing';\nconst FINISHED_STATE = 'finished';\n\n// the amount of characters each line in the transcription will have\nconst MAXIMUM_SENTENCE_LENGTH = 80;\n\n/**\n * This is the main object for handing the Transcription. It interacts with\n * the audioRecorder to record every person in a conference and sends the\n * recorder audio to a transcriptionService. The returned speech-to-text result\n * will be merged to create a transcript\n * @param {AudioRecorder} audioRecorder An audioRecorder recording a conference\n */\nfunction Transcriber() {\n // the object which can record all audio in the conference\n this.audioRecorder = new AudioRecorder();\n\n // this object can send the recorder audio to a speech-to-text service\n this.transcriptionService = new SphinxService();\n\n // holds a counter to keep track if merging can start\n this.counter = null;\n\n // holds the date when transcription started which makes it possible\n // to calculate the offset between recordings\n this.startTime = null;\n\n // will hold the transcription once it is completed\n this.transcription = null;\n\n // this will be a method which will be called once the transcription is done\n // with the transcription as parameter\n this.callback = null;\n\n // stores all the retrieved speech-to-text results to merge together\n // this value will store an Array object\n this.results = [];\n\n // Stores the current state of the transcription process\n this.state = BEFORE_STATE;\n\n // Used in the updateTranscription method to add a new line when the\n // sentence becomes to long\n this.lineLength = 0;\n}\n\n/**\n * Method to start the transcription process. It will tell the audioRecorder\n * to start storing all audio streams and record the start time for merging\n * purposes\n */\nTranscriber.prototype.start = function start() {\n if (this.state !== BEFORE_STATE) {\n throw new Error(\n `The transcription can only start when it's in the \"${\n BEFORE_STATE}\" state. It's currently in the \"${\n this.state}\" state`);\n }\n this.state = RECORDING_STATE;\n this.audioRecorder.start();\n this.startTime = new Date();\n};\n\n/**\n * Method to stop the transcription process. It will tell the audioRecorder to\n * stop, and get all the recorded audio to send it to the transcription service\n\n * @param callback a callback which will receive the transcription\n */\nTranscriber.prototype.stop = function stop(callback) {\n if (this.state !== RECORDING_STATE) {\n throw new Error(\n `The transcription can only stop when it's in the \"${\n RECORDING_STATE}\" state. It's currently in the \"${\n this.state}\" state`);\n }\n\n // stop the recording\n console.log('stopping recording and sending audio files');\n this.audioRecorder.stop();\n\n // and send all recorded audio to the transcription service\n const callBack = blobCallBack.bind(null, this);\n\n this.audioRecorder.getRecordingResults().forEach(recordingResult => {\n this.transcriptionService.send(recordingResult, callBack);\n this.counter++;\n });\n\n // set the state to \"transcribing\" so that maybeMerge() functions correctly\n this.state = TRANSCRIBING_STATE;\n\n // and store the callback for later\n this.callback = callback;\n};\n\n/**\n * This method gets the answer from the transcription service, calculates the\n * offset and adds is to every Word object. It will also start the merging\n * when every send request has been received\n *\n * note: Make sure to bind this as a Transcription object\n * @param {Transcriber} transcriber the transcriber instance\n * @param {RecordingResult} answer a RecordingResult object with a defined\n * WordArray\n */\nfunction blobCallBack(transcriber, answer) {\n console.log(\n 'retrieved an answer from the transcription service. The answer has an'\n + ` array of length: ${answer.wordArray.length}`);\n\n // first add the offset between the start of the transcription and\n // the start of the recording to all start and end times\n if (answer.wordArray.length > 0) {\n let offset = answer.startTime.getUTCMilliseconds()\n - transcriber.startTime.getUTCMilliseconds();\n\n // transcriber time will always be earlier\n\n if (offset < 0) {\n offset = 0; // presume 0 if it somehow not earlier\n }\n\n let array = '[';\n\n answer.wordArray.forEach(wordObject => {\n wordObject.begin += offset;\n wordObject.end += offset;\n array += `${wordObject.word},`;\n });\n array += ']';\n console.log(array);\n\n // give a name value to the Array object so that the merging can access\n // the name value without having to use the whole recordingResult object\n // in the algorithm\n answer.wordArray.name = answer.name;\n }\n\n // then store the array and decrease the counter\n transcriber.results.push(answer.wordArray);\n transcriber.counter--;\n console.log(`current counter: ${transcriber.counter}`);\n\n // and check if all results have been received.\n transcriber.maybeMerge();\n}\n\n/**\n * this method will check if the counter is zero. If it is, it will call\n * the merging method\n */\nTranscriber.prototype.maybeMerge = function() {\n if (this.state === TRANSCRIBING_STATE && this.counter === 0) {\n // make sure to include the events in the result arrays before\n // merging starts\n this.merge();\n }\n};\n\n/**\n * This method will merge all speech-to-text arrays together in one\n * readable transcription string\n */\nTranscriber.prototype.merge = function() {\n console.log(\n `starting merge process!\\n The length of the array: ${\n this.results.length}`);\n this.transcription = '';\n\n // the merging algorithm will look over all Word objects who are at pos 0 in\n // every array. It will then select the one closest in time to the\n // previously placed word, while removing the selected word from its array\n // note: words can be skipped the skipped word's begin and end time somehow\n // end up between the closest word start and end time\n const arrays = this.results;\n\n // arrays of Word objects\n const potentialWords = []; // array of the first Word objects\n // check if any arrays are already empty and remove them\n\n hasPopulatedArrays(arrays);\n\n // populate all the potential Words for a first time\n arrays.forEach(array => pushWordToSortedArray(potentialWords, array));\n\n // keep adding words to transcription until all arrays are exhausted\n while (hasPopulatedArrays(arrays)) {\n // first select the lowest array;\n let lowestWordArray = arrays[0];\n\n arrays.forEach(wordArray => {\n if (wordArray[0].begin < lowestWordArray[0].begin) {\n lowestWordArray = wordArray;\n }\n });\n\n // put the word in the transcription\n let wordToAdd = lowestWordArray.shift();\n\n this.updateTranscription(wordToAdd, lowestWordArray.name);\n\n // keep going until a word in another array has a smaller time\n // or the array is empty\n while (lowestWordArray.length > 0) {\n let foundSmaller = false;\n const wordToCompare = lowestWordArray[0].begin;\n\n arrays.forEach(wordArray => {\n if (wordArray[0].begin < wordToCompare) {\n foundSmaller = true;\n }\n });\n\n // add next word if no smaller time has been found\n if (foundSmaller) {\n break;\n }\n\n wordToAdd = lowestWordArray.shift();\n this.updateTranscription(wordToAdd, null);\n }\n\n }\n\n // set the state to finished and do the necessary left-over tasks\n this.state = FINISHED_STATE;\n if (this.callback) {\n this.callback(this.transcription);\n }\n};\n\n/**\n * Appends a word object to the transcription. It will make a new line with a\n * name if a name is specified\n * @param {Word} word the Word object holding the word to append\n * @param {String|null} name the name of a new speaker. Null if not applicable\n */\nTranscriber.prototype.updateTranscription = function(word, name) {\n if (name !== undefined && name !== null) {\n this.transcription += `\\n${name}:`;\n this.lineLength = name.length + 1; // +1 for the semi-colon\n }\n if (this.lineLength + word.word.length > MAXIMUM_SENTENCE_LENGTH) {\n this.transcription += '\\n ';\n this.lineLength = 4; // because of the 4 spaces after the new line\n }\n this.transcription += ` ${word.word}`;\n this.lineLength += word.word.length + 1; // +1 for the space\n};\n\n/**\n * Check if the given 2 dimensional array has any non-zero Word-arrays in them.\n * All zero-element arrays inside will be removed\n * If any non-zero-element arrays are found, the method will return true.\n * otherwise it will return false\n * @param {Array} twoDimensionalArray the array to check\n * @returns {boolean} true if any non-zero arrays inside, otherwise false\n */\nfunction hasPopulatedArrays(twoDimensionalArray) {\n for (let i = 0; i < twoDimensionalArray.length; i++) {\n if (twoDimensionalArray[i].length === 0) {\n twoDimensionalArray.splice(i, 1);\n }\n }\n\n return twoDimensionalArray.length > 0;\n}\n\n/**\n * Push a word to the right location in a sorted array. The array is sorted\n * from lowest to highest start time. Every word is stored in an object which\n * includes the name of the person saying the word.\n *\n * @param {Array} array the sorted array to push to\n * @param {Word} word the word to push into the array\n */\nfunction pushWordToSortedArray(array, word) {\n if (array.length === 0) {\n array.push(word);\n } else {\n if (array[array.length - 1].begin <= word.begin) {\n array.push(word);\n\n return;\n }\n\n for (let i = 0; i < array.length; i++) {\n if (word.begin < array[i].begin) {\n array.splice(i, 0, word);\n\n return;\n }\n }\n array.push(word); // fail safe\n }\n}\n\n/**\n * Gives the transcriber a JitsiTrack holding an audioStream to transcribe.\n * The JitsiTrack is given to the audioRecorder. If it doesn't hold an\n * audiostream, it will not be added by the audioRecorder\n * @param {JitsiTrack} track the track to give to the audioRecorder\n */\nTranscriber.prototype.addTrack = function(track) {\n this.audioRecorder.addTrack(track);\n};\n\n/**\n * Remove the given track from the auioRecorder\n * @param track\n */\nTranscriber.prototype.removeTrack = function(track) {\n this.audioRecorder.removeTrack(track);\n};\n\n/**\n * Will return the created transcription if it's avialable or throw an error\n * when it's not done yet\n * @returns {String} the transcription as a String\n */\nTranscriber.prototype.getTranscription = function() {\n if (this.state !== FINISHED_STATE) {\n throw new Error(\n `The transcription can only be retrieved when it's in the \"${\n FINISHED_STATE}\" state. It's currently in the \"${\n this.state}\" state`);\n }\n\n return this.transcription;\n};\n\n/**\n * Returns the current state of the transcription process\n */\nTranscriber.prototype.getState = function() {\n return this.state;\n};\n\n/**\n * Resets the state to the \"before\" state, such that it's again possible to\n * call the start method\n */\nTranscriber.prototype.reset = function() {\n this.state = BEFORE_STATE;\n this.counter = null;\n this.transcription = null;\n this.startTime = null;\n this.callback = null;\n this.results = [];\n this.lineLength = 0;\n};\n\nexport default Transcriber;\n","import Statistics from '../statistics/statistics';\n\nconst logger = require('@jitsi/logger').getLogger(__filename);\n\n/**\n * Creates new instance of ComponentsVersions which will be discovering\n * the versions of conferencing system components in given\n * JitsiConference.\n * @param conference JitsiConference instance which will be used to\n * listen for focus presence updates.\n * @constructor\n */\nexport default function ComponentsVersions(conference) {\n\n this.versions = {};\n\n this.conference = conference;\n this.conference.addCommandListener(\n 'versions', this.processVersions.bind(this));\n}\n\nComponentsVersions.prototype.processVersions\n = function(versions, mucResource, mucJid) {\n if (!this.conference.isFocus(mucJid)) {\n logger.warn(\n `Received versions not from the focus user: ${versions}`,\n mucJid);\n\n return;\n }\n\n const log = [];\n\n versions.children.forEach(component => {\n\n const name = component.attributes.name;\n const version = component.value;\n\n if (this.versions[name] !== version) {\n this.versions[name] = version;\n logger.info(`Got ${name} version: ${version}`);\n\n log.push({\n id: 'component_version',\n component: name,\n version\n });\n }\n });\n\n // logs versions to stats\n if (log.length > 0) {\n Statistics.sendLog(JSON.stringify(log));\n }\n };\n\n/**\n * Obtains the version of conferencing system component.\n * @param componentName the name of the component for which we want to obtain\n * the version.\n * @returns {String} which describes the version of the component identified by\n * given componentName or undefined if not found.\n */\nComponentsVersions.prototype.getComponentVersion = function(componentName) {\n return this.versions[componentName];\n};\n","/**\n * Status that video SIP GW service is available.\n * @type {string}\n */\nexport const STATUS_AVAILABLE = 'available';\n\n/**\n * Status that video SIP GW service is not available.\n * @type {string}\n */\nexport const STATUS_UNDEFINED = 'undefined';\n\n/**\n * Status that video SIP GW service is available but there are no free nodes\n * at the moment to serve new requests.\n * @type {string}\n */\nexport const STATUS_BUSY = 'busy';\n\n/**\n * Video SIP GW session state, currently running.\n * @type {string}\n */\nexport const STATE_ON = 'on';\n\n/**\n * Video SIP GW session state, currently stopped and not running.\n * @type {string}\n */\nexport const STATE_OFF = 'off';\n\n/**\n * Video SIP GW session state, currently is starting.\n * @type {string}\n */\nexport const STATE_PENDING = 'pending';\n\n/**\n * Video SIP GW session state, has observed some issues and is retrying at the\n * moment.\n * @type {string}\n */\nexport const STATE_RETRYING = 'retrying';\n\n/**\n * Video SIP GW session state, tried to start but it failed.\n * @type {string}\n */\nexport const STATE_FAILED = 'failed';\n\n/**\n * Error on trying to create video SIP GW session in conference where\n * there is no room connection (hasn't joined or has left the room).\n * @type {string}\n */\nexport const ERROR_NO_CONNECTION = 'error_no_connection';\n\n/**\n * Error on trying to create video SIP GW session with address for which\n * there is an already created session.\n * @type {string}\n */\nexport const ERROR_SESSION_EXISTS = 'error_session_already_exists';\n","import { getLogger } from '@jitsi/logger';\nimport { $iq } from 'strophe.js';\n\nimport Listenable from '../util/Listenable';\n\nimport * as VideoSIPGWConstants from './VideoSIPGWConstants';\n\nconst logger = getLogger(__filename);\n\n/**\n * The event name for current sip video session state changed.\n * @type {string} event name for sip video session state changed.\n */\nconst STATE_CHANGED = 'STATE_CHANGED';\n\n/**\n * Jitsi video SIP GW session. Holding its state and able to start/stop it.\n * When session is in OFF or FAILED stated it cannot be used anymore.\n */\nexport default class JitsiVideoSIPGWSession extends Listenable {\n\n /**\n * Creates new session with the desired sip address and display name.\n *\n * @param {string} sipAddress - The sip address to use when\n * starting the session.\n * @param {string} displayName - The display name to use for\n * that participant.\n * @param {ChatRoom} chatRoom - The chat room this session is bound to.\n */\n constructor(sipAddress, displayName, chatRoom) {\n super();\n\n this.sipAddress = sipAddress;\n this.displayName = displayName;\n this.chatRoom = chatRoom;\n\n /*\n * The initial state is undefined. Initial state cannot be STATE_OFF,\n * the session enters this state when it was in STATE_ON and was stopped\n * and such session cannot be used anymore.\n *\n * @type {VideoSIPGWConstants|undefined}\n */\n this.state = undefined;\n }\n\n /**\n * Stops the current session.\n */\n stop() {\n if (this.state === VideoSIPGWConstants.STATE_OFF\n || this.state === VideoSIPGWConstants.STATE_FAILED) {\n logger.warn('Video SIP GW session already stopped or failed!');\n\n return;\n }\n\n this._sendJibriIQ('stop');\n }\n\n /**\n * Starts a new session. Sends an iq to the focus.\n */\n start() {\n // if state is off, this session was active for some reason\n // and we should create new one, rather than reusing it\n if (this.state === VideoSIPGWConstants.STATE_ON\n || this.state === VideoSIPGWConstants.STATE_OFF\n || this.state === VideoSIPGWConstants.STATE_PENDING\n || this.state === VideoSIPGWConstants.STATE_RETRYING) {\n logger.warn('Video SIP GW session already started!');\n\n return;\n }\n\n this._sendJibriIQ('start');\n }\n\n /**\n * Changes the state of this session.\n *\n * @param {string} newState - The new {VideoSIPGWConstants} state to set.\n * @param {string} [optional] failureReason - The reason why a failure state\n * was entered.\n * @returns {void}\n */\n setState(newState, failureReason) {\n if (newState === this.state) {\n return;\n }\n\n const oldState = this.state;\n\n this.state = newState;\n this.eventEmitter.emit(STATE_CHANGED,\n {\n address: this.sipAddress,\n failureReason,\n oldState,\n newState: this.state,\n displayName: this.displayName\n }\n );\n }\n\n /**\n * Subscribes the passed listener to the event for state change of this\n * session.\n *\n * @param {Function} listener - The function that will receive the event.\n */\n addStateListener(listener) {\n this.addListener(STATE_CHANGED, listener);\n }\n\n /**\n * Unsubscribes the passed handler.\n *\n * @param {Function} listener - The function to be removed.\n */\n removeStateListener(listener) {\n this.removeListener(STATE_CHANGED, listener);\n }\n\n /**\n * Sends a jibri command using an iq.\n *\n * @private\n * @param {string} action - The action to send ('start' or 'stop').\n */\n _sendJibriIQ(action) {\n const attributes = {\n 'xmlns': 'http://jitsi.org/protocol/jibri',\n 'action': action,\n sipaddress: this.sipAddress\n };\n\n attributes.displayname = this.displayName;\n\n const iq = $iq({\n to: this.chatRoom.focusMucJid,\n type: 'set' })\n .c('jibri', attributes)\n .up();\n\n logger.debug(`${action} video SIP GW session`, iq.nodeTree);\n this.chatRoom.connection.sendIQ(\n iq,\n () => {}, // eslint-disable-line no-empty-function\n error => {\n logger.error(\n `Failed to ${action} video SIP GW session, error: `, error);\n this.setState(VideoSIPGWConstants.STATE_FAILED);\n });\n }\n}\n","import { getLogger } from '@jitsi/logger';\nconst logger = getLogger(__filename);\n\nimport XMPPEvents from '../../service/xmpp/XMPPEvents';\n\nimport JitsiVideoSIPGWSession from './JitsiVideoSIPGWSession';\nimport * as Constants from './VideoSIPGWConstants';\n\n/**\n * Main video SIP GW handler. Stores references of all created sessions.\n */\nexport default class VideoSIPGW {\n\n /**\n * Creates new handler.\n *\n * @param {ChatRoom} chatRoom - Tha chat room to handle.\n */\n constructor(chatRoom) {\n this.chatRoom = chatRoom;\n this.eventEmitter = chatRoom.eventEmitter;\n logger.debug('creating VideoSIPGW');\n this.sessions = {};\n\n this.sessionStateChangeListener = this.sessionStateChanged.bind(this);\n\n // VideoSIPGW, JitsiConference and ChatRoom are not reusable and no\n // more than one VideoSIPGW can be created per JitsiConference,\n // so we don't bother to cleanup\n chatRoom.addPresenceListener('jibri-sip-call-state',\n this.handleJibriSIPState.bind(this));\n }\n\n /**\n * Handles presence nodes with name: jibri-sip-call-state.\n *\n * @param {Object} node the presence node Object to handle.\n * Object representing part of the presence received over xmpp.\n */\n handleJibriSIPState(node) {\n const attributes = node.attributes;\n\n if (!attributes) {\n return;\n }\n\n logger.debug('Handle video sip gw state : ', attributes);\n\n const newState = attributes.state;\n\n if (newState === this.state) {\n return;\n }\n\n switch (newState) {\n case Constants.STATE_ON:\n case Constants.STATE_OFF:\n case Constants.STATE_PENDING:\n case Constants.STATE_RETRYING:\n case Constants.STATE_FAILED: {\n const address = attributes.sipaddress;\n\n if (!address) {\n return;\n }\n\n // find the corresponding session and set its state\n const session = this.sessions[address];\n\n if (session) {\n session.setState(newState, attributes.failure_reason);\n } else {\n logger.warn('Video SIP GW session not found:', address);\n }\n }\n }\n }\n\n /**\n * Creates new session and stores its reference if it does not exist or\n * returns an error otherwise.\n *\n * @param {string} sipAddress - The sip address to use.\n * @param {string} displayName - The display name to use.\n * @returns {JitsiVideoSIPGWSession|Error}\n */\n createVideoSIPGWSession(sipAddress, displayName) {\n if (this.sessions[sipAddress]) {\n logger.warn('There was already a Video SIP GW session for address',\n sipAddress);\n\n return new Error(Constants.ERROR_SESSION_EXISTS);\n }\n\n const session = new JitsiVideoSIPGWSession(\n sipAddress, displayName, this.chatRoom);\n\n session.addStateListener(this.sessionStateChangeListener);\n\n this.sessions[sipAddress] = session;\n\n return session;\n }\n\n /**\n * Listener for session state changed. When a session goes to off or failed\n * we delete its reference.\n *\n * @param {options} event - { address, oldState, newState, displayName }\n */\n sessionStateChanged(event) {\n const address = event.address;\n\n if (event.newState === Constants.STATE_OFF\n || event.newState === Constants.STATE_FAILED) {\n const session = this.sessions[address];\n\n if (!session) {\n logger.error('Missing Video SIP GW session with address:',\n address);\n\n return;\n }\n\n session.removeStateListener(this.sessionStateChangeListener);\n delete this.sessions[address];\n }\n\n this.eventEmitter.emit(\n XMPPEvents.VIDEO_SIP_GW_SESSION_STATE_CHANGED,\n event);\n }\n}\n","import { getLogger } from '@jitsi/logger';\nimport { Strophe } from 'strophe.js';\n\nimport * as MediaType from '../../service/RTC/MediaType';\nimport * as SignalingEvents from '../../service/RTC/SignalingEvents';\nimport SignalingLayer, { getMediaTypeFromSourceName } from '../../service/RTC/SignalingLayer';\nimport VideoType from '../../service/RTC/VideoType';\nimport XMPPEvents from '../../service/xmpp/XMPPEvents';\nimport FeatureFlags from '../flags/FeatureFlags';\n\nimport { filterNodeFromPresenceJSON } from './ChatRoom';\n\nconst logger = getLogger(__filename);\n\nexport const SOURCE_INFO_PRESENCE_ELEMENT = 'SourceInfo';\n\n/**\n * Default XMPP implementation of the {@link SignalingLayer} interface. Obtains\n * the data from the MUC presence.\n */\nexport default class SignalingLayerImpl extends SignalingLayer {\n /**\n * Creates new instance.\n */\n constructor() {\n super();\n\n /**\n * A map that stores SSRCs of remote streams. And is used only locally\n * We store the mapping when jingle is received, and later is used\n * onaddstream webrtc event where we have only the ssrc\n * FIXME: This map got filled and never cleaned and can grow during long\n * conference\n * @type {Map} maps SSRC number to jid\n */\n this.ssrcOwners = new Map();\n\n /**\n *\n * @type {ChatRoom|null}\n */\n this.chatRoom = null;\n\n /**\n * @type {Map}\n * @private\n */\n this._localSourceState = { };\n\n /**\n * @type {Map>}\n * @private\n */\n this._remoteSourceState = { };\n\n /**\n * A map that stores the source name of a track identified by it's ssrc.\n * We store the mapping when jingle is received, and later is used\n * onaddstream webrtc event where we have only the ssrc\n * FIXME: This map got filled and never cleaned and can grow during long\n * conference\n * @type {Map} maps SSRC number to source name\n */\n this._sourceNames = new Map();\n }\n\n /**\n * Adds element to the local presence.\n *\n * @returns {void}\n * @private\n */\n _addLocalSourceInfoToPresence() {\n if (this.chatRoom) {\n return this.chatRoom.addOrReplaceInPresence(\n SOURCE_INFO_PRESENCE_ELEMENT,\n { value: JSON.stringify(this._localSourceState) });\n }\n\n return false;\n }\n\n /**\n * Check is given endpoint has advertised in it's presence which means that the source name signaling\n * is used by this endpoint.\n *\n * @param {EndpointId} endpointId\n * @returns {boolean}\n */\n _doesEndpointSendNewSourceInfo(endpointId) {\n const presence = this.chatRoom?.getLastPresence(endpointId);\n\n return Boolean(presence && presence.find(node => node.tagName === SOURCE_INFO_PRESENCE_ELEMENT));\n }\n\n /**\n * Sets the ChatRoom instance used and binds presence listeners.\n * @param {ChatRoom} room\n */\n setChatRoom(room) {\n const oldChatRoom = this.chatRoom;\n\n this.chatRoom = room;\n if (oldChatRoom) {\n oldChatRoom.removePresenceListener(\n 'audiomuted', this._audioMuteHandler);\n oldChatRoom.removePresenceListener(\n 'videomuted', this._videoMuteHandler);\n oldChatRoom.removePresenceListener(\n 'videoType', this._videoTypeHandler);\n if (FeatureFlags.isSourceNameSignalingEnabled()) {\n this._sourceInfoHandler\n && oldChatRoom.removePresenceListener(\n SOURCE_INFO_PRESENCE_ELEMENT, this._sourceInfoHandler);\n this._memberLeftHandler\n && oldChatRoom.removeEventListener(\n XMPPEvents.MUC_MEMBER_LEFT, this._memberLeftHandler);\n }\n }\n if (room) {\n if (FeatureFlags.isSourceNameSignalingEnabled()) {\n this._bindChatRoomEventHandlers(room);\n this._addLocalSourceInfoToPresence();\n } else {\n // TODO the logic below has been duplicated in _bindChatRoomEventHandlers, clean this up once\n // the new impl has been tested well enough\n // SignalingEvents\n this._audioMuteHandler = (node, from) => {\n this.eventEmitter.emit(\n SignalingEvents.PEER_MUTED_CHANGED,\n from, MediaType.AUDIO, node.value === 'true');\n };\n room.addPresenceListener('audiomuted', this._audioMuteHandler);\n\n this._videoMuteHandler = (node, from) => {\n this.eventEmitter.emit(\n SignalingEvents.PEER_MUTED_CHANGED,\n from, MediaType.VIDEO, node.value === 'true');\n };\n room.addPresenceListener('videomuted', this._videoMuteHandler);\n\n this._videoTypeHandler = (node, from) => {\n this.eventEmitter.emit(\n SignalingEvents.PEER_VIDEO_TYPE_CHANGED,\n from, node.value);\n };\n room.addPresenceListener('videoType', this._videoTypeHandler);\n }\n }\n }\n\n /**\n * Binds event listeners to the chat room instance.\n * @param {ChatRoom} room\n * @private\n * @returns {void}\n */\n _bindChatRoomEventHandlers(room) {\n const emitAudioMutedEvent = (endpointId, muted) => {\n this.eventEmitter.emit(\n SignalingEvents.PEER_MUTED_CHANGED,\n endpointId,\n MediaType.AUDIO,\n muted);\n };\n const emitVideoMutedEvent = (endpointId, muted) => {\n this.eventEmitter.emit(\n SignalingEvents.PEER_MUTED_CHANGED,\n endpointId,\n MediaType.VIDEO,\n muted);\n };\n\n // SignalingEvents\n this._audioMuteHandler = (node, from) => {\n if (!this._doesEndpointSendNewSourceInfo(from)) {\n emitAudioMutedEvent(from, node.value === 'true');\n }\n };\n room.addPresenceListener('audiomuted', this._audioMuteHandler);\n\n this._videoMuteHandler = (node, from) => {\n if (!this._doesEndpointSendNewSourceInfo(from)) {\n emitVideoMutedEvent(from, node.value === 'true');\n }\n };\n room.addPresenceListener('videomuted', this._videoMuteHandler);\n\n const emitVideoTypeEvent = (endpointId, videoType) => {\n this.eventEmitter.emit(\n SignalingEvents.PEER_VIDEO_TYPE_CHANGED,\n endpointId, videoType);\n };\n\n this._videoTypeHandler = (node, from) => {\n if (!this._doesEndpointSendNewSourceInfo(from)) {\n emitVideoTypeEvent(from, node.value);\n }\n };\n\n if (!FeatureFlags.isMultiStreamSupportEnabled()) {\n room.addPresenceListener('videoType', this._videoTypeHandler);\n }\n\n this._sourceInfoHandler = (node, mucNick) => {\n const endpointId = mucNick;\n const { value } = node;\n const sourceInfoJSON = JSON.parse(value);\n const emitEventsFromHere = this._doesEndpointSendNewSourceInfo(endpointId);\n const endpointSourceState\n = this._remoteSourceState[endpointId] || (this._remoteSourceState[endpointId] = {});\n\n for (const sourceName of Object.keys(sourceInfoJSON)) {\n const mediaType = getMediaTypeFromSourceName(sourceName);\n const newMutedState = Boolean(sourceInfoJSON[sourceName].muted);\n const oldSourceState = endpointSourceState[sourceName]\n || (endpointSourceState[sourceName] = { sourceName });\n\n if (oldSourceState.muted !== newMutedState) {\n oldSourceState.muted = newMutedState;\n if (emitEventsFromHere && mediaType === MediaType.AUDIO) {\n emitAudioMutedEvent(endpointId, newMutedState);\n } else {\n emitVideoMutedEvent(endpointId, newMutedState);\n }\n }\n\n // Assume a default videoType of 'camera' for video sources.\n const newVideoType = mediaType === MediaType.VIDEO\n ? sourceInfoJSON[sourceName].videoType ?? VideoType.CAMERA\n : undefined;\n\n if (oldSourceState.videoType !== newVideoType) {\n oldSourceState.videoType = newVideoType;\n\n // videoType is not allowed to change on a given JitsiLocalTrack when multi stream support is\n // enabled.\n emitEventsFromHere\n && !FeatureFlags.isMultiStreamSupportEnabled()\n && emitVideoTypeEvent(endpointId, newVideoType);\n }\n }\n\n // Cleanup removed source names\n const newSourceNames = Object.keys(sourceInfoJSON);\n\n for (const sourceName of Object.keys(endpointSourceState)) {\n if (newSourceNames.indexOf(sourceName) === -1) {\n delete endpointSourceState[sourceName];\n }\n }\n };\n room.addPresenceListener('SourceInfo', this._sourceInfoHandler);\n\n // Cleanup when participant leaves\n this._memberLeftHandler = jid => {\n const endpointId = Strophe.getResourceFromJid(jid);\n\n delete this._remoteSourceState[endpointId];\n\n if (FeatureFlags.isSourceNameSignalingEnabled()) {\n for (const [ key, value ] of this.ssrcOwners.entries()) {\n if (value === endpointId) {\n delete this._sourceNames[key];\n }\n }\n }\n };\n\n room.addEventListener(XMPPEvents.MUC_MEMBER_LEFT, this._memberLeftHandler);\n }\n\n /**\n * Finds the first source of given media type for the given endpoint.\n * @param endpointId\n * @param mediaType\n * @returns {SourceInfo|null}\n * @private\n */\n _findEndpointSourceInfoForMediaType(endpointId, mediaType) {\n const remoteSourceState = this._remoteSourceState[endpointId];\n\n if (!remoteSourceState) {\n return null;\n }\n\n for (const sourceInfo of Object.values(remoteSourceState)) {\n const _mediaType = getMediaTypeFromSourceName(sourceInfo.sourceName);\n\n if (_mediaType === mediaType) {\n return sourceInfo;\n }\n }\n\n return null;\n }\n\n /**\n * @inheritDoc\n */\n getPeerMediaInfo(owner, mediaType) {\n const legacyGetPeerMediaInfo = () => {\n if (this.chatRoom) {\n return this.chatRoom.getMediaPresenceInfo(owner, mediaType);\n }\n logger.error('Requested peer media info, before room was set');\n };\n\n if (FeatureFlags.isSourceNameSignalingEnabled()) {\n const lastPresence = this.chatRoom.getLastPresence(owner);\n\n if (!lastPresence) {\n throw new Error(`getPeerMediaInfo - no presence stored for: ${owner}`);\n }\n\n if (!this._doesEndpointSendNewSourceInfo(owner)) {\n return legacyGetPeerMediaInfo();\n }\n\n /**\n * @type {PeerMediaInfo}\n */\n const mediaInfo = {};\n const endpointMediaSource = this._findEndpointSourceInfoForMediaType(owner, mediaType);\n\n // The defaults are provided only, because getPeerMediaInfo is a legacy method. This will be eventually\n // changed into a getSourceInfo method which returns undefined if there's no source. Also there will be\n // no mediaType argument there.\n if (mediaType === MediaType.AUDIO) {\n mediaInfo.muted = endpointMediaSource ? endpointMediaSource.muted : true;\n } else if (mediaType === MediaType.VIDEO) {\n mediaInfo.muted = endpointMediaSource ? endpointMediaSource.muted : true;\n mediaInfo.videoType = endpointMediaSource ? endpointMediaSource.videoType : undefined;\n\n const codecTypeNode = filterNodeFromPresenceJSON(lastPresence, 'jitsi_participant_codecType');\n\n if (codecTypeNode.length > 0) {\n mediaInfo.codecType = codecTypeNode[0].value;\n }\n } else {\n throw new Error(`Unsupported media type: ${mediaType}`);\n }\n\n return mediaInfo;\n }\n\n return legacyGetPeerMediaInfo();\n }\n\n /**\n * @inheritDoc\n */\n getPeerSourceInfo(owner, sourceName) {\n return this._remoteSourceState[owner] ? this._remoteSourceState[owner][sourceName] : undefined;\n }\n\n /**\n * @inheritDoc\n */\n getSSRCOwner(ssrc) {\n return this.ssrcOwners.get(ssrc);\n }\n\n /**\n * Set an SSRC owner.\n * @param {number} ssrc an SSRC to be owned\n * @param {string} endpointId owner's ID (MUC nickname)\n * @throws TypeError if ssrc is not a number\n */\n setSSRCOwner(ssrc, endpointId) {\n if (typeof ssrc !== 'number') {\n throw new TypeError(`SSRC(${ssrc}) must be a number`);\n }\n\n // Now signaling layer instance is shared between different JingleSessionPC instances, so although very unlikely\n // an SSRC conflict could potentially occur. Log a message to make debugging easier.\n const existingOwner = this.ssrcOwners.get(ssrc);\n\n if (existingOwner && existingOwner !== endpointId) {\n logger.error(`SSRC owner re-assigned from ${existingOwner} to ${endpointId}`);\n }\n this.ssrcOwners.set(ssrc, endpointId);\n }\n\n /**\n * Adjusts muted status of given track.\n *\n * @param {SourceName} sourceName - the name of the track's source.\n * @param {boolean} muted - the new muted status.\n * @returns {boolean}\n */\n setTrackMuteStatus(sourceName, muted) {\n if (!this._localSourceState[sourceName]) {\n this._localSourceState[sourceName] = {};\n }\n\n this._localSourceState[sourceName].muted = muted;\n\n if (this.chatRoom) {\n // FIXME This only adjusts the presence, but doesn't actually send it. Here we temporarily rely on\n // the legacy signaling part to send the presence. Remember to add \"send presence\" here when the legacy\n // signaling is removed.\n return this._addLocalSourceInfoToPresence();\n }\n\n return false;\n }\n\n /**\n * Sets track's video type.\n * @param {SourceName} sourceName - the track's source name.\n * @param {VideoType} videoType - the new video type.\n * @returns {boolean}\n */\n setTrackVideoType(sourceName, videoType) {\n if (!this._localSourceState[sourceName]) {\n this._localSourceState[sourceName] = {};\n }\n\n if (this._localSourceState[sourceName].videoType !== videoType) {\n // Include only if not a camera (default)\n this._localSourceState[sourceName].videoType = videoType === VideoType.CAMERA ? undefined : videoType;\n\n // NOTE this doesn't send the actual presence, because is called from the same place where the legacy video\n // type is emitted which does the actual sending. A send presence statement needs to be added when\n // the legacy part is removed.\n return this._addLocalSourceInfoToPresence();\n }\n\n return false;\n }\n\n /**\n * @inheritDoc\n */\n getTrackSourceName(ssrc) {\n return this._sourceNames.get(ssrc);\n }\n\n /**\n * Saves the source name for a track identified by it's ssrc.\n * @param {number} ssrc the ssrc of the target track.\n * @param {SourceName} sourceName the track's source name to save.\n * @throws TypeError if ssrc is not a number\n */\n setTrackSourceName(ssrc, sourceName) {\n if (typeof ssrc !== 'number') {\n throw new TypeError(`SSRC(${ssrc}) must be a number`);\n }\n\n // Now signaling layer instance is shared between different JingleSessionPC instances, so although very unlikely\n // an SSRC conflict could potentially occur. Log a message to make debugging easier.\n const existingName = this._sourceNames.get(ssrc);\n\n if (existingName && existingName !== sourceName) {\n logger.error(`SSRC(${ssrc}) sourceName re-assigned from ${existingName} to ${sourceName}`);\n }\n\n this._sourceNames.set(ssrc, sourceName);\n }\n\n}\n","/* global $ */\n\nimport { getLogger } from '@jitsi/logger';\nimport EventEmitter from 'events';\nimport isEqual from 'lodash.isequal';\nimport { Strophe } from 'strophe.js';\n\nimport * as JitsiConferenceErrors from './JitsiConferenceErrors';\nimport JitsiConferenceEventManager from './JitsiConferenceEventManager';\nimport * as JitsiConferenceEvents from './JitsiConferenceEvents';\nimport JitsiParticipant from './JitsiParticipant';\nimport JitsiTrackError from './JitsiTrackError';\nimport * as JitsiTrackErrors from './JitsiTrackErrors';\nimport * as JitsiTrackEvents from './JitsiTrackEvents';\nimport authenticateAndUpgradeRole from './authenticateAndUpgradeRole';\nimport { CodecSelection } from './modules/RTC/CodecSelection';\nimport RTC from './modules/RTC/RTC';\nimport { SS_DEFAULT_FRAME_RATE } from './modules/RTC/ScreenObtainer';\nimport browser from './modules/browser';\nimport ConnectionQuality from './modules/connectivity/ConnectionQuality';\nimport IceFailedHandling\n from './modules/connectivity/IceFailedHandling';\nimport ParticipantConnectionStatusHandler\n from './modules/connectivity/ParticipantConnectionStatus';\nimport * as DetectionEvents from './modules/detection/DetectionEvents';\nimport NoAudioSignalDetection from './modules/detection/NoAudioSignalDetection';\nimport P2PDominantSpeakerDetection from './modules/detection/P2PDominantSpeakerDetection';\nimport VADAudioAnalyser from './modules/detection/VADAudioAnalyser';\nimport VADNoiseDetection from './modules/detection/VADNoiseDetection';\nimport VADTalkMutedDetection from './modules/detection/VADTalkMutedDetection';\nimport { E2EEncryption } from './modules/e2ee/E2EEncryption';\nimport E2ePing from './modules/e2eping/e2eping';\nimport Jvb121EventGenerator from './modules/event/Jvb121EventGenerator';\nimport FeatureFlags from './modules/flags/FeatureFlags';\nimport ReceiveVideoController from './modules/qualitycontrol/ReceiveVideoController';\nimport SendVideoController from './modules/qualitycontrol/SendVideoController';\nimport RecordingManager from './modules/recording/RecordingManager';\nimport Settings from './modules/settings/Settings';\nimport AudioOutputProblemDetector from './modules/statistics/AudioOutputProblemDetector';\nimport AvgRTPStatsReporter from './modules/statistics/AvgRTPStatsReporter';\nimport SpeakerStatsCollector from './modules/statistics/SpeakerStatsCollector';\nimport Statistics from './modules/statistics/statistics';\nimport Transcriber from './modules/transcription/transcriber';\nimport GlobalOnErrorHandler from './modules/util/GlobalOnErrorHandler';\nimport RandomUtil from './modules/util/RandomUtil';\nimport ComponentsVersions from './modules/version/ComponentsVersions';\nimport VideoSIPGW from './modules/videosipgw/VideoSIPGW';\nimport * as VideoSIPGWConstants from './modules/videosipgw/VideoSIPGWConstants';\nimport SignalingLayerImpl from './modules/xmpp/SignalingLayerImpl';\nimport {\n FEATURE_E2EE,\n FEATURE_JIGASI,\n JITSI_MEET_MUC_TYPE\n} from './modules/xmpp/xmpp';\nimport BridgeVideoType from './service/RTC/BridgeVideoType';\nimport CodecMimeType from './service/RTC/CodecMimeType';\nimport * as MediaType from './service/RTC/MediaType';\nimport RTCEvents from './service/RTC/RTCEvents';\nimport { getSourceNameForJitsiTrack } from './service/RTC/SignalingLayer';\nimport VideoType from './service/RTC/VideoType';\nimport {\n ACTION_JINGLE_RESTART,\n ACTION_JINGLE_SI_RECEIVED,\n ACTION_JINGLE_SI_TIMEOUT,\n ACTION_JINGLE_TERMINATE,\n ACTION_P2P_DECLINED,\n ACTION_P2P_ESTABLISHED,\n ACTION_P2P_FAILED,\n ACTION_P2P_SWITCH_TO_JVB,\n ICE_ESTABLISHMENT_DURATION_DIFF,\n createConferenceEvent,\n createJingleEvent,\n createP2PEvent\n} from './service/statistics/AnalyticsEvents';\nimport * as XMPPEvents from './service/xmpp/XMPPEvents';\n\nconst logger = getLogger(__filename);\n\n/**\n * How long since Jicofo is supposed to send a session-initiate, before\n * {@link ACTION_JINGLE_SI_TIMEOUT} analytics event is sent (in ms).\n * @type {number}\n */\nconst JINGLE_SI_TIMEOUT = 5000;\n\n/**\n * Creates a JitsiConference object with the given name and properties.\n * Note: this constructor is not a part of the public API (objects should be\n * created using JitsiConnection.createConference).\n * @param options.config properties / settings related to the conference that\n * will be created.\n * @param options.name the name of the conference\n * @param options.connection the JitsiConnection object for this\n * JitsiConference.\n * @param {number} [options.config.avgRtpStatsN=15] how many samples are to be\n * collected by {@link AvgRTPStatsReporter}, before arithmetic mean is\n * calculated and submitted to the analytics module.\n * @param {boolean} [options.config.enableIceRestart=false] - enables the ICE\n * restart logic.\n * @param {boolean} [options.config.p2p.enabled] when set to true\n * the peer to peer mode will be enabled. It means that when there are only 2\n * participants in the conference an attempt to make direct connection will be\n * made. If the connection succeeds the conference will stop sending data\n * through the JVB connection and will use the direct one instead.\n * @param {number} [options.config.p2p.backToP2PDelay=5] a delay given in\n * seconds, before the conference switches back to P2P, after the 3rd\n * participant has left the room.\n * @param {number} [options.config.channelLastN=-1] The requested amount of\n * videos are going to be delivered after the value is in effect. Set to -1 for\n * unlimited or all available videos.\n * @param {number} [options.config.forceJVB121Ratio]\n * \"Math.random() < forceJVB121Ratio\" will determine whether a 2 people\n * conference should be moved to the JVB instead of P2P. The decision is made on\n * the responder side, after ICE succeeds on the P2P connection.\n * @constructor\n *\n * FIXME Make all methods which are called from lib-internal classes\n * to non-public (use _). To name a few:\n * {@link JitsiConference.onLocalRoleChanged}\n * {@link JitsiConference.onUserRoleChanged}\n * {@link JitsiConference.onMemberLeft}\n * and so on...\n */\nexport default function JitsiConference(options) {\n if (!options.name || options.name.toLowerCase() !== options.name.toString()) {\n const errmsg\n = 'Invalid conference name (no conference name passed or it '\n + 'contains invalid characters like capital letters)!';\n\n logger.error(errmsg);\n throw new Error(errmsg);\n }\n this.connection = options.connection;\n this.xmpp = this.connection?.xmpp;\n\n if (this.xmpp.isRoomCreated(options.name, options.customDomain)) {\n const errmsg = 'A coference with the same name has already been created!';\n\n delete this.connection;\n delete this.xmpp;\n logger.error(errmsg);\n throw new Error(errmsg);\n }\n this.eventEmitter = new EventEmitter();\n this.options = options;\n this.eventManager = new JitsiConferenceEventManager(this);\n this.participants = {};\n\n /**\n * The signaling layer instance.\n * @type {SignalingLayerImpl}\n * @private\n */\n this._signalingLayer = new SignalingLayerImpl();\n\n this._init(options);\n this.componentsVersions = new ComponentsVersions(this);\n\n /**\n * Jingle session instance for the JVB connection.\n * @type {JingleSessionPC}\n */\n this.jvbJingleSession = null;\n this.lastDominantSpeaker = null;\n this.dtmfManager = null;\n this.somebodySupportsDTMF = false;\n this.authEnabled = false;\n this.startAudioMuted = false;\n this.startVideoMuted = false;\n this.startMutedPolicy = {\n audio: false,\n video: false\n };\n this.isMutedByFocus = false;\n\n // when muted by focus we receive the jid of the initiator of the mute\n this.mutedByFocusActor = null;\n\n this.isVideoMutedByFocus = false;\n\n // when video muted by focus we receive the jid of the initiator of the mute\n this.mutedVideoByFocusActor = null;\n\n // Flag indicates if the 'onCallEnded' method was ever called on this\n // instance. Used to log extra analytics event for debugging purpose.\n // We need to know if the potential issue happened before or after\n // the restart.\n this.wasStopped = false;\n\n // Conference properties, maintained by jicofo.\n this.properties = {};\n\n /**\n * The object which monitors local and remote connection statistics (e.g.\n * sending bitrate) and calculates a number which represents the connection\n * quality.\n */\n this.connectionQuality\n = new ConnectionQuality(this, this.eventEmitter, options);\n\n /**\n * Reports average RTP statistics to the analytics module.\n * @type {AvgRTPStatsReporter}\n */\n this.avgRtpStatsReporter\n = new AvgRTPStatsReporter(this, options.config.avgRtpStatsN || 15);\n\n /**\n * Detects issues with the audio of remote participants.\n * @type {AudioOutputProblemDetector}\n */\n if (!options.config.disableAudioLevels) {\n this._audioOutputProblemDetector = new AudioOutputProblemDetector(this);\n }\n\n /**\n * Indicates whether the connection is interrupted or not.\n */\n this.isJvbConnectionInterrupted = false;\n\n /**\n * The object which tracks active speaker times\n */\n this.speakerStatsCollector = new SpeakerStatsCollector(this);\n\n /* P2P related fields below: */\n\n /**\n * Stores reference to deferred start P2P task. It's created when 3rd\n * participant leaves the room in order to avoid ping pong effect (it\n * could be just a page reload).\n * @type {number|null}\n */\n this.deferredStartP2PTask = null;\n\n const delay\n = parseInt(options.config.p2p && options.config.p2p.backToP2PDelay, 10);\n\n /**\n * A delay given in seconds, before the conference switches back to P2P\n * after the 3rd participant has left.\n * @type {number}\n */\n this.backToP2PDelay = isNaN(delay) ? 5 : delay;\n logger.info(`backToP2PDelay: ${this.backToP2PDelay}`);\n\n /**\n * If set to true it means the P2P ICE is no longer connected.\n * When false it means that P2P ICE (media) connection is up\n * and running.\n * @type {boolean}\n */\n this.isP2PConnectionInterrupted = false;\n\n /**\n * Flag set to true when P2P session has been established\n * (ICE has been connected) and this conference is currently in the peer to\n * peer mode (P2P connection is the active one).\n * @type {boolean}\n */\n this.p2p = false;\n\n /**\n * A JingleSession for the direct peer to peer connection.\n * @type {JingleSessionPC}\n */\n this.p2pJingleSession = null;\n\n this.videoSIPGWHandler = new VideoSIPGW(this.room);\n this.recordingManager = new RecordingManager(this.room);\n\n /**\n * If the conference.joined event has been sent this will store the timestamp when it happened.\n *\n * @type {undefined|number}\n * @private\n */\n this._conferenceJoinAnalyticsEventSent = undefined;\n\n /**\n * End-to-End Encryption. Make it available if supported.\n */\n if (this.isE2EESupported()) {\n logger.info('End-to-End Encryption is supported');\n\n this._e2eEncryption = new E2EEncryption(this);\n }\n\n /**\n * Flag set to true when Jicofo sends a presence message indicating that the max audio sender limit has\n * been reached for the call. Once this is set, unmuting audio will be disabled from the client until it gets reset\n * again by Jicofo.\n */\n this._audioSenderLimitReached = undefined;\n\n /**\n * Flag set to true when Jicofo sends a presence message indicating that the max video sender limit has\n * been reached for the call. Once this is set, unmuting video will be disabled from the client until it gets reset\n * again by Jicofo.\n */\n this._videoSenderLimitReached = undefined;\n}\n\n// FIXME convert JitsiConference to ES6 - ASAP !\nJitsiConference.prototype.constructor = JitsiConference;\n\n/**\n * Create a resource for the a jid. We use the room nickname (the resource part\n * of the occupant JID, see XEP-0045) as the endpoint ID in colibri. We require\n * endpoint IDs to be 8 hex digits because in some cases they get serialized\n * into a 32bit field.\n *\n * @param {string} jid - The id set onto the XMPP connection.\n * @param {boolean} isAuthenticatedUser - Whether or not the user has connected\n * to the XMPP service with a password.\n * @returns {string}\n * @static\n */\nJitsiConference.resourceCreator = function(jid, isAuthenticatedUser) {\n let mucNickname;\n\n if (isAuthenticatedUser) {\n // For authenticated users generate a random ID.\n mucNickname = RandomUtil.randomHexString(8).toLowerCase();\n } else {\n // We try to use the first part of the node (which for anonymous users\n // on prosody is a UUID) to match the previous behavior (and maybe make\n // debugging easier).\n mucNickname = Strophe.getNodeFromJid(jid)?.substr(0, 8)\n .toLowerCase();\n\n // But if this doesn't have the required format we just generate a new\n // random nickname.\n const re = /[0-9a-f]{8}/g;\n\n if (!mucNickname || !re.test(mucNickname)) {\n mucNickname = RandomUtil.randomHexString(8).toLowerCase();\n }\n }\n\n return mucNickname;\n};\n\n/**\n * Initializes the conference object properties\n * @param options {object}\n * @param options.connection {JitsiConnection} overrides this.connection\n */\nJitsiConference.prototype._init = function(options = {}) {\n this.eventManager.setupXMPPListeners();\n\n const { config } = this.options;\n\n // Get the codec preference settings from config.js.\n // 'preferH264' and 'disableH264' settings have been deprecated for a while,\n // 'preferredCodec' and 'disabledCodec' will have precedence over them.\n const codecSettings = {\n disabledCodec: config.videoQuality\n ? config.videoQuality.disabledCodec\n : config.p2p && config.p2p.disableH264 && CodecMimeType.H264,\n enforcePreferredCodec: config.videoQuality && config.videoQuality.enforcePreferredCodec,\n jvbCodec: (config.videoQuality && config.videoQuality.preferredCodec)\n || (config.preferH264 && CodecMimeType.H264),\n p2pCodec: config.p2p\n ? config.p2p.preferredCodec || (config.p2p.preferH264 && CodecMimeType.H264)\n : CodecMimeType.VP8\n };\n\n this.codecSelection = new CodecSelection(this, codecSettings);\n this._statsCurrentId = config.statisticsId ? config.statisticsId : Settings.callStatsUserName;\n this.room = this.xmpp.createRoom(\n this.options.name, {\n ...config,\n statsId: this._statsCurrentId\n },\n JitsiConference.resourceCreator\n );\n\n this._signalingLayer.setChatRoom(this.room);\n\n // Connection interrupted/restored listeners\n this._onIceConnectionInterrupted\n = this._onIceConnectionInterrupted.bind(this);\n this.room.addListener(\n XMPPEvents.CONNECTION_INTERRUPTED, this._onIceConnectionInterrupted);\n\n this._onIceConnectionRestored = this._onIceConnectionRestored.bind(this);\n this.room.addListener(\n XMPPEvents.CONNECTION_RESTORED, this._onIceConnectionRestored);\n\n this._onIceConnectionEstablished\n = this._onIceConnectionEstablished.bind(this);\n this.room.addListener(\n XMPPEvents.CONNECTION_ESTABLISHED, this._onIceConnectionEstablished);\n\n this._updateProperties = this._updateProperties.bind(this);\n this.room.addListener(XMPPEvents.CONFERENCE_PROPERTIES_CHANGED,\n this._updateProperties);\n\n this._sendConferenceJoinAnalyticsEvent = this._sendConferenceJoinAnalyticsEvent.bind(this);\n this.room.addListener(XMPPEvents.MEETING_ID_SET, this._sendConferenceJoinAnalyticsEvent);\n\n this._removeLocalSourceOnReject = this._removeLocalSourceOnReject.bind(this);\n this._updateRoomPresence = this._updateRoomPresence.bind(this);\n this.room.addListener(XMPPEvents.SESSION_ACCEPT, this._updateRoomPresence);\n this.room.addListener(XMPPEvents.SOURCE_ADD, this._updateRoomPresence);\n this.room.addListener(XMPPEvents.SOURCE_ADD_ERROR, this._removeLocalSourceOnReject);\n this.room.addListener(XMPPEvents.SOURCE_REMOVE, this._updateRoomPresence);\n\n this.e2eping = new E2ePing(\n this,\n config,\n (message, to) => {\n try {\n this.sendMessage(\n message, to, true /* sendThroughVideobridge */);\n } catch (error) {\n logger.warn('Failed to send E2E ping request or response.', error && error.msg);\n }\n });\n\n if (!this.rtc) {\n this.rtc = new RTC(this, options);\n this.eventManager.setupRTCListeners();\n if (FeatureFlags.isSourceNameSignalingEnabled()) {\n this._registerRtcListeners(this.rtc);\n }\n }\n\n this.receiveVideoController = new ReceiveVideoController(this, this.rtc);\n this.sendVideoController = new SendVideoController(this, this.rtc);\n\n this.participantConnectionStatus\n = new ParticipantConnectionStatusHandler(\n this.rtc,\n this,\n {\n // These options are not public API, leaving it here only as an entry point through config for tuning\n // up purposes. Default values should be adjusted as soon as optimal values are discovered.\n p2pRtcMuteTimeout: config._p2pConnStatusRtcMuteTimeout,\n rtcMuteTimeout: config._peerConnStatusRtcMuteTimeout,\n outOfLastNTimeout: config._peerConnStatusOutOfLastNTimeout\n });\n this.participantConnectionStatus.init();\n\n // Add the ability to enable callStats only on a percentage of users based on config.js settings.\n let enableCallStats = true;\n\n if (config.testing && config.testing.callStatsThreshold) {\n enableCallStats = (Math.random() * 100) <= config.testing.callStatsThreshold;\n }\n\n if (!this.statistics) {\n this.statistics = new Statistics(this.xmpp, {\n aliasName: this._statsCurrentId,\n userName: config.statisticsDisplayName ? config.statisticsDisplayName : this.myUserId(),\n confID: config.confID || `${this.connection.options.hosts.domain}/${this.options.name}`,\n siteID: config.siteID,\n customScriptUrl: config.callStatsCustomScriptUrl,\n callStatsID: config.callStatsID,\n callStatsSecret: config.callStatsSecret,\n callStatsApplicationLogsDisabled: config.callStatsApplicationLogsDisabled,\n enableCallStats,\n roomName: this.options.name,\n applicationName: config.applicationName,\n getWiFiStatsMethod: config.getWiFiStatsMethod,\n configParams: config.callStatsConfigParams\n });\n Statistics.analytics.addPermanentProperties({\n 'callstats_name': this._statsCurrentId\n });\n\n // Start performance observer for monitoring long tasks\n if (config.longTasksStatsInterval) {\n this.statistics.attachLongTasksStats(this);\n }\n }\n\n this.eventManager.setupChatRoomListeners();\n\n // Always add listeners because on reload we are executing leave and the\n // listeners are removed from statistics module.\n this.eventManager.setupStatisticsListeners();\n\n // Disable VAD processing on Safari since it causes audio input to\n // fail on some of the mobile devices.\n if (config.enableTalkWhileMuted && browser.supportsVADDetection()) {\n // If VAD processor factory method is provided uses VAD based detection, otherwise fallback to audio level\n // based detection.\n if (config.createVADProcessor) {\n logger.info('Using VAD detection for generating talk while muted events');\n\n if (!this._audioAnalyser) {\n this._audioAnalyser = new VADAudioAnalyser(this, config.createVADProcessor);\n }\n\n const vadTalkMutedDetection = new VADTalkMutedDetection();\n\n vadTalkMutedDetection.on(DetectionEvents.VAD_TALK_WHILE_MUTED, () =>\n this.eventEmitter.emit(JitsiConferenceEvents.TALK_WHILE_MUTED));\n\n this._audioAnalyser.addVADDetectionService(vadTalkMutedDetection);\n } else {\n logger.warn('No VAD Processor was provided. Talk while muted detection service was not initialized!');\n }\n }\n\n // Disable noisy mic detection on safari since it causes the audio input to\n // fail on Safari on iPadOS.\n if (config.enableNoisyMicDetection && browser.supportsVADDetection()) {\n if (config.createVADProcessor) {\n if (!this._audioAnalyser) {\n this._audioAnalyser = new VADAudioAnalyser(this, config.createVADProcessor);\n }\n\n const vadNoiseDetection = new VADNoiseDetection();\n\n vadNoiseDetection.on(DetectionEvents.VAD_NOISY_DEVICE, () =>\n this.eventEmitter.emit(JitsiConferenceEvents.NOISY_MIC));\n\n this._audioAnalyser.addVADDetectionService(vadNoiseDetection);\n } else {\n logger.warn('No VAD Processor was provided. Noisy microphone detection service was not initialized!');\n }\n }\n\n // Generates events based on no audio input detector.\n if (config.enableNoAudioDetection) {\n this._noAudioSignalDetection = new NoAudioSignalDetection(this);\n this._noAudioSignalDetection.on(DetectionEvents.NO_AUDIO_INPUT, () => {\n this.eventEmitter.emit(JitsiConferenceEvents.NO_AUDIO_INPUT);\n });\n this._noAudioSignalDetection.on(DetectionEvents.AUDIO_INPUT_STATE_CHANGE, hasAudioSignal => {\n this.eventEmitter.emit(JitsiConferenceEvents.AUDIO_INPUT_STATE_CHANGE, hasAudioSignal);\n });\n }\n\n\n if ('channelLastN' in config) {\n this.setLastN(config.channelLastN);\n }\n\n /**\n * Emits {@link JitsiConferenceEvents.JVB121_STATUS}.\n * @type {Jvb121EventGenerator}\n */\n this.jvb121Status = new Jvb121EventGenerator(this);\n\n // creates dominant speaker detection that works only in p2p mode\n this.p2pDominantSpeakerDetection = new P2PDominantSpeakerDetection(this);\n\n if (config && config.deploymentInfo && config.deploymentInfo.userRegion) {\n this.setLocalParticipantProperty(\n 'region', config.deploymentInfo.userRegion);\n }\n\n // Publish the codec type to presence.\n this.setLocalParticipantProperty('codecType', this.codecSelection.getPreferredCodec());\n\n // Set transcription language presence extension.\n // In case the language config is undefined or has the default value that the transcriber uses\n // (in our case Jigasi uses 'en-US'), don't set the participant property in order to avoid\n // needlessly polluting the presence stanza.\n if (config && config.transcriptionLanguage && config.transcriptionLanguage !== 'en-US') {\n this.setLocalParticipantProperty('transcription_language', config.transcriptionLanguage);\n }\n};\n\n/**\n * Joins the conference.\n * @param password {string} the password\n * @param replaceParticipant {boolean} whether the current join replaces\n * an existing participant with same jwt from the meeting.\n */\nJitsiConference.prototype.join = function(password, replaceParticipant = false) {\n if (this.room) {\n this.room.join(password, replaceParticipant).then(() => this._maybeSetSITimeout());\n }\n};\n\n/**\n * Authenticates and upgrades the role of the local participant/user.\n *\n * @returns {Object} A thenable which (1) settles when the process of\n * authenticating and upgrading the role of the local participant/user finishes\n * and (2) has a cancel method that allows the caller to interrupt the\n * process.\n */\nJitsiConference.prototype.authenticateAndUpgradeRole = function(options) {\n return authenticateAndUpgradeRole.call(this, {\n ...options,\n onCreateResource: JitsiConference.resourceCreator\n });\n};\n\n/**\n * Check if joined to the conference.\n */\nJitsiConference.prototype.isJoined = function() {\n return this.room && this.room.joined;\n};\n\n/**\n * Tells whether or not the P2P mode is enabled in the configuration.\n * @return {boolean}\n */\nJitsiConference.prototype.isP2PEnabled = function() {\n return Boolean(this.options.config.p2p && this.options.config.p2p.enabled)\n\n // FIXME: remove once we have a default config template. -saghul\n || typeof this.options.config.p2p === 'undefined';\n};\n\n/**\n * When in P2P test mode, the conference will not automatically switch to P2P\n * when there 2 participants.\n * @return {boolean}\n */\nJitsiConference.prototype.isP2PTestModeEnabled = function() {\n return Boolean(this.options.config.testing\n && this.options.config.testing.p2pTestMode);\n};\n\n/**\n * Leaves the conference.\n * @returns {Promise}\n */\nJitsiConference.prototype.leave = async function() {\n if (this.participantConnectionStatus) {\n this.participantConnectionStatus.dispose();\n this.participantConnectionStatus = null;\n }\n if (this.avgRtpStatsReporter) {\n this.avgRtpStatsReporter.dispose();\n this.avgRtpStatsReporter = null;\n }\n\n if (this._audioOutputProblemDetector) {\n this._audioOutputProblemDetector.dispose();\n this._audioOutputProblemDetector = null;\n }\n\n if (this.e2eping) {\n this.e2eping.stop();\n this.e2eping = null;\n }\n\n this.getLocalTracks().forEach(track => this.onLocalTrackRemoved(track));\n\n this.rtc.closeBridgeChannel();\n\n this._sendConferenceLeftAnalyticsEvent();\n\n if (this.statistics) {\n this.statistics.dispose();\n }\n\n this._delayedIceFailed && this._delayedIceFailed.cancel();\n\n this._maybeClearSITimeout();\n\n // Close both JVb and P2P JingleSessions\n if (this.jvbJingleSession) {\n this.jvbJingleSession.close();\n this.jvbJingleSession = null;\n }\n if (this.p2pJingleSession) {\n this.p2pJingleSession.close();\n this.p2pJingleSession = null;\n }\n\n // Leave the conference. If this.room == null we are calling second time leave().\n if (!this.room) {\n throw new Error('The conference is has been already left');\n }\n\n const room = this.room;\n\n // Unregister connection state listeners\n room.removeListener(\n XMPPEvents.CONNECTION_INTERRUPTED,\n this._onIceConnectionInterrupted);\n room.removeListener(\n XMPPEvents.CONNECTION_RESTORED,\n this._onIceConnectionRestored);\n room.removeListener(\n XMPPEvents.CONNECTION_ESTABLISHED,\n this._onIceConnectionEstablished);\n\n room.removeListener(\n XMPPEvents.CONFERENCE_PROPERTIES_CHANGED,\n this._updateProperties);\n\n room.removeListener(XMPPEvents.MEETING_ID_SET, this._sendConferenceJoinAnalyticsEvent);\n room.removeListener(XMPPEvents.SESSION_ACCEPT, this._updateRoomPresence);\n room.removeListener(XMPPEvents.SOURCE_ADD, this._updateRoomPresence);\n room.removeListener(XMPPEvents.SOURCE_ADD_ERROR, this._removeLocalSourceOnReject);\n room.removeListener(XMPPEvents.SOURCE_REMOVE, this._updateRoomPresence);\n\n this.eventManager.removeXMPPListeners();\n\n this._signalingLayer.setChatRoom(null);\n\n this.room = null;\n\n let leaveError;\n\n try {\n await room.leave();\n } catch (err) {\n leaveError = err;\n\n // Remove all participants because currently the conference\n // won't be usable anyway. This is done on success automatically\n // by the ChatRoom instance.\n this.getParticipants().forEach(\n participant => this.onMemberLeft(participant.getJid()));\n }\n\n if (this.rtc) {\n this.rtc.destroy();\n }\n\n if (leaveError) {\n throw leaveError;\n }\n};\n\n/**\n * Returns the currently active media session if any.\n *\n * @returns {JingleSessionPC|undefined}\n */\nJitsiConference.prototype.getActiveMediaSession = function() {\n return this.isP2PActive() ? this.p2pJingleSession : this.jvbJingleSession;\n};\n\n/**\n * Returns an array containing all media sessions existing in this conference.\n *\n * @returns {Array}\n */\nJitsiConference.prototype.getMediaSessions = function() {\n const sessions = [];\n\n this.jvbJingleSession && sessions.push(this.jvbJingleSession);\n this.p2pJingleSession && sessions.push(this.p2pJingleSession);\n\n return sessions;\n};\n\n/**\n * Registers event listeners on the RTC instance.\n * @param {RTC} rtc - the RTC module instance used by this conference.\n * @private\n * @returns {void}\n */\nJitsiConference.prototype._registerRtcListeners = function(rtc) {\n rtc.addListener(RTCEvents.DATA_CHANNEL_OPEN, () => {\n for (const localTrack of this.rtc.localTracks) {\n localTrack.isVideoTrack() && this._sendBridgeVideoTypeMessage(localTrack);\n }\n });\n};\n\n/**\n * Sends the 'VideoTypeMessage' to the bridge on the bridge channel so that the bridge can make bitrate allocation\n * decisions based on the video type of the local source.\n *\n * @param {JitsiLocalTrack} localtrack - The track associated with the local source signaled to the bridge.\n * @returns {void}\n * @private\n */\nJitsiConference.prototype._sendBridgeVideoTypeMessage = function(localtrack) {\n let videoType = !localtrack || localtrack.isMuted() ? BridgeVideoType.NONE : localtrack.getVideoType();\n\n if (videoType === BridgeVideoType.DESKTOP && this._desktopSharingFrameRate > SS_DEFAULT_FRAME_RATE) {\n videoType = BridgeVideoType.DESKTOP_HIGH_FPS;\n }\n\n if (FeatureFlags.isSourceNameSignalingEnabled() && localtrack) {\n this.rtc.sendSourceVideoType(localtrack.getSourceName(), videoType);\n } else if (!FeatureFlags.isSourceNameSignalingEnabled()) {\n this.rtc.setVideoType(videoType);\n }\n};\n\n/**\n * Returns name of this conference.\n */\nJitsiConference.prototype.getName = function() {\n return this.options.name.toString();\n};\n\n/**\n * Returns the {@link JitsiConnection} used by this this conference.\n */\nJitsiConference.prototype.getConnection = function() {\n return this.connection;\n};\n\n/**\n * Check if authentication is enabled for this conference.\n */\nJitsiConference.prototype.isAuthEnabled = function() {\n return this.authEnabled;\n};\n\n/**\n * Check if user is logged in.\n */\nJitsiConference.prototype.isLoggedIn = function() {\n return Boolean(this.authIdentity);\n};\n\n/**\n * Get authorized login.\n */\nJitsiConference.prototype.getAuthLogin = function() {\n return this.authIdentity;\n};\n\n/**\n * Check if external authentication is enabled for this conference.\n */\nJitsiConference.prototype.isExternalAuthEnabled = function() {\n return this.room && this.room.moderator.isExternalAuthEnabled();\n};\n\n/**\n * Get url for external authentication.\n * @param {boolean} [urlForPopup] if true then return url for login popup,\n * else url of login page.\n * @returns {Promise}\n */\nJitsiConference.prototype.getExternalAuthUrl = function(urlForPopup) {\n return new Promise((resolve, reject) => {\n if (!this.isExternalAuthEnabled()) {\n reject();\n\n return;\n }\n if (urlForPopup) {\n this.room.moderator.getPopupLoginUrl(resolve, reject);\n } else {\n this.room.moderator.getLoginUrl(resolve, reject);\n }\n });\n};\n\n/**\n * Returns the local tracks of the given media type, or all local tracks if no\n * specific type is given.\n * @param {MediaType} [mediaType] Optional media type (audio or video).\n */\nJitsiConference.prototype.getLocalTracks = function(mediaType) {\n let tracks = [];\n\n if (this.rtc) {\n tracks = this.rtc.getLocalTracks(mediaType);\n }\n\n return tracks;\n};\n\n/**\n * Obtains local audio track.\n * @return {JitsiLocalTrack|null}\n */\nJitsiConference.prototype.getLocalAudioTrack = function() {\n return this.rtc ? this.rtc.getLocalAudioTrack() : null;\n};\n\n/**\n * Obtains local video track.\n * @return {JitsiLocalTrack|null}\n */\nJitsiConference.prototype.getLocalVideoTrack = function() {\n return this.rtc ? this.rtc.getLocalVideoTrack() : null;\n};\n\n/**\n * Returns all the local video tracks.\n * @returns {Array}\n */\nJitsiConference.prototype.getLocalVideoTracks = function() {\n return this.rtc ? this.rtc.getLocalVideoTracks() : null;\n};\n\n/**\n * Obtains the performance statistics.\n * @returns {Object|null}\n */\nJitsiConference.prototype.getPerformanceStats = function() {\n return {\n longTasksStats: this.statistics.getLongTasksStats()\n };\n};\n\n/**\n * Attaches a handler for events(For example - \"participant joined\".) in the\n * conference. All possible event are defined in JitsiConferenceEvents.\n * @param eventId the event ID.\n * @param handler handler for the event.\n *\n * Note: consider adding eventing functionality by extending an EventEmitter\n * impl, instead of rolling ourselves\n */\nJitsiConference.prototype.on = function(eventId, handler) {\n if (this.eventEmitter) {\n this.eventEmitter.on(eventId, handler);\n }\n};\n\n/**\n * Removes event listener\n * @param eventId the event ID.\n * @param [handler] optional, the specific handler to unbind\n *\n * Note: consider adding eventing functionality by extending an EventEmitter\n * impl, instead of rolling ourselves\n */\nJitsiConference.prototype.off = function(eventId, handler) {\n if (this.eventEmitter) {\n this.eventEmitter.removeListener(eventId, handler);\n }\n};\n\n// Common aliases for event emitter\nJitsiConference.prototype.addEventListener = JitsiConference.prototype.on;\nJitsiConference.prototype.removeEventListener = JitsiConference.prototype.off;\n\n/**\n * Receives notifications from other participants about commands / custom events\n * (sent by sendCommand or sendCommandOnce methods).\n * @param command {String} the name of the command\n * @param handler {Function} handler for the command\n */\nJitsiConference.prototype.addCommandListener = function(command, handler) {\n if (this.room) {\n this.room.addPresenceListener(command, handler);\n }\n};\n\n/**\n * Removes command listener\n * @param command {String} the name of the command\n * @param handler {Function} handler to remove for the command\n */\nJitsiConference.prototype.removeCommandListener = function(command, handler) {\n if (this.room) {\n this.room.removePresenceListener(command, handler);\n }\n};\n\n/**\n * Sends text message to the other participants in the conference\n * @param message the text message.\n * @param elementName the element name to encapsulate the message.\n * @deprecated Use 'sendMessage' instead. TODO: this should be private.\n */\nJitsiConference.prototype.sendTextMessage = function(message, elementName = 'body') {\n if (this.room) {\n this.room.sendMessage(message, elementName);\n }\n};\n\n/**\n * Send private text message to another participant of the conference\n * @param id the id of the participant to send a private message.\n * @param message the text message.\n * @param elementName the element name to encapsulate the message.\n * @deprecated Use 'sendMessage' instead. TODO: this should be private.\n */\nJitsiConference.prototype.sendPrivateTextMessage = function(id, message, elementName = 'body') {\n if (this.room) {\n this.room.sendPrivateMessage(id, message, elementName);\n }\n};\n\n/**\n * Send presence command.\n * @param name {String} the name of the command.\n * @param values {Object} with keys and values that will be sent.\n **/\nJitsiConference.prototype.sendCommand = function(name, values) {\n if (this.room) {\n this.room.addOrReplaceInPresence(name, values) && this.room.sendPresence();\n } else {\n logger.warn('Not sending a command, room not initialized.');\n }\n\n};\n\n/**\n * Send presence command one time.\n * @param name {String} the name of the command.\n * @param values {Object} with keys and values that will be sent.\n **/\nJitsiConference.prototype.sendCommandOnce = function(name, values) {\n this.sendCommand(name, values);\n this.removeCommand(name);\n};\n\n/**\n * Removes presence command.\n * @param name {String} the name of the command.\n **/\nJitsiConference.prototype.removeCommand = function(name) {\n if (this.room) {\n this.room.removeFromPresence(name);\n }\n};\n\n/**\n * Sets the display name for this conference.\n * @param name the display name to set\n */\nJitsiConference.prototype.setDisplayName = function(name) {\n if (this.room) {\n const nickKey = 'nick';\n\n // if there is no display name already set, avoid setting an empty one\n if (!name && !this.room.getFromPresence(nickKey)) {\n return;\n }\n\n this.room.addOrReplaceInPresence(nickKey, {\n attributes: { xmlns: 'http://jabber.org/protocol/nick' },\n value: name\n }) && this.room.sendPresence();\n }\n};\n\n/**\n * Set new subject for this conference. (available only for moderator)\n * @param {string} subject new subject\n */\nJitsiConference.prototype.setSubject = function(subject) {\n if (this.room && this.isModerator()) {\n this.room.setSubject(subject);\n } else {\n logger.warn(`Failed to set subject, ${this.room ? '' : 'not in a room, '}${\n this.isModerator() ? '' : 'participant is not a moderator'}`);\n }\n};\n\n/**\n * Get a transcriber object for all current participants in this conference\n * @return {Transcriber} the transcriber object\n */\nJitsiConference.prototype.getTranscriber = function() {\n if (this.transcriber === undefined) {\n this.transcriber = new Transcriber();\n\n // add all existing local audio tracks to the transcriber\n const localAudioTracks = this.getLocalTracks(MediaType.AUDIO);\n\n for (const localAudio of localAudioTracks) {\n this.transcriber.addTrack(localAudio);\n }\n\n // and all remote audio tracks\n const remoteAudioTracks = this.rtc.getRemoteTracks(MediaType.AUDIO);\n\n for (const remoteTrack of remoteAudioTracks) {\n this.transcriber.addTrack(remoteTrack);\n }\n }\n\n return this.transcriber;\n};\n\n/**\n * Returns the transcription status.\n *\n * @returns {String} \"on\" or \"off\".\n */\nJitsiConference.prototype.getTranscriptionStatus = function() {\n return this.room.transcriptionStatus;\n};\n\n/**\n * Adds JitsiLocalTrack object to the conference.\n * @param {JitsiLocalTrack} track the JitsiLocalTrack object.\n * @returns {Promise}\n * @throws {Error} if the specified track is a video track and there is already\n * another video track in the conference.\n */\nJitsiConference.prototype.addTrack = function(track) {\n const mediaType = track.getType();\n const localTracks = this.rtc.getLocalTracks(mediaType);\n\n // Ensure there's exactly 1 local track of each media type in the conference.\n if (localTracks.length > 0) {\n // Don't be excessively harsh and severe if the API client happens to attempt to add the same local track twice.\n if (track === localTracks[0]) {\n return Promise.resolve(track);\n }\n\n if (FeatureFlags.isMultiStreamSupportEnabled() && mediaType === MediaType.VIDEO) {\n const addTrackPromises = [];\n\n this.p2pJingleSession && addTrackPromises.push(this.p2pJingleSession.addTrack(track));\n this.jvbJingleSession && addTrackPromises.push(this.jvbJingleSession.addTrack(track));\n\n return Promise.all(addTrackPromises)\n .then(() => {\n this._setupNewTrack(track);\n\n // TODO Update presence and sent videoType message.\n if (this.isMutedByFocus || this.isVideoMutedByFocus) {\n this._fireMuteChangeEvent(track);\n }\n });\n }\n\n return Promise.reject(new Error(`Cannot add second ${mediaType} track to the conference`));\n }\n\n return this.replaceTrack(null, track);\n};\n\n/**\n * Fires TRACK_AUDIO_LEVEL_CHANGED change conference event (for local tracks).\n * @param {number} audioLevel the audio level\n * @param {TraceablePeerConnection} [tpc]\n */\nJitsiConference.prototype._fireAudioLevelChangeEvent = function(audioLevel, tpc) {\n const activeTpc = this.getActivePeerConnection();\n\n // There will be no TraceablePeerConnection if audio levels do not come from\n // a peerconnection. LocalStatsCollector.js measures audio levels using Web\n // Audio Analyser API and emits local audio levels events through\n // JitsiTrack.setAudioLevel, but does not provide TPC instance which is\n // optional.\n if (!tpc || activeTpc === tpc) {\n this.eventEmitter.emit(\n JitsiConferenceEvents.TRACK_AUDIO_LEVEL_CHANGED,\n this.myUserId(), audioLevel);\n }\n};\n\n/**\n * Fires TRACK_MUTE_CHANGED change conference event.\n * @param track the JitsiTrack object related to the event.\n */\nJitsiConference.prototype._fireMuteChangeEvent = function(track) {\n // check if track was muted by focus and now is unmuted by user\n if (this.isMutedByFocus && track.isAudioTrack() && !track.isMuted()) {\n this.isMutedByFocus = false;\n\n // unmute local user on server\n this.room.muteParticipant(this.room.myroomjid, false, MediaType.AUDIO);\n } else if (this.isVideoMutedByFocus && track.isVideoTrack() && !track.isMuted()) {\n this.isVideoMutedByFocus = false;\n\n // unmute local user on server\n this.room.muteParticipant(this.room.myroomjid, false, MediaType.VIDEO);\n }\n\n let actorParticipant;\n\n if (this.mutedByFocusActor && track.isAudioTrack()) {\n const actorId = Strophe.getResourceFromJid(this.mutedByFocusActor);\n\n actorParticipant = this.participants[actorId];\n } else if (this.mutedVideoByFocusActor && track.isVideoTrack()) {\n const actorId = Strophe.getResourceFromJid(this.mutedVideoByFocusActor);\n\n actorParticipant = this.participants[actorId];\n }\n\n // Send the video type message to the bridge if the track is not removed/added to the pc as part of\n // the mute/unmute operation. This currently happens only on Firefox.\n if (track.isVideoTrack() && !browser.doesVideoMuteByStreamRemove()) {\n this._sendBridgeVideoTypeMessage(track);\n }\n\n this.eventEmitter.emit(JitsiConferenceEvents.TRACK_MUTE_CHANGED, track, actorParticipant);\n};\n\n/**\n * Returns the list of local tracks that need to be added to the peerconnection on join.\n * This takes the startAudioMuted/startVideoMuted flags into consideration since we do not\n * want to add the tracks if the user joins the call audio/video muted. The tracks will be\n * added when the user unmutes for the first time.\n * @returns {Array} - list of local tracks that are unmuted.\n */\nJitsiConference.prototype._getInitialLocalTracks = function() {\n // Always add the audio track on certain platforms:\n // * Safari / WebKit: because of a known issue where audio playout doesn't happen\n // if the user joins audio and video muted.\n // * React Native: after iOS 15, if a user joins muted they won't be able to unmute.\n return this.getLocalTracks()\n .filter(track => {\n const trackType = track.getType();\n\n if (trackType === MediaType.AUDIO\n && (!this.isStartAudioMuted() || browser.isWebKitBased() || browser.isReactNative())) {\n return true;\n } else if (trackType === MediaType.VIDEO && !this.isStartVideoMuted()) {\n return true;\n }\n\n return false;\n });\n};\n\n/**\n * Clear JitsiLocalTrack properties and listeners.\n * @param track the JitsiLocalTrack object.\n */\nJitsiConference.prototype.onLocalTrackRemoved = function(track) {\n track.setConference(null);\n this.rtc.removeLocalTrack(track);\n track.removeEventListener(JitsiTrackEvents.TRACK_MUTE_CHANGED, track.muteHandler);\n if (track.isAudioTrack()) {\n track.removeEventListener(JitsiTrackEvents.TRACK_AUDIO_LEVEL_CHANGED, track.audioLevelHandler);\n }\n\n // send event for stopping screen sharing\n // FIXME: we assume we have only one screen sharing track\n // if we change this we need to fix this check\n if (track.isVideoTrack() && track.videoType === VideoType.DESKTOP) {\n this.statistics.sendScreenSharingEvent(false);\n }\n\n this.eventEmitter.emit(JitsiConferenceEvents.TRACK_REMOVED, track);\n};\n\n/**\n * Removes JitsiLocalTrack from the conference and performs\n * a new offer/answer cycle.\n * @param {JitsiLocalTrack} track\n * @returns {Promise}\n */\nJitsiConference.prototype.removeTrack = function(track) {\n return this.replaceTrack(track, null);\n};\n\n/**\n * Replaces oldTrack with newTrack and performs a single offer/answer\n * cycle after both operations are done. Either oldTrack or newTrack\n * can be null; replacing a valid 'oldTrack' with a null 'newTrack'\n * effectively just removes 'oldTrack'\n * @param {JitsiLocalTrack} oldTrack the current stream in use to be replaced\n * @param {JitsiLocalTrack} newTrack the new stream to use\n * @returns {Promise} resolves when the replacement is finished\n */\nJitsiConference.prototype.replaceTrack = function(oldTrack, newTrack) {\n const oldVideoType = oldTrack?.getVideoType();\n const newVideoType = newTrack?.getVideoType();\n\n if (FeatureFlags.isMultiStreamSupportEnabled() && oldTrack && newTrack && oldVideoType !== newVideoType) {\n throw new Error(`Replacing a track of videoType=${oldVideoType} with a track of videoType=${newVideoType} is`\n + ' not supported in this mode.');\n }\n const oldTrackBelongsToConference = this === oldTrack?.conference;\n\n if (oldTrackBelongsToConference && oldTrack.disposed) {\n return Promise.reject(new JitsiTrackError(JitsiTrackErrors.TRACK_IS_DISPOSED));\n }\n if (newTrack?.disposed) {\n return Promise.reject(new JitsiTrackError(JitsiTrackErrors.TRACK_IS_DISPOSED));\n }\n\n if (oldTrack && !oldTrackBelongsToConference) {\n logger.warn(`JitsiConference.replaceTrack oldTrack (${oldTrack} does not belong to this conference`);\n }\n\n // Now replace the stream at the lower levels\n return this._doReplaceTrack(oldTrackBelongsToConference ? oldTrack : null, newTrack)\n .then(() => {\n oldTrackBelongsToConference && this.onLocalTrackRemoved(oldTrack);\n newTrack && this._setupNewTrack(newTrack);\n\n // Send 'VideoTypeMessage' on the bridge channel when a video track is added/removed.\n if ((oldTrackBelongsToConference && oldTrack?.isVideoTrack()) || newTrack?.isVideoTrack()) {\n this._sendBridgeVideoTypeMessage(newTrack);\n }\n\n // updates presence when we replace the video tracks desktop with screen and screen with desktop\n if (oldTrackBelongsToConference && oldTrack?.isVideoTrack()\n\n // we do not want to send presence update during setEffect switching, which does remove and then add\n && !(oldTrack?._setEffectInProgress || newTrack?._setEffectInProgress)) {\n this._updateRoomPresence(this.getActiveMediaSession());\n }\n\n if (newTrack !== null && (this.isMutedByFocus || this.isVideoMutedByFocus)) {\n this._fireMuteChangeEvent(newTrack);\n }\n\n return Promise.resolve();\n })\n .catch(error => {\n logger.error(`replaceTrack failed: ${error?.stack}`);\n\n return Promise.reject(error);\n });\n};\n\n/**\n * Replaces the tracks at the lower level by going through the Jingle session\n * and WebRTC peer connection. The method will resolve immediately if there is\n * currently no JingleSession started.\n * @param {JitsiLocalTrack|null} oldTrack the track to be removed during\n * the process or null if the method should act as \"add track\"\n * @param {JitsiLocalTrack|null} newTrack the new track to be added or\n * null if the method should act as \"remove track\"\n * @return {Promise} resolved when the process is done or rejected with a string\n * which describes the error.\n * @private\n */\nJitsiConference.prototype._doReplaceTrack = function(oldTrack, newTrack) {\n const replaceTrackPromises = [];\n\n if (this.jvbJingleSession) {\n replaceTrackPromises.push(this.jvbJingleSession.replaceTrack(oldTrack, newTrack));\n } else {\n logger.info('_doReplaceTrack - no JVB JingleSession');\n }\n\n if (this.p2pJingleSession) {\n replaceTrackPromises.push(this.p2pJingleSession.replaceTrack(oldTrack, newTrack));\n } else {\n logger.info('_doReplaceTrack - no P2P JingleSession');\n }\n\n return Promise.all(replaceTrackPromises);\n};\n\n/**\n * Handler for when a source-add for a local source is rejected by Jicofo.\n *\n * @param {JingleSessionPC} jingleSession - The media session.\n * @param {Error} error - The error message.\n * @param {MediaType} mediaType - The media type of the track associated with the source that was rejected.\n * @returns {void}\n */\nJitsiConference.prototype._removeLocalSourceOnReject = function(jingleSession, error, mediaType) {\n if (!jingleSession) {\n return;\n }\n logger.warn(`Source-add rejected on ${jingleSession}, reason=\"${error?.reason}\", message=\"${error?.msg}\"`);\n const track = this.getLocalTracks(mediaType)[0];\n\n this.eventEmitter.emit(JitsiConferenceEvents.TRACK_UNMUTE_REJECTED, track);\n};\n\n/**\n * Operations related to creating a new track\n * @param {JitsiLocalTrack} newTrack the new track being created\n */\nJitsiConference.prototype._setupNewTrack = function(newTrack) {\n const mediaType = newTrack.getType();\n\n if (newTrack.isAudioTrack() || (newTrack.isVideoTrack() && newTrack.videoType !== VideoType.DESKTOP)) {\n // Report active device to statistics\n const devices = RTC.getCurrentlyAvailableMediaDevices();\n const device = devices\n .find(d => d.kind === `${newTrack.getTrack().kind}input` && d.label === newTrack.getTrack().label);\n\n if (device) {\n Statistics.sendActiveDeviceListEvent(RTC.getEventDataForActiveDevice(device));\n }\n }\n\n // Create a source name for this track if it doesn't exist.\n if (FeatureFlags.isSourceNameSignalingEnabled() && !newTrack.getSourceName()) {\n const sourceName = getSourceNameForJitsiTrack(\n this.myUserId(),\n mediaType,\n this.getLocalTracks(mediaType)?.length);\n\n newTrack.setSourceName(sourceName);\n }\n\n this.rtc.addLocalTrack(newTrack);\n newTrack.setConference(this);\n\n // Add event handlers.\n newTrack.muteHandler = this._fireMuteChangeEvent.bind(this, newTrack);\n newTrack.addEventListener(JitsiTrackEvents.TRACK_MUTE_CHANGED, newTrack.muteHandler);\n\n if (newTrack.isAudioTrack()) {\n newTrack.audioLevelHandler = this._fireAudioLevelChangeEvent.bind(this);\n newTrack.addEventListener(JitsiTrackEvents.TRACK_AUDIO_LEVEL_CHANGED, newTrack.audioLevelHandler);\n }\n\n this.eventEmitter.emit(JitsiConferenceEvents.TRACK_ADDED, newTrack);\n};\n\n/**\n * Sets the video type.\n * @param track\n * @return true if video type was changed in presence.\n * @private\n */\nJitsiConference.prototype._setNewVideoType = function(track) {\n let videoTypeChanged = false;\n\n if (FeatureFlags.isSourceNameSignalingEnabled() && track) {\n videoTypeChanged = this._signalingLayer.setTrackVideoType(track.getSourceName(), track.videoType);\n }\n\n if (!FeatureFlags.isMultiStreamSupportEnabled()) {\n const videoTypeTagName = 'videoType';\n\n // If track is missing we revert to default type Camera, the case where we screenshare and\n // we return to be video muted.\n const trackVideoType = track ? track.videoType : VideoType.CAMERA;\n\n // If video type is camera and there is no videoType in presence, we skip adding it, as this is the default one\n if (trackVideoType !== VideoType.CAMERA || this.room.getFromPresence(videoTypeTagName)) {\n // We will not use this.sendCommand here to avoid sending the presence immediately, as later we may also\n // set the mute status.\n const legacyTypeChanged = this.room.addOrReplaceInPresence(videoTypeTagName, { value: trackVideoType });\n\n videoTypeChanged = videoTypeChanged || legacyTypeChanged;\n }\n }\n\n return videoTypeChanged;\n};\n\n/**\n * Sets mute status.\n * @param mediaType\n * @param localTrack\n * @param isMuted\n * @param true when presence was changed, false otherwise.\n * @private\n */\nJitsiConference.prototype._setTrackMuteStatus = function(mediaType, localTrack, isMuted) {\n let presenceChanged = false;\n\n if (FeatureFlags.isSourceNameSignalingEnabled() && localTrack) {\n presenceChanged = this._signalingLayer.setTrackMuteStatus(localTrack.getSourceName(), isMuted);\n }\n\n // Add the 'audioMuted' and 'videoMuted' tags when source name signaling is enabled for backward compatibility.\n // It won't be used anymore when multiple stream support is enabled.\n if (!FeatureFlags.isMultiStreamSupportEnabled()) {\n let audioMuteChanged, videoMuteChanged;\n\n if (!this.room) {\n return false;\n }\n\n if (mediaType === MediaType.AUDIO) {\n audioMuteChanged = this.room.addAudioInfoToPresence(isMuted);\n } else {\n videoMuteChanged = this.room.addVideoInfoToPresence(isMuted);\n }\n\n presenceChanged = presenceChanged || audioMuteChanged || videoMuteChanged;\n }\n\n return presenceChanged;\n};\n\n/**\n * Method called by the {@link JitsiLocalTrack} (a video one) in order to add\n * back the underlying WebRTC MediaStream to the PeerConnection (which has\n * removed on video mute).\n * @param {JitsiLocalTrack} track the local track that will be added as part of\n * the unmute operation.\n * @return {Promise} resolved when the process is done or rejected with a string\n * which describes the error.\n */\nJitsiConference.prototype._addLocalTrackAsUnmute = function(track) {\n const addAsUnmutePromises = [];\n\n if (this.jvbJingleSession) {\n addAsUnmutePromises.push(this.jvbJingleSession.addTrackAsUnmute(track));\n } else {\n logger.debug('Add local MediaStream as unmute - no JVB Jingle session started yet');\n }\n\n if (this.p2pJingleSession) {\n addAsUnmutePromises.push(this.p2pJingleSession.addTrackAsUnmute(track));\n } else {\n logger.debug('Add local MediaStream as unmute - no P2P Jingle session started yet');\n }\n\n return Promise.allSettled(addAsUnmutePromises);\n};\n\n/**\n * Method called by the {@link JitsiLocalTrack} (a video one) in order to remove\n * the underlying WebRTC MediaStream from the PeerConnection. The purpose of\n * that is to stop sending any data and turn off the HW camera device.\n * @param {JitsiLocalTrack} track the local track that will be removed.\n * @return {Promise}\n */\nJitsiConference.prototype._removeLocalTrackAsMute = function(track) {\n const removeAsMutePromises = [];\n\n if (this.jvbJingleSession) {\n removeAsMutePromises.push(this.jvbJingleSession.removeTrackAsMute(track));\n } else {\n logger.debug('Remove local MediaStream - no JVB JingleSession started yet');\n }\n if (this.p2pJingleSession) {\n removeAsMutePromises.push(this.p2pJingleSession.removeTrackAsMute(track));\n } else {\n logger.debug('Remove local MediaStream - no P2P JingleSession started yet');\n }\n\n return Promise.allSettled(removeAsMutePromises);\n};\n\n/**\n * Get role of the local user.\n * @returns {string} user role: 'moderator' or 'none'\n */\nJitsiConference.prototype.getRole = function() {\n return this.room.role;\n};\n\n/**\n * Returns whether or not the current conference has been joined as a hidden\n * user.\n *\n * @returns {boolean|null} True if hidden, false otherwise. Will return null if\n * no connection is active.\n */\nJitsiConference.prototype.isHidden = function() {\n if (!this.connection) {\n return null;\n }\n\n return Strophe.getDomainFromJid(this.connection.getJid())\n === this.options.config.hiddenDomain;\n};\n\n/**\n * Check if local user is moderator.\n * @returns {boolean|null} true if local user is moderator, false otherwise. If\n * we're no longer in the conference room then null is returned.\n */\nJitsiConference.prototype.isModerator = function() {\n return this.room ? this.room.isModerator() : null;\n};\n\n/**\n * Set password for the room.\n * @param {string} password new password for the room.\n * @returns {Promise}\n */\nJitsiConference.prototype.lock = function(password) {\n if (!this.isModerator()) {\n return Promise.reject(new Error('You are not moderator.'));\n }\n\n return new Promise((resolve, reject) => {\n this.room.lockRoom(\n password || '',\n () => resolve(),\n err => reject(err),\n () => reject(JitsiConferenceErrors.PASSWORD_NOT_SUPPORTED));\n });\n};\n\n/**\n * Remove password from the room.\n * @returns {Promise}\n */\nJitsiConference.prototype.unlock = function() {\n return this.lock();\n};\n\n/**\n * Elects the participant with the given id to be the selected participant in\n * order to receive higher video quality (if simulcast is enabled).\n * Or cache it if channel is not created and send it once channel is available.\n * @param participantId the identifier of the participant\n * @throws NetworkError or InvalidStateError or Error if the operation fails.\n * @returns {void}\n */\nJitsiConference.prototype.selectParticipant = function(participantId) {\n this.selectParticipants([ participantId ]);\n};\n\n/*\n * Elects participants with given ids to be the selected participants in order\n * to receive higher video quality (if simulcast is enabled). The argument\n * should be an array of participant id strings or an empty array; an error will\n * be thrown if a non-array is passed in. The error is thrown as a layer of\n * protection against passing an invalid argument, as the error will happen in\n * the bridge and may not be visible in the client.\n *\n * @param {Array} participantIds - An array of identifiers for\n * participants.\n * @returns {void}\n */\nJitsiConference.prototype.selectParticipants = function(participantIds) {\n if (!Array.isArray(participantIds)) {\n throw new Error('Invalid argument; participantIds must be an array.');\n }\n\n this.receiveVideoController.selectEndpoints(participantIds);\n};\n\n/**\n * Obtains the current value for \"lastN\". See {@link setLastN} for more info.\n * @returns {number}\n */\nJitsiConference.prototype.getLastN = function() {\n return this.receiveVideoController.getLastN();\n};\n\n/**\n * Obtains the forwarded sources list in this conference.\n * @return {Array|null}\n */\nJitsiConference.prototype.getForwardedSources = function() {\n return this.rtc.getForwardedSources();\n};\n\n/**\n * Selects a new value for \"lastN\". The requested amount of videos are going\n * to be delivered after the value is in effect. Set to -1 for unlimited or\n * all available videos.\n * @param lastN the new number of videos the user would like to receive.\n * @throws Error or RangeError if the given value is not a number or is smaller\n * than -1.\n */\nJitsiConference.prototype.setLastN = function(lastN) {\n if (!Number.isInteger(lastN) && !Number.parseInt(lastN, 10)) {\n throw new Error(`Invalid value for lastN: ${lastN}`);\n }\n const n = Number(lastN);\n\n if (n < -1) {\n throw new RangeError('lastN cannot be smaller than -1');\n }\n this.receiveVideoController.setLastN(n);\n\n // If the P2P session is not fully established yet, we wait until it gets\n // established.\n if (this.p2pJingleSession) {\n const isVideoActive = n !== 0;\n\n this.p2pJingleSession\n .setMediaTransferActive(true, isVideoActive)\n .catch(error => {\n logger.error(\n `Failed to adjust video transfer status (${isVideoActive})`,\n error);\n });\n }\n};\n\n/**\n * Checks if the participant given by participantId is currently included in\n * the last N.\n * @param {string} participantId the identifier of the participant we would\n * like to check.\n * @return {boolean} true if the participant with id is in the last N set or\n * if there's no last N set, false otherwise.\n * @deprecated this method should never be used to figure out the UI, but\n * {@link ParticipantConnectionStatus} should be used instead.\n */\nJitsiConference.prototype.isInLastN = function(participantId) {\n return this.rtc.isInLastN(participantId);\n};\n\n/**\n * @return Array an array of all participants in this\n * conference.\n */\nJitsiConference.prototype.getParticipants = function() {\n return Object.values(this.participants);\n};\n\n/**\n * Returns the number of participants in the conference, including the local\n * participant.\n * @param countHidden {boolean} Whether or not to include hidden participants\n * in the count. Default: false.\n **/\nJitsiConference.prototype.getParticipantCount = function(countHidden = false) {\n let participants = this.getParticipants();\n\n if (!countHidden) {\n participants = participants.filter(p => !p.isHidden());\n }\n\n // Add one for the local participant.\n return participants.length + 1;\n};\n\n/**\n * @returns {JitsiParticipant} the participant in this conference with the\n * specified id (or undefined if there isn't one).\n * @param id the id of the participant.\n */\nJitsiConference.prototype.getParticipantById = function(id) {\n return this.participants[id];\n};\n\n/**\n * Grant owner rights to the participant.\n * @param {string} id id of the participant to grant owner rights to.\n */\nJitsiConference.prototype.grantOwner = function(id) {\n const participant = this.getParticipantById(id);\n\n if (!participant) {\n return;\n }\n this.room.setAffiliation(participant.getJid(), 'owner');\n};\n\n/**\n * Revoke owner rights to the participant or local Participant as\n * the user might want to refuse to be a moderator.\n * @param {string} id id of the participant to revoke owner rights to.\n */\nJitsiConference.prototype.revokeOwner = function(id) {\n const participant = this.getParticipantById(id);\n const isMyself = this.myUserId() === id;\n const role = this.isMembersOnly() ? 'member' : 'none';\n\n if (isMyself) {\n this.room.setAffiliation(this.room.myroomjid, role);\n } else if (participant) {\n this.room.setAffiliation(participant.getJid(), role);\n }\n};\n\n\n/**\n * Kick participant from this conference.\n * @param {string} id id of the participant to kick\n * @param {string} reason reason of the participant to kick\n */\nJitsiConference.prototype.kickParticipant = function(id, reason) {\n const participant = this.getParticipantById(id);\n\n if (!participant) {\n return;\n }\n this.room.kick(participant.getJid(), reason);\n};\n\n/**\n * Maybe clears the timeout which emits {@link ACTION_JINGLE_SI_TIMEOUT}\n * analytics event.\n * @private\n */\nJitsiConference.prototype._maybeClearSITimeout = function() {\n if (this._sessionInitiateTimeout\n && (this.jvbJingleSession || this.getParticipantCount() < 2)) {\n window.clearTimeout(this._sessionInitiateTimeout);\n this._sessionInitiateTimeout = null;\n }\n};\n\n/**\n * Sets a timeout which will emit {@link ACTION_JINGLE_SI_TIMEOUT} analytics\n * event.\n * @private\n */\nJitsiConference.prototype._maybeSetSITimeout = function() {\n // Jicofo is supposed to invite if there are at least 2 participants\n if (!this.jvbJingleSession\n && this.getParticipantCount() >= 2\n && !this._sessionInitiateTimeout) {\n this._sessionInitiateTimeout = window.setTimeout(() => {\n this._sessionInitiateTimeout = null;\n Statistics.sendAnalytics(createJingleEvent(\n ACTION_JINGLE_SI_TIMEOUT,\n {\n p2p: false,\n value: JINGLE_SI_TIMEOUT\n }));\n }, JINGLE_SI_TIMEOUT);\n }\n};\n\n/**\n * Mutes a participant.\n * @param {string} id The id of the participant to mute.\n */\nJitsiConference.prototype.muteParticipant = function(id, mediaType) {\n const muteMediaType = mediaType ? mediaType : MediaType.AUDIO;\n\n if (muteMediaType !== MediaType.AUDIO && muteMediaType !== MediaType.VIDEO) {\n logger.error(`Unsupported media type: ${muteMediaType}`);\n\n return;\n }\n\n const participant = this.getParticipantById(id);\n\n if (!participant) {\n return;\n }\n this.room.muteParticipant(participant.getJid(), true, muteMediaType);\n};\n\n/* eslint-disable max-params */\n\n/**\n * Notifies this JitsiConference that a new member has joined its chat room.\n *\n * FIXME This should NOT be exposed!\n *\n * @param jid the jid of the participant in the MUC\n * @param nick the display name of the participant\n * @param role the role of the participant in the MUC\n * @param isHidden indicates if this is a hidden participant (system\n * participant for example a recorder).\n * @param statsID the participant statsID (optional)\n * @param status the initial status if any\n * @param identity the member identity, if any\n * @param botType the member botType, if any\n * @param fullJid the member full jid, if any\n * @param features the member botType, if any\n * @param isReplaceParticipant whether this join replaces a participant with\n * the same jwt.\n */\nJitsiConference.prototype.onMemberJoined = function(\n jid, nick, role, isHidden, statsID, status, identity, botType, fullJid, features, isReplaceParticipant) {\n const id = Strophe.getResourceFromJid(jid);\n\n if (id === 'focus' || this.myUserId() === id) {\n return;\n }\n\n const participant\n = new JitsiParticipant(jid, this, nick, isHidden, statsID, status, identity);\n\n participant.setRole(role);\n participant.setBotType(botType);\n participant.setFeatures(features);\n participant.setIsReplacing(isReplaceParticipant);\n\n this.participants[id] = participant;\n this.eventEmitter.emit(\n JitsiConferenceEvents.USER_JOINED,\n id,\n participant);\n\n this._updateFeatures(participant);\n\n // maybeStart only if we had finished joining as then we will have information for the number of participants\n if (this.isJoined()) {\n this._maybeStartOrStopP2P();\n }\n\n this._maybeSetSITimeout();\n};\n\n/* eslint-enable max-params */\n\n/**\n * Get notified when we joined the room.\n *\n * FIXME This should NOT be exposed!\n *\n * @private\n */\nJitsiConference.prototype._onMucJoined = function() {\n this._maybeStartOrStopP2P();\n};\n\n/**\n * Updates features for a participant.\n * @param {JitsiParticipant} participant - The participant to query for features.\n * @returns {void}\n * @private\n */\nJitsiConference.prototype._updateFeatures = function(participant) {\n participant.getFeatures()\n .then(features => {\n participant._supportsDTMF = features.has('urn:xmpp:jingle:dtmf:0');\n this.updateDTMFSupport();\n\n if (features.has(FEATURE_JIGASI)) {\n participant.setProperty('features_jigasi', true);\n }\n\n if (features.has(FEATURE_E2EE)) {\n participant.setProperty('features_e2ee', true);\n }\n })\n .catch(() => false);\n};\n\n/**\n * Get notified when member bot type had changed.\n * @param jid the member jid\n * @param botType the new botType value\n * @private\n */\nJitsiConference.prototype._onMemberBotTypeChanged = function(jid, botType) {\n\n // find the participant and mark it as non bot, as the real one will join\n // in a moment\n const peers = this.getParticipants();\n const botParticipant = peers.find(p => p.getJid() === jid);\n\n if (botParticipant) {\n botParticipant.setBotType(botType);\n const id = Strophe.getResourceFromJid(jid);\n\n this.eventEmitter.emit(\n JitsiConferenceEvents.BOT_TYPE_CHANGED,\n id,\n botType);\n }\n\n // if botType changed to undefined, botType was removed, in case of\n // poltergeist mode this is the moment when the poltergeist had exited and\n // the real participant had already replaced it.\n // In this case we can check and try p2p\n if (!botParticipant.getBotType()) {\n this._maybeStartOrStopP2P();\n }\n};\n\nJitsiConference.prototype.onMemberLeft = function(jid) {\n const id = Strophe.getResourceFromJid(jid);\n\n if (id === 'focus' || this.myUserId() === id) {\n return;\n }\n\n const participant = this.participants[id];\n const mediaSessions = this.getMediaSessions();\n let tracksToBeRemoved = [];\n\n for (const session of mediaSessions) {\n const remoteTracks = session.peerconnection.getRemoteTracks(id);\n\n remoteTracks && (tracksToBeRemoved = [ ...tracksToBeRemoved, ...remoteTracks ]);\n\n // Remove the ssrcs from the remote description and renegotiate.\n session.removeRemoteStreamsOnLeave(id);\n }\n\n // Fire the event before renegotiation is done so that the thumbnails can be removed immediately.\n tracksToBeRemoved.forEach(track => {\n this.eventEmitter.emit(JitsiConferenceEvents.TRACK_REMOVED, track);\n });\n\n if (participant) {\n delete this.participants[id];\n this.eventEmitter.emit(JitsiConferenceEvents.USER_LEFT, id, participant);\n }\n\n if (this.room !== null) { // Skip if we have left the room already.\n this._maybeStartOrStopP2P(true /* triggered by user left event */);\n this._maybeClearSITimeout();\n }\n};\n\n/* eslint-disable max-params */\n\n/**\n * Designates an event indicating that we were kicked from the XMPP MUC.\n * @param {boolean} isSelfPresence - whether it is for local participant\n * or another participant.\n * @param {string} actorId - the id of the participant who was initiator\n * of the kick.\n * @param {string?} kickedParticipantId - when it is not a kick for local participant,\n * this is the id of the participant which was kicked.\n * @param {string} reason - reason of the participant to kick\n * @param {boolean?} isReplaceParticipant - whether this is a server initiated kick in order\n * to replace it with a participant with same jwt.\n */\nJitsiConference.prototype.onMemberKicked = function(\n isSelfPresence,\n actorId,\n kickedParticipantId,\n reason,\n isReplaceParticipant) {\n // This check which be true when we kick someone else. With the introduction of lobby\n // the ChatRoom KICKED event is now also emitted for ourselves (the kicker) so we want to\n // avoid emitting an event where `undefined` kicked someone.\n if (actorId === this.myUserId()) {\n return;\n }\n\n const actorParticipant = this.participants[actorId];\n\n if (isSelfPresence) {\n this.eventEmitter.emit(\n JitsiConferenceEvents.KICKED, actorParticipant, reason, isReplaceParticipant);\n\n this.leave();\n\n return;\n }\n\n const kickedParticipant = this.participants[kickedParticipantId];\n\n kickedParticipant.setIsReplaced(isReplaceParticipant);\n\n this.eventEmitter.emit(\n JitsiConferenceEvents.PARTICIPANT_KICKED, actorParticipant, kickedParticipant, reason);\n};\n\n/**\n * Method called on local MUC role change.\n * @param {string} role the name of new user's role as defined by XMPP MUC.\n */\nJitsiConference.prototype.onLocalRoleChanged = function(role) {\n // Emit role changed for local JID\n this.eventEmitter.emit(\n JitsiConferenceEvents.USER_ROLE_CHANGED, this.myUserId(), role);\n};\n\nJitsiConference.prototype.onUserRoleChanged = function(jid, role) {\n const id = Strophe.getResourceFromJid(jid);\n const participant = this.getParticipantById(id);\n\n if (!participant) {\n return;\n }\n participant.setRole(role);\n this.eventEmitter.emit(JitsiConferenceEvents.USER_ROLE_CHANGED, id, role);\n};\n\nJitsiConference.prototype.onDisplayNameChanged = function(jid, displayName) {\n const id = Strophe.getResourceFromJid(jid);\n const participant = this.getParticipantById(id);\n\n if (!participant) {\n return;\n }\n\n if (participant._displayName === displayName) {\n return;\n }\n\n participant._displayName = displayName;\n this.eventEmitter.emit(\n JitsiConferenceEvents.DISPLAY_NAME_CHANGED,\n id,\n displayName);\n};\n\n/**\n * Notifies this JitsiConference that a JitsiRemoteTrack was added to the conference.\n *\n * @param {JitsiRemoteTrack} track the JitsiRemoteTrack which was added to this JitsiConference.\n */\nJitsiConference.prototype.onRemoteTrackAdded = function(track) {\n if (track.isP2P && !this.isP2PActive()) {\n logger.info('Trying to add remote P2P track, when not in P2P - IGNORED');\n\n return;\n } else if (!track.isP2P && this.isP2PActive()) {\n logger.info('Trying to add remote JVB track, when in P2P - IGNORED');\n\n return;\n }\n\n const id = track.getParticipantId();\n const participant = this.getParticipantById(id);\n\n if (!participant) {\n logger.error(`No participant found for id: ${id}`);\n\n return;\n }\n\n // Add track to JitsiParticipant.\n participant._tracks.push(track);\n\n if (this.transcriber) {\n this.transcriber.addTrack(track);\n }\n\n const emitter = this.eventEmitter;\n\n track.addEventListener(\n JitsiTrackEvents.TRACK_MUTE_CHANGED,\n () => emitter.emit(JitsiConferenceEvents.TRACK_MUTE_CHANGED, track));\n track.isAudioTrack() && track.addEventListener(\n JitsiTrackEvents.TRACK_AUDIO_LEVEL_CHANGED,\n (audioLevel, tpc) => {\n const activeTPC = this.getActivePeerConnection();\n\n if (activeTPC === tpc) {\n emitter.emit(JitsiConferenceEvents.TRACK_AUDIO_LEVEL_CHANGED, id, audioLevel);\n }\n }\n );\n\n emitter.emit(JitsiConferenceEvents.TRACK_ADDED, track);\n};\n\n/**\n * Callback called by the Jingle plugin when 'session-answer' is received.\n * @param {JingleSessionPC} session the Jingle session for which an answer was\n * received.\n * @param {jQuery} answer a jQuery selector pointing to 'jingle' IQ element\n */\n// eslint-disable-next-line no-unused-vars\nJitsiConference.prototype.onCallAccepted = function(session, answer) {\n if (this.p2pJingleSession === session) {\n logger.info('P2P setAnswer');\n\n this.p2pJingleSession.setAnswer(answer);\n this.eventEmitter.emit(JitsiConferenceEvents._MEDIA_SESSION_STARTED, this.p2pJingleSession);\n }\n};\n\n/**\n * Callback called by the Jingle plugin when 'transport-info' is received.\n * @param {JingleSessionPC} session the Jingle session for which the IQ was\n * received\n * @param {jQuery} transportInfo a jQuery selector pointing to 'jingle' IQ\n * element\n */\n// eslint-disable-next-line no-unused-vars\nJitsiConference.prototype.onTransportInfo = function(session, transportInfo) {\n if (this.p2pJingleSession === session) {\n logger.info('P2P addIceCandidates');\n this.p2pJingleSession.addIceCandidates(transportInfo);\n }\n};\n\n/**\n * Notifies this JitsiConference that a JitsiRemoteTrack was removed from\n * the conference.\n *\n * @param {JitsiRemoteTrack} removedTrack\n */\nJitsiConference.prototype.onRemoteTrackRemoved = function(removedTrack) {\n this.getParticipants().forEach(participant => {\n const tracks = participant.getTracks();\n\n for (let i = 0; i < tracks.length; i++) {\n if (tracks[i] === removedTrack) {\n // Since the tracks have been compared and are\n // considered equal the result of splice can be ignored.\n participant._tracks.splice(i, 1);\n\n this.eventEmitter.emit(JitsiConferenceEvents.TRACK_REMOVED, removedTrack);\n\n if (this.transcriber) {\n this.transcriber.removeTrack(removedTrack);\n }\n\n break;\n }\n }\n }, this);\n};\n\n/**\n * Handles an incoming call event for the P2P jingle session.\n */\nJitsiConference.prototype._onIncomingCallP2P = function(jingleSession, jingleOffer) {\n\n let rejectReason;\n\n if ((!this.isP2PEnabled() && !this.isP2PTestModeEnabled()) || browser.isFirefox() || browser.isWebKitBased()) {\n rejectReason = {\n reason: 'decline',\n reasonDescription: 'P2P disabled',\n errorMsg: 'P2P mode disabled in the configuration'\n };\n } else if (this.p2pJingleSession) {\n // Reject incoming P2P call (already in progress)\n rejectReason = {\n reason: 'busy',\n reasonDescription: 'P2P already in progress',\n errorMsg: 'Duplicated P2P \"session-initiate\"'\n };\n } else if (!this._shouldBeInP2PMode()) {\n rejectReason = {\n reason: 'decline',\n reasonDescription: 'P2P requirements not met',\n errorMsg: 'Received P2P \"session-initiate\" when should not be in P2P mode'\n };\n Statistics.sendAnalytics(createJingleEvent(ACTION_P2P_DECLINED));\n }\n\n if (rejectReason) {\n this._rejectIncomingCall(jingleSession, rejectReason);\n } else {\n this._acceptP2PIncomingCall(jingleSession, jingleOffer);\n }\n};\n\n/**\n * Handles an incoming call event.\n */\nJitsiConference.prototype.onIncomingCall = function(jingleSession, jingleOffer, now) {\n // Handle incoming P2P call\n if (jingleSession.isP2P) {\n this._onIncomingCallP2P(jingleSession, jingleOffer);\n } else {\n if (!this.isFocus(jingleSession.remoteJid)) {\n const description = 'Rejecting session-initiate from non-focus.';\n\n this._rejectIncomingCall(\n jingleSession, {\n reason: 'security-error',\n reasonDescription: description,\n errorMsg: description\n });\n\n return;\n }\n this._acceptJvbIncomingCall(jingleSession, jingleOffer, now);\n }\n};\n\n/**\n * Accepts an incoming call event for the JVB jingle session.\n */\nJitsiConference.prototype._acceptJvbIncomingCall = function(jingleSession, jingleOffer, now) {\n\n // Accept incoming call\n this.jvbJingleSession = jingleSession;\n this.room.connectionTimes['session.initiate'] = now;\n this._sendConferenceJoinAnalyticsEvent();\n\n if (this.wasStopped) {\n Statistics.sendAnalyticsAndLog(createJingleEvent(ACTION_JINGLE_RESTART, { p2p: false }));\n }\n\n const serverRegion\n = $(jingleOffer)\n .find('>bridge-session[xmlns=\"http://jitsi.org/protocol/focus\"]')\n .attr('region');\n\n this.eventEmitter.emit(JitsiConferenceEvents.SERVER_REGION_CHANGED, serverRegion);\n\n this._maybeClearSITimeout();\n Statistics.sendAnalytics(createJingleEvent(\n ACTION_JINGLE_SI_RECEIVED,\n {\n p2p: false,\n value: now\n }));\n\n try {\n jingleSession.initialize(\n this.room,\n this.rtc,\n this._signalingLayer,\n {\n ...this.options.config,\n enableInsertableStreams: this.isE2EEEnabled()\n });\n } catch (error) {\n GlobalOnErrorHandler.callErrorHandler(error);\n logger.error(error);\n\n return;\n }\n\n // Open a channel with the videobridge.\n this._setBridgeChannel(jingleOffer, jingleSession.peerconnection);\n\n const localTracks = this._getInitialLocalTracks();\n\n try {\n jingleSession.acceptOffer(\n jingleOffer,\n () => {\n // If for any reason invite for the JVB session arrived after\n // the P2P has been established already the media transfer needs\n // to be turned off here.\n if (this.isP2PActive() && this.jvbJingleSession) {\n this._suspendMediaTransferForJvbConnection();\n }\n\n this.eventEmitter.emit(JitsiConferenceEvents._MEDIA_SESSION_STARTED, jingleSession);\n if (!this.isP2PActive()) {\n this.eventEmitter.emit(JitsiConferenceEvents._MEDIA_SESSION_ACTIVE_CHANGED, jingleSession);\n }\n },\n error => {\n GlobalOnErrorHandler.callErrorHandler(error);\n logger.error('Failed to accept incoming Jingle session', error);\n },\n localTracks\n );\n\n // Enable or disable simulcast for plan-b screensharing based on the capture fps if it is set through the UI.\n this._desktopSharingFrameRate\n && jingleSession.peerconnection.setDesktopSharingFrameRate(this._desktopSharingFrameRate);\n\n // Start callstats as soon as peerconnection is initialized,\n // do not wait for XMPPEvents.PEERCONNECTION_READY, as it may never\n // happen in case if user doesn't have or denied permission to\n // both camera and microphone.\n logger.info('Starting CallStats for JVB connection...');\n this.statistics.startCallStats(\n this.jvbJingleSession.peerconnection,\n 'jitsi' /* Remote user ID for JVB is 'jitsi' */);\n this.statistics.startRemoteStats(this.jvbJingleSession.peerconnection);\n } catch (e) {\n GlobalOnErrorHandler.callErrorHandler(e);\n logger.error(e);\n }\n};\n\n/**\n * Sets the BridgeChannel.\n *\n * @param {jQuery} offerIq a jQuery selector pointing to the jingle element of\n * the offer IQ which may carry the WebSocket URL for the 'websocket'\n * BridgeChannel mode.\n * @param {TraceablePeerConnection} pc the peer connection which will be used\n * to listen for new WebRTC Data Channels (in the 'datachannel' mode).\n */\nJitsiConference.prototype._setBridgeChannel = function(offerIq, pc) {\n let wsUrl = null;\n const webSocket\n = $(offerIq)\n .find('>content>transport>web-socket')\n .first();\n\n if (webSocket.length === 1) {\n wsUrl = webSocket[0].getAttribute('url');\n }\n\n if (wsUrl) {\n // If the offer contains a websocket use it.\n this.rtc.initializeBridgeChannel(null, wsUrl);\n } else {\n // Otherwise, fall back to an attempt to use SCTP.\n this.rtc.initializeBridgeChannel(pc, null);\n }\n};\n\n/**\n * Rejects incoming Jingle call.\n * @param {JingleSessionPC} jingleSession the session instance to be rejected.\n * @param {object} [options]\n * @param {string} options.reason the name of the reason element as defined\n * by Jingle\n * @param {string} options.reasonDescription the reason description which will\n * be included in Jingle 'session-terminate' message.\n * @param {string} options.errorMsg an error message to be logged on global\n * error handler\n * @private\n */\nJitsiConference.prototype._rejectIncomingCall = function(jingleSession, options) {\n if (options && options.errorMsg) {\n GlobalOnErrorHandler.callErrorHandler(new Error(options.errorMsg));\n }\n\n // Terminate the jingle session with a reason\n jingleSession.terminate(\n null /* success callback => we don't care */,\n error => {\n logger.warn(\n 'An error occurred while trying to terminate'\n + ' invalid Jingle session', error);\n }, {\n reason: options && options.reason,\n reasonDescription: options && options.reasonDescription,\n sendSessionTerminate: true\n });\n};\n\n/**\n * Handles the call ended event.\n * XXX is this due to the remote side terminating the Jingle session?\n *\n * @param {JingleSessionPC} jingleSession the jingle session which has been\n * terminated.\n * @param {String} reasonCondition the Jingle reason condition.\n * @param {String|null} reasonText human readable reason text which may provide\n * more details about why the call has been terminated.\n */\nJitsiConference.prototype.onCallEnded = function(jingleSession, reasonCondition, reasonText) {\n logger.info(\n `Call ended: ${reasonCondition} - ${reasonText} P2P ?${\n jingleSession.isP2P}`);\n if (jingleSession === this.jvbJingleSession) {\n this.wasStopped = true;\n\n Statistics.sendAnalytics(\n createJingleEvent(ACTION_JINGLE_TERMINATE, { p2p: false }));\n\n // Stop the stats\n if (this.statistics) {\n this.statistics.stopRemoteStats(\n this.jvbJingleSession.peerconnection);\n logger.info('Stopping JVB CallStats');\n this.statistics.stopCallStats(\n this.jvbJingleSession.peerconnection);\n }\n\n // Current JVB JingleSession is no longer valid, so set it to null\n this.jvbJingleSession = null;\n\n // Let the RTC service do any cleanups\n this.rtc.onCallEnded();\n } else if (jingleSession === this.p2pJingleSession) {\n const stopOptions = {};\n\n // It's the responder who decides to enforce JVB mode, so that both\n // initiator and responder are aware if it was intentional.\n if (reasonCondition === 'decline' && reasonText === 'force JVB121') {\n logger.info('In forced JVB 121 mode...');\n Statistics.analytics.addPermanentProperties({ forceJvb121: true });\n } else if (reasonCondition === 'connectivity-error'\n && reasonText === 'ICE FAILED') {\n // It can happen that the other peer detects ICE failed and\n // terminates the session, before we get the event on our side.\n // But we are able to parse the reason and mark it here.\n Statistics.analytics.addPermanentProperties({ p2pFailed: true });\n } else if (reasonCondition === 'success' && reasonText === 'restart') {\n // When we are restarting media sessions we don't want to switch the tracks\n // to the JVB just yet.\n stopOptions.requestRestart = true;\n }\n this._stopP2PSession(stopOptions);\n } else {\n logger.error(\n 'Received onCallEnded for invalid session',\n jingleSession.sid,\n jingleSession.remoteJid,\n reasonCondition,\n reasonText);\n }\n};\n\n/**\n * Handles the suspend detected event. Leaves the room and fires suspended.\n * @param {JingleSessionPC} jingleSession\n */\nJitsiConference.prototype.onSuspendDetected = function(jingleSession) {\n if (!jingleSession.isP2P) {\n this.leave();\n this.eventEmitter.emit(JitsiConferenceEvents.SUSPEND_DETECTED);\n }\n};\n\nJitsiConference.prototype.updateDTMFSupport = function() {\n let somebodySupportsDTMF = false;\n const participants = this.getParticipants();\n\n // check if at least 1 participant supports DTMF\n for (let i = 0; i < participants.length; i += 1) {\n if (participants[i].supportsDTMF()) {\n somebodySupportsDTMF = true;\n break;\n }\n }\n if (somebodySupportsDTMF !== this.somebodySupportsDTMF) {\n this.somebodySupportsDTMF = somebodySupportsDTMF;\n this.eventEmitter.emit(\n JitsiConferenceEvents.DTMF_SUPPORT_CHANGED,\n somebodySupportsDTMF);\n }\n};\n\n/**\n * Allows to check if there is at least one user in the conference\n * that supports DTMF.\n * @returns {boolean} true if somebody supports DTMF, false otherwise\n */\nJitsiConference.prototype.isDTMFSupported = function() {\n return this.somebodySupportsDTMF;\n};\n\n/**\n * Returns the local user's ID\n * @return {string} local user's ID\n */\nJitsiConference.prototype.myUserId = function() {\n return (\n this.room && this.room.myroomjid\n ? Strophe.getResourceFromJid(this.room.myroomjid)\n : null);\n};\n\nJitsiConference.prototype.sendTones = function(tones, duration, pause) {\n const peerConnection = this.getActivePeerConnection();\n\n if (peerConnection) {\n peerConnection.sendTones(tones, duration, pause);\n } else {\n logger.warn('cannot sendTones: no peer connection');\n }\n};\n\n/**\n * Starts recording the current conference.\n *\n * @param {Object} options - Configuration for the recording. See\n * {@link Chatroom#startRecording} for more info.\n * @returns {Promise} See {@link Chatroom#startRecording} for more info.\n */\nJitsiConference.prototype.startRecording = function(options) {\n if (this.room) {\n return this.recordingManager.startRecording(options);\n }\n\n return Promise.reject(new Error('The conference is not created yet!'));\n};\n\n/**\n * Stop a recording session.\n *\n * @param {string} sessionID - The ID of the recording session that\n * should be stopped.\n * @returns {Promise} See {@link Chatroom#stopRecording} for more info.\n */\nJitsiConference.prototype.stopRecording = function(sessionID) {\n if (this.room) {\n return this.recordingManager.stopRecording(sessionID);\n }\n\n return Promise.reject(new Error('The conference is not created yet!'));\n};\n\n/**\n * Returns true if the SIP calls are supported and false otherwise\n */\nJitsiConference.prototype.isSIPCallingSupported = function() {\n if (this.room) {\n return this.room.isSIPCallingSupported();\n }\n\n return false;\n};\n\n/**\n * Dials a number.\n * @param number the number\n */\nJitsiConference.prototype.dial = function(number) {\n if (this.room) {\n return this.room.dial(number);\n }\n\n return new Promise((resolve, reject) => {\n reject(new Error('The conference is not created yet!'));\n });\n};\n\n/**\n * Hangup an existing call\n */\nJitsiConference.prototype.hangup = function() {\n if (this.room) {\n return this.room.hangup();\n }\n\n return new Promise((resolve, reject) => {\n reject(new Error('The conference is not created yet!'));\n });\n};\n\n/**\n * Starts the transcription service.\n */\nJitsiConference.prototype.startTranscriber = function() {\n return this.dial('jitsi_meet_transcribe');\n};\n\n\n/**\n * Stops the transcription service.\n */\nJitsiConference.prototype.stopTranscriber = JitsiConference.prototype.hangup;\n\n/**\n * Returns the phone number for joining the conference.\n */\nJitsiConference.prototype.getPhoneNumber = function() {\n if (this.room) {\n return this.room.getPhoneNumber();\n }\n\n return null;\n};\n\n/**\n * Returns the pin for joining the conference with phone.\n */\nJitsiConference.prototype.getPhonePin = function() {\n if (this.room) {\n return this.room.getPhonePin();\n }\n\n return null;\n};\n\n/**\n * Returns the meeting unique ID if any.\n *\n * @returns {string|undefined}\n */\nJitsiConference.prototype.getMeetingUniqueId = function() {\n if (this.room) {\n return this.room.getMeetingId();\n }\n};\n\n/**\n * Will return P2P or JVB TraceablePeerConnection depending on\n * which connection is currently active.\n *\n * @return {TraceablePeerConnection|null} null if there isn't any active\n * TraceablePeerConnection currently available.\n * @public (FIXME how to make package local ?)\n */\nJitsiConference.prototype.getActivePeerConnection = function() {\n const session = this.isP2PActive() ? this.p2pJingleSession : this.jvbJingleSession;\n\n return session ? session.peerconnection : null;\n};\n\n/**\n * Returns the connection state for the current room. Its ice connection state\n * for its session.\n * NOTE that \"completed\" ICE state which can appear on the P2P connection will\n * be converted to \"connected\".\n * @return {string|null} ICE state name or null if there is no active\n * peer connection at this time.\n */\nJitsiConference.prototype.getConnectionState = function() {\n const peerConnection = this.getActivePeerConnection();\n\n return peerConnection ? peerConnection.getConnectionState() : null;\n};\n\n/**\n * Make all new participants mute their audio/video on join.\n * @param policy {Object} object with 2 boolean properties for video and audio:\n * @param {boolean} audio if audio should be muted.\n * @param {boolean} video if video should be muted.\n */\nJitsiConference.prototype.setStartMutedPolicy = function(policy) {\n if (!this.isModerator()) {\n logger.warn(`Failed to set start muted policy, ${this.room ? '' : 'not in a room, '}${\n this.isModerator() ? '' : 'participant is not a moderator'}`);\n\n return;\n }\n this.startMutedPolicy = policy;\n this.room.addOrReplaceInPresence('startmuted', {\n attributes: {\n audio: policy.audio,\n video: policy.video,\n xmlns: 'http://jitsi.org/jitmeet/start-muted'\n }\n }) && this.room.sendPresence();\n};\n\n/**\n * Returns current start muted policy\n * @returns {Object} with 2 properties - audio and video.\n */\nJitsiConference.prototype.getStartMutedPolicy = function() {\n return this.startMutedPolicy;\n};\n\n/**\n * Check if audio is muted on join.\n */\nJitsiConference.prototype.isStartAudioMuted = function() {\n return this.startAudioMuted;\n};\n\n/**\n * Check if video is muted on join.\n */\nJitsiConference.prototype.isStartVideoMuted = function() {\n return this.startVideoMuted;\n};\n\n/**\n * Returns measured connectionTimes.\n */\nJitsiConference.prototype.getConnectionTimes = function() {\n return this.room.connectionTimes;\n};\n\n/**\n * Sets a property for the local participant.\n */\nJitsiConference.prototype.setLocalParticipantProperty = function(name, value) {\n this.sendCommand(`jitsi_participant_${name}`, { value });\n};\n\n/**\n * Removes a property for the local participant and sends the updated presence.\n */\nJitsiConference.prototype.removeLocalParticipantProperty = function(name) {\n this.removeCommand(`jitsi_participant_${name}`);\n this.room.sendPresence();\n};\n\n/**\n * Gets a local participant property.\n *\n * @return value of the local participant property if the tagName exists in the\n * list of properties, otherwise returns undefined.\n */\nJitsiConference.prototype.getLocalParticipantProperty = function(name) {\n const property = this.room.presMap.nodes.find(prop =>\n prop.tagName === `jitsi_participant_${name}`\n );\n\n return property ? property.value : undefined;\n};\n\n/**\n * Sends the given feedback through CallStats if enabled.\n *\n * @param overallFeedback an integer between 1 and 5 indicating the\n * user feedback\n * @param detailedFeedback detailed feedback from the user. Not yet used\n * @returns {Promise} Resolves if feedback is submitted successfully.\n */\nJitsiConference.prototype.sendFeedback = function(overallFeedback, detailedFeedback) {\n return this.statistics.sendFeedback(overallFeedback, detailedFeedback);\n};\n\n/**\n * Returns true if the callstats integration is enabled, otherwise returns\n * false.\n *\n * @returns true if the callstats integration is enabled, otherwise returns\n * false.\n */\nJitsiConference.prototype.isCallstatsEnabled = function() {\n return this.statistics.isCallstatsEnabled();\n};\n\n/**\n * Finds the SSRC of a given track\n *\n * @param track\n * @returns {number|undefined} the SSRC of the specificed track, otherwise undefined.\n */\nJitsiConference.prototype.getSsrcByTrack = function(track) {\n return track.isLocal() ? this.getActivePeerConnection()?.getLocalSSRC(track) : track.getSSRC();\n};\n\n/**\n * Handles track attached to container (Calls associateStreamWithVideoTag method\n * from statistics module)\n * @param {JitsiLocalTrack|JitsiRemoteTrack} track the track\n * @param container the container\n */\nJitsiConference.prototype._onTrackAttach = function(track, container) {\n const isLocal = track.isLocal();\n let ssrc = null;\n const isP2P = track.isP2P;\n const remoteUserId = isP2P ? track.getParticipantId() : 'jitsi';\n const peerConnection\n = isP2P\n ? this.p2pJingleSession && this.p2pJingleSession.peerconnection\n : this.jvbJingleSession && this.jvbJingleSession.peerconnection;\n\n if (isLocal) {\n // Local tracks have SSRC stored on per peer connection basis.\n if (peerConnection) {\n ssrc = peerConnection.getLocalSSRC(track);\n }\n } else {\n ssrc = track.getSSRC();\n }\n if (!container.id || !ssrc || !peerConnection) {\n return;\n }\n\n this.statistics.associateStreamWithVideoTag(\n peerConnection,\n ssrc,\n isLocal,\n remoteUserId,\n track.getUsageLabel(),\n container.id);\n};\n\n/**\n * Logs an \"application log\" message.\n * @param message {string} The message to log. Note that while this can be a\n * generic string, the convention used by lib-jitsi-meet and jitsi-meet is to\n * log valid JSON strings, with an \"id\" field used for distinguishing between\n * message types. E.g.: {id: \"recorder_status\", status: \"off\"}\n */\nJitsiConference.prototype.sendApplicationLog = function(message) {\n Statistics.sendLog(message);\n};\n\n/**\n * Checks if the user identified by given mucJid is the conference focus.\n * @param mucJid the full MUC address of the user to be checked.\n * @returns {boolean|null} true if MUC user is the conference focus,\n * false when is not. null if we're not in the MUC anymore and\n * are unable to figure out the status or if given mucJid is invalid.\n */\nJitsiConference.prototype.isFocus = function(mucJid) {\n return this.room ? this.room.isFocus(mucJid) : null;\n};\n\n/**\n * Fires CONFERENCE_FAILED event with INCOMPATIBLE_SERVER_VERSIONS parameter\n */\nJitsiConference.prototype._fireIncompatibleVersionsEvent = function() {\n this.eventEmitter.emit(JitsiConferenceEvents.CONFERENCE_FAILED,\n JitsiConferenceErrors.INCOMPATIBLE_SERVER_VERSIONS);\n};\n\n/**\n * Sends a message via the data channel.\n * @param to {string} the id of the endpoint that should receive the message.\n * If \"\" the message will be sent to all participants.\n * @param payload {object} the payload of the message.\n * @throws NetworkError or InvalidStateError or Error if the operation fails.\n * @deprecated Use 'sendMessage' instead. TODO: this should be private.\n */\nJitsiConference.prototype.sendEndpointMessage = function(to, payload) {\n this.rtc.sendChannelMessage(to, payload);\n};\n\n/**\n * Sends local stats via the bridge channel which then forwards to other endpoints selectively.\n * @param {Object} payload The payload of the message.\n * @throws NetworkError/InvalidStateError/Error if the operation fails or if there is no data channel created.\n */\nJitsiConference.prototype.sendEndpointStatsMessage = function(payload) {\n this.rtc.sendEndpointStatsMessage(payload);\n};\n\n/**\n * Sends a broadcast message via the data channel.\n * @param payload {object} the payload of the message.\n * @throws NetworkError or InvalidStateError or Error if the operation fails.\n * @deprecated Use 'sendMessage' instead. TODO: this should be private.\n */\nJitsiConference.prototype.broadcastEndpointMessage = function(payload) {\n this.sendEndpointMessage('', payload);\n};\n\n/**\n * Sends a message to a given endpoint (if 'to' is a non-empty string), or\n * broadcasts it to all endpoints in the conference.\n * @param {string} to The ID of the endpoint/participant which is to receive\n * the message, or '' to broadcast the message to all endpoints in the\n * conference.\n * @param {string|object} message the message to send. If this is of type\n * 'string' it will be sent as a chat message. If it is of type 'object', it\n * will be encapsulated in a format recognized by jitsi-meet and converted to\n * JSON before being sent.\n * @param {boolean} sendThroughVideobridge Whether to send the message through\n * jitsi-videobridge (via the COLIBRI data channel or web socket), or through\n * the XMPP MUC. Currently only objects can be sent through jitsi-videobridge.\n */\nJitsiConference.prototype.sendMessage = function(message, to = '', sendThroughVideobridge = false) {\n const messageType = typeof message;\n\n // Through videobridge we support only objects. Through XMPP we support\n // objects (encapsulated in a specific JSON format) and strings (i.e.\n // regular chat messages).\n if (messageType !== 'object'\n && (sendThroughVideobridge || messageType !== 'string')) {\n logger.error(`Can not send a message of type ${messageType}`);\n\n return;\n }\n\n if (sendThroughVideobridge) {\n this.sendEndpointMessage(to, message);\n } else {\n let messageToSend = message;\n\n // Name of packet extension of message stanza to send the required\n // message in.\n let elementName = 'body';\n\n if (messageType === 'object') {\n elementName = 'json-message';\n\n // Mark as valid JSON message if not already\n if (!messageToSend.hasOwnProperty(JITSI_MEET_MUC_TYPE)) {\n messageToSend[JITSI_MEET_MUC_TYPE] = '';\n }\n\n try {\n messageToSend = JSON.stringify(messageToSend);\n } catch (e) {\n logger.error('Can not send a message, stringify failed: ', e);\n\n return;\n }\n }\n\n if (to) {\n this.sendPrivateTextMessage(to, messageToSend, elementName);\n } else {\n // Broadcast\n this.sendTextMessage(messageToSend, elementName);\n }\n }\n\n};\n\nJitsiConference.prototype.isConnectionInterrupted = function() {\n return this.isP2PActive()\n ? this.isP2PConnectionInterrupted : this.isJvbConnectionInterrupted;\n};\n\n/**\n * Handles {@link XMPPEvents.CONNECTION_RESTARTED} event. This happens when the bridge goes down\n * and Jicofo moves conferences away to a different bridge.\n * @param {JingleSessionPC} session\n * @private\n */\nJitsiConference.prototype._onConferenceRestarted = function(session) {\n if (!session.isP2P && this.options.config.enableForcedReload) {\n this.eventEmitter.emit(JitsiConferenceEvents.CONFERENCE_FAILED, JitsiConferenceErrors.CONFERENCE_RESTARTED);\n }\n};\n\n/**\n * Handles {@link XMPPEvents.CONNECTION_INTERRUPTED}\n * @param {JingleSessionPC} session\n * @private\n */\nJitsiConference.prototype._onIceConnectionInterrupted = function(session) {\n if (session.isP2P) {\n this.isP2PConnectionInterrupted = true;\n } else {\n this.isJvbConnectionInterrupted = true;\n }\n if (session.isP2P === this.isP2PActive()) {\n this.eventEmitter.emit(JitsiConferenceEvents.CONNECTION_INTERRUPTED);\n }\n};\n\n/**\n * Handles {@link XMPPEvents.CONNECTION_ICE_FAILED}\n * @param {JingleSessionPC} session\n * @private\n */\nJitsiConference.prototype._onIceConnectionFailed = function(session) {\n // We do nothing for the JVB connection, because it's up to the Jicofo to\n // eventually come up with the new offer (at least for the time being).\n if (session.isP2P) {\n // Add p2pFailed property to analytics to distinguish, between \"good\"\n // and \"bad\" connection\n Statistics.analytics.addPermanentProperties({ p2pFailed: true });\n\n if (this.p2pJingleSession) {\n Statistics.sendAnalyticsAndLog(\n createP2PEvent(\n ACTION_P2P_FAILED,\n {\n initiator: this.p2pJingleSession.isInitiator\n }));\n\n }\n this._stopP2PSession({\n reason: 'connectivity-error',\n reasonDescription: 'ICE FAILED'\n });\n } else if (session && this.jvbJingleSession === session) {\n this._delayedIceFailed = new IceFailedHandling(this);\n this._delayedIceFailed.start(session);\n }\n};\n\n/**\n * Handles {@link XMPPEvents.CONNECTION_RESTORED}\n * @param {JingleSessionPC} session\n * @private\n */\nJitsiConference.prototype._onIceConnectionRestored = function(session) {\n if (session.isP2P) {\n this.isP2PConnectionInterrupted = false;\n } else {\n this.isJvbConnectionInterrupted = false;\n this._delayedIceFailed && this._delayedIceFailed.cancel();\n }\n\n if (session.isP2P === this.isP2PActive()) {\n this.eventEmitter.emit(JitsiConferenceEvents.CONNECTION_RESTORED);\n }\n};\n\n/**\n * Accept incoming P2P Jingle call.\n * @param {JingleSessionPC} jingleSession the session instance\n * @param {jQuery} jingleOffer a jQuery selector pointing to 'jingle' IQ element\n * @private\n */\nJitsiConference.prototype._acceptP2PIncomingCall = function(jingleSession, jingleOffer) {\n this.isP2PConnectionInterrupted = false;\n\n // Accept the offer\n this.p2pJingleSession = jingleSession;\n this._sendConferenceJoinAnalyticsEvent();\n\n this.p2pJingleSession.initialize(\n this.room,\n this.rtc,\n this._signalingLayer,\n {\n ...this.options.config,\n enableInsertableStreams: this.isE2EEEnabled()\n });\n\n logger.info('Starting CallStats for P2P connection...');\n\n let remoteID = Strophe.getResourceFromJid(this.p2pJingleSession.remoteJid);\n\n const participant = this.participants[remoteID];\n\n if (participant) {\n remoteID = participant.getStatsID() || remoteID;\n }\n\n this.statistics.startCallStats(\n this.p2pJingleSession.peerconnection,\n remoteID);\n\n const localTracks = this.getLocalTracks();\n\n this.p2pJingleSession.acceptOffer(\n jingleOffer,\n () => {\n logger.debug('Got RESULT for P2P \"session-accept\"');\n\n this.eventEmitter.emit(\n JitsiConferenceEvents._MEDIA_SESSION_STARTED,\n jingleSession);\n },\n error => {\n logger.error(\n 'Failed to accept incoming P2P Jingle session', error);\n },\n localTracks);\n};\n\n/**\n * Adds remote tracks to the conference associated with the JVB session.\n * @private\n */\nJitsiConference.prototype._addRemoteJVBTracks = function() {\n this._addRemoteTracks('JVB', this.jvbJingleSession.peerconnection.getRemoteTracks());\n};\n\n/**\n * Adds remote tracks to the conference associated with the P2P session.\n * @private\n */\nJitsiConference.prototype._addRemoteP2PTracks = function() {\n this._addRemoteTracks('P2P', this.p2pJingleSession.peerconnection.getRemoteTracks());\n};\n\n/**\n * Generates fake \"remote track added\" events for given Jingle session.\n * @param {string} logName the session's nickname which will appear in log\n * messages.\n * @param {Array} remoteTracks the tracks that will be added\n * @private\n */\nJitsiConference.prototype._addRemoteTracks = function(logName, remoteTracks) {\n for (const track of remoteTracks) {\n logger.info(`Adding remote ${logName} track: ${track}`);\n this.onRemoteTrackAdded(track);\n }\n};\n\n/**\n * Called when {@link XMPPEvents.CONNECTION_ESTABLISHED} event is\n * triggered for a {@link JingleSessionPC}. Switches the conference to use\n * the P2P connection if the event comes from the P2P session.\n * @param {JingleSessionPC} jingleSession the session instance.\n * @private\n */\nJitsiConference.prototype._onIceConnectionEstablished = function(jingleSession) {\n if (this.p2pJingleSession !== null) {\n // store the establishment time of the p2p session as a field of the\n // JitsiConference because the p2pJingleSession might get disposed (thus\n // the value is lost).\n this.p2pEstablishmentDuration\n = this.p2pJingleSession.establishmentDuration;\n }\n\n if (this.jvbJingleSession !== null) {\n this.jvbEstablishmentDuration\n = this.jvbJingleSession.establishmentDuration;\n }\n\n let done = false;\n const forceJVB121Ratio = this.options.config.forceJVB121Ratio;\n\n // We don't care about the JVB case, there's nothing to be done\n if (!jingleSession.isP2P) {\n done = true;\n } else if (this.p2pJingleSession !== jingleSession) {\n logger.error('CONNECTION_ESTABLISHED - wrong P2P session instance ?!');\n\n done = true;\n } else if (!jingleSession.isInitiator\n && typeof forceJVB121Ratio === 'number'\n && Math.random() < forceJVB121Ratio) {\n logger.info(`Forcing JVB 121 mode (ratio=${forceJVB121Ratio})...`);\n Statistics.analytics.addPermanentProperties({ forceJvb121: true });\n this._stopP2PSession({\n reason: 'decline',\n reasonDescription: 'force JVB121'\n });\n\n done = true;\n }\n\n if (!isNaN(this.p2pEstablishmentDuration)\n && !isNaN(this.jvbEstablishmentDuration)) {\n const establishmentDurationDiff\n = this.p2pEstablishmentDuration - this.jvbEstablishmentDuration;\n\n Statistics.sendAnalytics(\n ICE_ESTABLISHMENT_DURATION_DIFF,\n { value: establishmentDurationDiff });\n }\n\n if (jingleSession.isP2P === this.isP2PActive()) {\n this.eventEmitter.emit(JitsiConferenceEvents.CONNECTION_ESTABLISHED);\n }\n\n if (done) {\n\n return;\n }\n\n // Update P2P status and emit events\n this._setP2PStatus(true);\n\n // Remove remote tracks\n if (this.jvbJingleSession) {\n this._removeRemoteJVBTracks();\n } else {\n logger.info('Not removing remote JVB tracks - no session yet');\n }\n\n this._addRemoteP2PTracks();\n\n // Stop media transfer over the JVB connection\n if (this.jvbJingleSession) {\n this._suspendMediaTransferForJvbConnection();\n }\n\n logger.info('Starting remote stats with p2p connection');\n this.statistics.startRemoteStats(this.p2pJingleSession.peerconnection);\n\n Statistics.sendAnalyticsAndLog(\n createP2PEvent(\n ACTION_P2P_ESTABLISHED,\n {\n initiator: this.p2pJingleSession.isInitiator\n }));\n\n};\n\n/**\n * Called when the chat room reads a new list of properties from jicofo's\n * presence. The properties may have changed, but they don't have to.\n *\n * @param {Object} properties - The properties keyed by the property name\n * ('key').\n * @private\n */\nJitsiConference.prototype._updateProperties = function(properties = {}) {\n const changed = !isEqual(properties, this.properties);\n\n this.properties = properties;\n if (changed) {\n this.eventEmitter.emit(JitsiConferenceEvents.PROPERTIES_CHANGED, this.properties);\n\n const audioLimitReached = this.properties['audio-limit-reached'] === 'true';\n const videoLimitReached = this.properties['video-limit-reached'] === 'true';\n\n if (this._audioSenderLimitReached !== audioLimitReached) {\n this._audioSenderLimitReached = audioLimitReached;\n this.eventEmitter.emit(JitsiConferenceEvents.AUDIO_UNMUTE_PERMISSIONS_CHANGED, audioLimitReached);\n logger.info(`Audio unmute permissions set by Jicofo to ${audioLimitReached}`);\n }\n\n if (this._videoSenderLimitReached !== videoLimitReached) {\n this._videoSenderLimitReached = videoLimitReached;\n this.eventEmitter.emit(JitsiConferenceEvents.VIDEO_UNMUTE_PERMISSIONS_CHANGED, videoLimitReached);\n logger.info(`Video unmute permissions set by Jicofo to ${videoLimitReached}`);\n }\n\n // Some of the properties need to be added to analytics events.\n const analyticsKeys = [\n\n // The number of jitsi-videobridge instances currently used for the\n // conference.\n 'bridge-count',\n\n // The conference creation time (set by jicofo).\n 'created-ms'\n ];\n\n analyticsKeys.forEach(key => {\n if (properties[key] !== undefined) {\n Statistics.analytics.addPermanentProperties({\n [key.replace('-', '_')]: properties[key]\n });\n }\n });\n }\n};\n\n/**\n * Gets a conference property with a given key.\n *\n * @param {string} key - The key.\n * @returns {*} The value\n */\nJitsiConference.prototype.getProperty = function(key) {\n return this.properties[key];\n};\n\n/**\n * Clears the deferred start P2P task if it has been scheduled.\n * @private\n */\nJitsiConference.prototype._maybeClearDeferredStartP2P = function() {\n if (this.deferredStartP2PTask) {\n logger.info('Cleared deferred start P2P task');\n clearTimeout(this.deferredStartP2PTask);\n this.deferredStartP2PTask = null;\n }\n};\n\n/**\n * Removes from the conference remote tracks associated with the JVB\n * connection.\n * @private\n */\nJitsiConference.prototype._removeRemoteJVBTracks = function() {\n this._removeRemoteTracks(\n 'JVB', this.jvbJingleSession.peerconnection.getRemoteTracks());\n};\n\n/**\n * Removes from the conference remote tracks associated with the P2P\n * connection.\n * @private\n */\nJitsiConference.prototype._removeRemoteP2PTracks = function() {\n this._removeRemoteTracks(\n 'P2P', this.p2pJingleSession.peerconnection.getRemoteTracks());\n};\n\n/**\n * Generates fake \"remote track removed\" events for given Jingle session.\n * @param {string} sessionNickname the session's nickname which will appear in\n * log messages.\n * @param {Array} remoteTracks the tracks that will be removed\n * @private\n */\nJitsiConference.prototype._removeRemoteTracks = function(sessionNickname, remoteTracks) {\n for (const track of remoteTracks) {\n logger.info(`Removing remote ${sessionNickname} track: ${track}`);\n this.onRemoteTrackRemoved(track);\n }\n};\n\n/**\n * Resumes media transfer over the JVB connection.\n * @private\n */\nJitsiConference.prototype._resumeMediaTransferForJvbConnection = function() {\n logger.info('Resuming media transfer over the JVB connection...');\n this.jvbJingleSession.setMediaTransferActive(true, true).then(\n () => {\n logger.info('Resumed media transfer over the JVB connection!');\n },\n error => {\n logger.error(\n 'Failed to resume media transfer over the JVB connection:',\n error);\n });\n};\n\n/**\n * Sets new P2P status and updates some events/states hijacked from\n * the JitsiConference.\n * @param {boolean} newStatus the new P2P status value, true means that\n * P2P is now in use, false means that the JVB connection is now in use\n * @private\n */\nJitsiConference.prototype._setP2PStatus = function(newStatus) {\n if (this.p2p === newStatus) {\n logger.debug(`Called _setP2PStatus with the same status: ${newStatus}`);\n\n return;\n }\n this.p2p = newStatus;\n if (newStatus) {\n logger.info('Peer to peer connection established!');\n\n // When we end up in a valid P2P session need to reset the properties\n // in case they have persisted, after session with another peer.\n Statistics.analytics.addPermanentProperties({\n p2pFailed: false,\n forceJvb121: false\n });\n\n // Sync up video transfer active in case p2pJingleSession not existed\n // when the lastN value was being adjusted.\n const isVideoActive = this.getLastN() !== 0;\n\n this.p2pJingleSession\n .setMediaTransferActive(true, isVideoActive)\n .catch(error => {\n logger.error(\n 'Failed to sync up P2P video transfer status'\n + `(${isVideoActive})`, error);\n });\n } else {\n logger.info('Peer to peer connection closed!');\n }\n\n // Put the JVB connection on hold/resume\n if (this.jvbJingleSession) {\n this.statistics.sendConnectionResumeOrHoldEvent(\n this.jvbJingleSession.peerconnection, !newStatus);\n }\n\n // Clear dtmfManager, so that it can be recreated with new connection\n this.dtmfManager = null;\n\n // Update P2P status\n this.eventEmitter.emit(\n JitsiConferenceEvents.P2P_STATUS,\n this,\n this.p2p);\n this.eventEmitter.emit(JitsiConferenceEvents._MEDIA_SESSION_ACTIVE_CHANGED, this.getActiveMediaSession());\n\n // Refresh connection interrupted/restored\n this.eventEmitter.emit(\n this.isConnectionInterrupted()\n ? JitsiConferenceEvents.CONNECTION_INTERRUPTED\n : JitsiConferenceEvents.CONNECTION_RESTORED);\n};\n\n/**\n * Starts new P2P session.\n * @param {string} remoteJid the JID of the remote participant\n * @private\n */\nJitsiConference.prototype._startP2PSession = function(remoteJid) {\n this._maybeClearDeferredStartP2P();\n if (this.p2pJingleSession) {\n logger.error('P2P session already started!');\n\n return;\n }\n\n this.isP2PConnectionInterrupted = false;\n this.p2pJingleSession\n = this.xmpp.connection.jingle.newP2PJingleSession(\n this.room.myroomjid,\n remoteJid);\n logger.info(\n 'Created new P2P JingleSession', this.room.myroomjid, remoteJid);\n this._sendConferenceJoinAnalyticsEvent();\n\n this.p2pJingleSession.initialize(\n this.room,\n this.rtc,\n this._signalingLayer,\n {\n ...this.options.config,\n enableInsertableStreams: this.isE2EEEnabled()\n });\n\n logger.info('Starting CallStats for P2P connection...');\n\n let remoteID = Strophe.getResourceFromJid(this.p2pJingleSession.remoteJid);\n\n const participant = this.participants[remoteID];\n\n if (participant) {\n remoteID = participant.getStatsID() || remoteID;\n }\n\n this.statistics.startCallStats(\n this.p2pJingleSession.peerconnection,\n remoteID);\n\n const localTracks = this.getLocalTracks();\n\n this.p2pJingleSession.invite(localTracks);\n};\n\n/**\n * Suspends media transfer over the JVB connection.\n * @private\n */\nJitsiConference.prototype._suspendMediaTransferForJvbConnection = function() {\n logger.info('Suspending media transfer over the JVB connection...');\n this.jvbJingleSession.setMediaTransferActive(false, false).then(\n () => {\n logger.info('Suspended media transfer over the JVB connection !');\n },\n error => {\n logger.error(\n 'Failed to suspend media transfer over the JVB connection:',\n error);\n });\n};\n\n/**\n * Method when called will decide whether it's the time to start or stop\n * the P2P session.\n * @param {boolean} userLeftEvent if true it means that the call\n * originates from the user left event.\n * @private\n */\nJitsiConference.prototype._maybeStartOrStopP2P = function(userLeftEvent) {\n if (!this.isP2PEnabled()\n || this.isP2PTestModeEnabled()\n || browser.isFirefox()\n || browser.isWebKitBased()\n || this.isE2EEEnabled()) {\n logger.info('Auto P2P disabled');\n\n return;\n }\n const peers = this.getParticipants();\n const peerCount = peers.length;\n\n // FIXME 1 peer and it must *support* P2P switching\n const shouldBeInP2P = this._shouldBeInP2PMode();\n\n // Clear deferred \"start P2P\" task\n if (!shouldBeInP2P && this.deferredStartP2PTask) {\n this._maybeClearDeferredStartP2P();\n }\n\n // Start peer to peer session\n if (!this.p2pJingleSession && shouldBeInP2P) {\n const peer = peerCount && peers[0];\n\n\n const myId = this.myUserId();\n const peersId = peer.getId();\n\n if (myId > peersId) {\n logger.debug(\n 'I\\'m the bigger peersId - '\n + 'the other peer should start P2P', myId, peersId);\n\n return;\n } else if (myId === peersId) {\n logger.error('The same IDs ? ', myId, peersId);\n\n return;\n }\n\n const jid = peer.getJid();\n\n if (userLeftEvent) {\n if (this.deferredStartP2PTask) {\n logger.error('Deferred start P2P task\\'s been set already!');\n\n return;\n }\n logger.info(\n `Will start P2P with: ${jid} after ${\n this.backToP2PDelay} seconds...`);\n this.deferredStartP2PTask = setTimeout(\n this._startP2PSession.bind(this, jid),\n this.backToP2PDelay * 1000);\n } else {\n logger.info(`Will start P2P with: ${jid}`);\n this._startP2PSession(jid);\n }\n } else if (this.p2pJingleSession && !shouldBeInP2P) {\n logger.info(`Will stop P2P with: ${this.p2pJingleSession.remoteJid}`);\n\n // Log that there will be a switch back to the JVB connection\n if (this.p2pJingleSession.isInitiator && peerCount > 1) {\n Statistics.sendAnalyticsAndLog(\n createP2PEvent(ACTION_P2P_SWITCH_TO_JVB));\n }\n this._stopP2PSession();\n }\n};\n\n/**\n * Tells whether or not this conference should be currently in the P2P mode.\n *\n * @private\n * @returns {boolean}\n */\nJitsiConference.prototype._shouldBeInP2PMode = function() {\n const peers = this.getParticipants();\n const peerCount = peers.length;\n const hasBotPeer = peers.find(p => p.getBotType() === 'poltergeist' || p.hasFeature(FEATURE_JIGASI)) !== undefined;\n const shouldBeInP2P = peerCount === 1 && !hasBotPeer;\n\n logger.debug(`P2P? peerCount: ${peerCount}, hasBotPeer: ${hasBotPeer} => ${shouldBeInP2P}`);\n\n return shouldBeInP2P;\n};\n\n/**\n * Stops the current P2P session.\n * @param {Object} options - Options for stopping P2P.\n * @param {string} options.reason - One of the Jingle \"reason\" element\n * names as defined by https://xmpp.org/extensions/xep-0166.html#def-reason\n * @param {string} options.reasonDescription - Text\n * description that will be included in the session terminate message\n * @param {boolean} requestRestart - Whether this is due to a session restart, in which case\n * media will not be resumed on the JVB.\n * @private\n */\nJitsiConference.prototype._stopP2PSession = function(options = {}) {\n const {\n reason = 'success',\n reasonDescription = 'Turning off P2P session',\n requestRestart = false\n } = options;\n\n if (!this.p2pJingleSession) {\n logger.error('No P2P session to be stopped!');\n\n return;\n }\n\n const wasP2PEstablished = this.isP2PActive();\n\n // Swap remote tracks, but only if the P2P has been fully established\n if (wasP2PEstablished) {\n if (this.jvbJingleSession && !requestRestart) {\n this._resumeMediaTransferForJvbConnection();\n }\n\n // Remove remote P2P tracks\n this._removeRemoteP2PTracks();\n }\n\n // Stop P2P stats\n logger.info('Stopping remote stats for P2P connection');\n this.statistics.stopRemoteStats(this.p2pJingleSession.peerconnection);\n logger.info('Stopping CallStats for P2P connection');\n this.statistics.stopCallStats(this.p2pJingleSession.peerconnection);\n\n this.p2pJingleSession.terminate(\n () => {\n logger.info('P2P session terminate RESULT');\n },\n error => {\n // Because both initiator and responder are simultaneously\n // terminating their JingleSessions in case of the 'to JVB switch'\n // when 3rd participant joins, both will dispose their sessions and\n // reply with 'item-not-found' (see strophe.jingle.js). We don't\n // want to log this as an error since it's expected behaviour.\n //\n // We want them both to terminate, because in case of initiator's\n // crash the responder would stay in P2P mode until ICE fails which\n // could take up to 20 seconds.\n //\n // NOTE: whilst this is an error callback, 'success' as a reason is\n // considered as graceful session terminate\n // where both initiator and responder terminate their sessions\n // simultaneously.\n if (reason !== 'success') {\n logger.error('An error occurred while trying to terminate P2P Jingle session', error);\n }\n }, {\n reason,\n reasonDescription,\n sendSessionTerminate: this.room\n && this.getParticipantById(\n Strophe.getResourceFromJid(this.p2pJingleSession.remoteJid))\n });\n\n this.p2pJingleSession = null;\n\n // Update P2P status and other affected events/states\n this._setP2PStatus(false);\n\n if (wasP2PEstablished) {\n // Add back remote JVB tracks\n if (this.jvbJingleSession && !requestRestart) {\n this._addRemoteJVBTracks();\n } else {\n logger.info('Not adding remote JVB tracks - no session yet');\n }\n }\n};\n\n/**\n * Updates room presence if needed and send the packet in case of a modification.\n * @param {JingleSessionPC} jingleSession the session firing the event, contains the peer connection which\n * tracks we will check.\n * @param {Object|null} ctx a context object we can distinguish multiple calls of the same pass of updating tracks.\n */\nJitsiConference.prototype._updateRoomPresence = function(jingleSession, ctx) {\n if (!jingleSession) {\n return;\n }\n\n // skips sending presence twice for the same pass of updating ssrcs\n if (ctx) {\n if (ctx.skip) {\n return;\n }\n ctx.skip = true;\n }\n\n let presenceChanged = false;\n let muteStatusChanged, videoTypeChanged;\n const localTracks = this.getLocalTracks();\n\n // Set presence for all the available local tracks.\n for (const track of localTracks) {\n muteStatusChanged = this._setTrackMuteStatus(track.getType(), track, track.isMuted());\n if (track.getType() === MediaType.VIDEO) {\n videoTypeChanged = this._setNewVideoType(track);\n }\n presenceChanged = presenceChanged || muteStatusChanged || videoTypeChanged;\n }\n\n // Set the presence in the legacy format if there are no local tracks and multi stream support is not enabled.\n if (!localTracks.length && !FeatureFlags.isMultiStreamSupportEnabled()) {\n const audioMuteStatusChanged = this._setTrackMuteStatus(MediaType.AUDIO, undefined, true);\n const videoMuteStatusChanged = this._setTrackMuteStatus(MediaType.VIDEO, undefined, true);\n\n videoTypeChanged = this._setNewVideoType();\n presenceChanged = audioMuteStatusChanged || videoMuteStatusChanged || videoTypeChanged;\n }\n\n presenceChanged && this.room.sendPresence();\n};\n\n/**\n * Checks whether or not the conference is currently in the peer to peer mode.\n * Being in peer to peer mode means that the direct connection has been\n * established and the P2P connection is being used for media transmission.\n * @return {boolean} true if in P2P mode or false otherwise.\n */\nJitsiConference.prototype.isP2PActive = function() {\n return this.p2p;\n};\n\n/**\n * Returns the current ICE state of the P2P connection.\n * NOTE: method is used by the jitsi-meet-torture tests.\n * @return {string|null} an ICE state or null if there's currently\n * no P2P connection.\n */\nJitsiConference.prototype.getP2PConnectionState = function() {\n if (this.isP2PActive()) {\n return this.p2pJingleSession.peerconnection.getConnectionState();\n }\n\n return null;\n};\n\n/**\n * Configures the peerconnection so that a given framre rate can be achieved for desktop share.\n *\n * @param {number} maxFps The capture framerate to be used for desktop tracks.\n * @returns {boolean} true if the operation is successful, false otherwise.\n */\nJitsiConference.prototype.setDesktopSharingFrameRate = function(maxFps) {\n if (typeof maxFps !== 'number' || isNaN(maxFps)) {\n logger.error(`Invalid value ${maxFps} specified for desktop capture frame rate`);\n\n return false;\n }\n\n this._desktopSharingFrameRate = maxFps;\n\n // Enable or disable simulcast for plan-b screensharing based on the capture fps.\n this.jvbJingleSession && this.jvbJingleSession.peerconnection.setDesktopSharingFrameRate(maxFps);\n\n // Set the capture rate for desktop sharing.\n this.rtc.setDesktopSharingFrameRate(maxFps);\n\n return true;\n};\n\n/**\n * Manually starts new P2P session (should be used only in the tests).\n */\nJitsiConference.prototype.startP2PSession = function() {\n const peers = this.getParticipants();\n\n // Start peer to peer session\n if (peers.length === 1) {\n const peerJid = peers[0].getJid();\n\n this._startP2PSession(peerJid);\n } else {\n throw new Error(\n 'There must be exactly 1 participant to start the P2P session !');\n }\n};\n\n/**\n * Manually stops the current P2P session (should be used only in the tests).\n */\nJitsiConference.prototype.stopP2PSession = function(options) {\n this._stopP2PSession(options);\n};\n\n/**\n * Get a summary of how long current participants have been the dominant speaker\n * @returns {object}\n */\nJitsiConference.prototype.getSpeakerStats = function() {\n return this.speakerStatsCollector.getStats();\n};\n\n/**\n * Sends a facial expression with its duration to the xmpp server.\n * @param {Object} payload\n */\nJitsiConference.prototype.sendFacialExpression = function(payload) {\n this.xmpp.sendFacialExpressionEvent(this.room.roomjid, payload);\n};\n\n/**\n * Sets the constraints for the video that is requested from the bridge.\n *\n * @param {Object} videoConstraints The constraints which are specified in the\n * following format. The message updates the fields that are present and leaves the\n * rest unchanged on the bridge. Therefore, any field that is not applicable anymore\n * should be cleared by passing an empty object or list (whatever is applicable).\n * {\n * 'lastN': 20,\n * 'selectedEndpoints': ['A', 'B', 'C'],\n * 'onStageEndpoints': ['A'],\n * 'defaultConstraints': { 'maxHeight': 180 },\n * 'constraints': {\n * 'A': { 'maxHeight': 720 }\n * }\n * }\n */\nJitsiConference.prototype.setReceiverConstraints = function(videoConstraints) {\n this.receiveVideoController.setReceiverConstraints(videoConstraints);\n};\n\n/**\n * Sets the maximum video size the local participant should receive from remote\n * participants.\n *\n * @param {number} maxFrameHeight - the maximum frame height, in pixels,\n * this receiver is willing to receive.\n * @returns {void}\n */\nJitsiConference.prototype.setReceiverVideoConstraint = function(maxFrameHeight) {\n this.receiveVideoController.setPreferredReceiveMaxFrameHeight(maxFrameHeight);\n};\n\n/**\n * Sets the maximum video size the local participant should send to remote\n * participants.\n * @param {number} maxFrameHeight - The user preferred max frame height.\n * @returns {Promise} promise that will be resolved when the operation is\n * successful and rejected otherwise.\n */\nJitsiConference.prototype.setSenderVideoConstraint = function(maxFrameHeight) {\n return this.sendVideoController.setPreferredSendMaxFrameHeight(maxFrameHeight);\n};\n\n/**\n * Creates a video SIP GW session and returns it if service is enabled. Before\n * creating a session one need to check whether video SIP GW service is\n * available in the system {@link JitsiConference.isVideoSIPGWAvailable}. Even\n * if there are available nodes to serve this request, after creating the\n * session those nodes can be taken and the request about using the\n * created session can fail.\n *\n * @param {string} sipAddress - The sip address to be used.\n * @param {string} displayName - The display name to be used for this session.\n * @returns {JitsiVideoSIPGWSession|Error} Returns null if conference is not\n * initialised and there is no room.\n */\nJitsiConference.prototype.createVideoSIPGWSession = function(sipAddress, displayName) {\n if (!this.room) {\n return new Error(VideoSIPGWConstants.ERROR_NO_CONNECTION);\n }\n\n return this.videoSIPGWHandler\n .createVideoSIPGWSession(sipAddress, displayName);\n};\n\n/**\n * Sends a conference.join analytics event.\n *\n * @returns {void}\n */\nJitsiConference.prototype._sendConferenceJoinAnalyticsEvent = function() {\n const meetingId = this.getMeetingUniqueId();\n\n if (this._conferenceJoinAnalyticsEventSent || !meetingId || this.getActivePeerConnection() === null) {\n return;\n }\n\n Statistics.sendAnalytics(createConferenceEvent('joined', {\n meetingId,\n participantId: `${meetingId}.${this._statsCurrentId}`\n }));\n this._conferenceJoinAnalyticsEventSent = Date.now();\n};\n\n/**\n * Sends conference.left analytics event.\n * @private\n */\nJitsiConference.prototype._sendConferenceLeftAnalyticsEvent = function() {\n const meetingId = this.getMeetingUniqueId();\n\n if (!meetingId || !this._conferenceJoinAnalyticsEventSent) {\n\n return;\n }\n\n Statistics.sendAnalytics(createConferenceEvent('left', {\n meetingId,\n participantId: `${meetingId}.${this._statsCurrentId}`,\n stats: {\n duration: Math.floor((Date.now() - this._conferenceJoinAnalyticsEventSent) / 1000),\n perf: this.getPerformanceStats()\n }\n }));\n};\n\n/**\n * Restarts all active media sessions.\n *\n * @returns {void}\n */\nJitsiConference.prototype._restartMediaSessions = function() {\n if (this.p2pJingleSession) {\n this._stopP2PSession({\n reasonDescription: 'restart',\n requestRestart: true\n });\n }\n\n if (this.jvbJingleSession) {\n this.jvbJingleSession.terminate(\n null /* success callback => we don't care */,\n error => {\n logger.warn('An error occurred while trying to terminate the JVB session', error);\n }, {\n reason: 'success',\n reasonDescription: 'restart required',\n requestRestart: true,\n sendSessionTerminate: true\n });\n }\n\n this._maybeStartOrStopP2P(false);\n};\n\n/**\n * Returns whether End-To-End encryption is enabled.\n *\n * @returns {boolean}\n */\nJitsiConference.prototype.isE2EEEnabled = function() {\n return Boolean(this._e2eEncryption && this._e2eEncryption.isEnabled());\n};\n\n/**\n * Returns whether End-To-End encryption is supported. Note that not all participants\n * in the conference may support it.\n *\n * @returns {boolean}\n */\nJitsiConference.prototype.isE2EESupported = function() {\n return E2EEncryption.isSupported(this.options.config);\n};\n\n/**\n * Enables / disables End-to-End encryption.\n *\n * @param {boolean} enabled whether to enable E2EE or not.\n * @returns {void}\n */\nJitsiConference.prototype.toggleE2EE = function(enabled) {\n if (!this.isE2EESupported()) {\n logger.warn('Cannot enable / disable E2EE: platform is not supported.');\n\n return;\n }\n\n this._e2eEncryption.setEnabled(enabled);\n};\n\n/**\n * Sets the key and index for End-to-End encryption.\n *\n * @param {CryptoKey} [keyInfo.encryptionKey] - encryption key.\n * @param {Number} [keyInfo.index] - the index of the encryption key.\n * @returns {void}\n */\nJitsiConference.prototype.setMediaEncryptionKey = function(keyInfo) {\n this._e2eEncryption.setEncryptionKey(keyInfo);\n};\n\n/**\n * Returns true if lobby support is enabled in the backend.\n *\n * @returns {boolean} whether lobby is supported in the backend.\n */\nJitsiConference.prototype.isLobbySupported = function() {\n return Boolean(this.room && this.room.getLobby().isSupported());\n};\n\n/**\n * Returns true if the room has members only enabled.\n *\n * @returns {boolean} whether conference room is members only.\n */\nJitsiConference.prototype.isMembersOnly = function() {\n return Boolean(this.room && this.room.membersOnlyEnabled);\n};\n\n/**\n * Enables lobby by moderators\n *\n * @returns {Promise} resolves when lobby room is joined or rejects with the error.\n */\nJitsiConference.prototype.enableLobby = function() {\n if (this.room && this.isModerator()) {\n return this.room.getLobby().enable();\n }\n\n return Promise.reject(\n new Error('The conference not started or user is not moderator'));\n};\n\n/**\n * Disabled lobby by moderators\n *\n * @returns {void}\n */\nJitsiConference.prototype.disableLobby = function() {\n if (this.room && this.isModerator()) {\n this.room.getLobby().disable();\n } else {\n logger.warn(`Failed to disable lobby, ${this.room ? '' : 'not in a room, '}${\n this.isModerator() ? '' : 'participant is not a moderator'}`);\n }\n};\n\n/**\n * Joins the lobby room with display name and optional email or with a shared password to skip waiting.\n *\n * @param {string} displayName Display name should be set to show it to moderators.\n * @param {string} email Optional email is used to present avatar to the moderator.\n * @returns {Promise}\n */\nJitsiConference.prototype.joinLobby = function(displayName, email) {\n if (this.room) {\n return this.room.getLobby().join(displayName, email);\n }\n\n return Promise.reject(new Error('The conference not started'));\n};\n\n/**\n * Gets the local id for a participant in a lobby room.\n * Returns undefined when current participant is not in the lobby room.\n * This is used for lobby room private chat messages.\n *\n * @returns {string}\n */\nJitsiConference.prototype.myLobbyUserId = function() {\n if (this.room) {\n return this.room.getLobby().getLocalId();\n }\n};\n\n/**\n * Sends a message to a lobby room.\n * When id is specified it sends a private message.\n * Otherwise it sends the message to all moderators.\n * @param {message} Object The message to send\n * @param {string} id The participant id.\n *\n * @returns {void}\n */\nJitsiConference.prototype.sendLobbyMessage = function(message, id) {\n if (this.room) {\n if (id) {\n return this.room.getLobby().sendPrivateMessage(id, message);\n }\n\n return this.room.getLobby().sendMessage(message);\n }\n};\n\n/**\n * Adds a message listener to the lobby room\n * @param {Function} listener The listener function,\n * called when a new message is received in the lobby room.\n *\n * @returns {Function} Handler returned to be able to remove it later.\n */\nJitsiConference.prototype.addLobbyMessageListener = function(listener) {\n if (this.room) {\n return this.room.getLobby().addMessageListener(listener);\n }\n};\n\n/**\n * Removes a message handler from the lobby room\n * @param {Function} handler The handler function to remove.\n *\n * @returns {void}\n */\nJitsiConference.prototype.removeLobbyMessageHandler = function(handler) {\n if (this.room) {\n return this.room.getLobby().removeMessageHandler(handler);\n }\n};\n\n/**\n * Denies an occupant in the lobby room access to the conference.\n * @param {string} id The participant id.\n */\nJitsiConference.prototype.lobbyDenyAccess = function(id) {\n if (this.room) {\n this.room.getLobby().denyAccess(id);\n }\n};\n\n/**\n * Approves the request to join the conference to a participant waiting in the lobby.\n *\n * @param {string} id The participant id.\n */\nJitsiConference.prototype.lobbyApproveAccess = function(id) {\n if (this.room) {\n this.room.getLobby().approveAccess(id);\n }\n};\n\n/**\n * Returns true if AV Moderation support is enabled in the backend.\n *\n * @returns {boolean} whether AV Moderation is supported in the backend.\n */\nJitsiConference.prototype.isAVModerationSupported = function() {\n return Boolean(this.room && this.room.getAVModeration().isSupported());\n};\n\n/**\n * Enables AV Moderation.\n * @param {MediaType} mediaType \"audio\" or \"video\"\n */\nJitsiConference.prototype.enableAVModeration = function(mediaType) {\n if (this.room && this.isModerator()\n && (mediaType === MediaType.AUDIO || mediaType === MediaType.VIDEO)) {\n this.room.getAVModeration().enable(true, mediaType);\n } else {\n logger.warn(`Failed to enable AV moderation, ${this.room ? '' : 'not in a room, '}${\n this.isModerator() ? '' : 'participant is not a moderator, '}${\n this.room && this.isModerator() ? 'wrong media type passed' : ''}`);\n }\n};\n\n/**\n * Disables AV Moderation.\n * @param {MediaType} mediaType \"audio\" or \"video\"\n */\nJitsiConference.prototype.disableAVModeration = function(mediaType) {\n if (this.room && this.isModerator()\n && (mediaType === MediaType.AUDIO || mediaType === MediaType.VIDEO)) {\n this.room.getAVModeration().enable(false, mediaType);\n } else {\n logger.warn(`Failed to disable AV moderation, ${this.room ? '' : 'not in a room, '}${\n this.isModerator() ? '' : 'participant is not a moderator, '}${\n this.room && this.isModerator() ? 'wrong media type passed' : ''}`);\n }\n};\n\n/**\n * Approve participant access to certain media, allows unmuting audio or video.\n *\n * @param {MediaType} mediaType \"audio\" or \"video\"\n * @param id the id of the participant.\n */\nJitsiConference.prototype.avModerationApprove = function(mediaType, id) {\n if (this.room && this.isModerator()\n && (mediaType === MediaType.AUDIO || mediaType === MediaType.VIDEO)) {\n\n const participant = this.getParticipantById(id);\n\n if (!participant) {\n return;\n }\n\n this.room.getAVModeration().approve(mediaType, participant.getJid());\n } else {\n logger.warn(`AV moderation approve skipped , ${this.room ? '' : 'not in a room, '}${\n this.isModerator() ? '' : 'participant is not a moderator, '}${\n this.room && this.isModerator() ? 'wrong media type passed' : ''}`);\n }\n};\n\n/**\n * Reject participant access to certain media, blocks unmuting audio or video.\n *\n * @param {MediaType} mediaType \"audio\" or \"video\"\n * @param id the id of the participant.\n */\nJitsiConference.prototype.avModerationReject = function(mediaType, id) {\n if (this.room && this.isModerator()\n && (mediaType === MediaType.AUDIO || mediaType === MediaType.VIDEO)) {\n\n const participant = this.getParticipantById(id);\n\n if (!participant) {\n return;\n }\n\n this.room.getAVModeration().reject(mediaType, participant.getJid());\n } else {\n logger.warn(`AV moderation reject skipped , ${this.room ? '' : 'not in a room, '}${\n this.isModerator() ? '' : 'participant is not a moderator, '}${\n this.room && this.isModerator() ? 'wrong media type passed' : ''}`);\n }\n};\n\n/**\n * Returns the breakout rooms manager object.\n *\n * @returns {Object} the breakout rooms manager.\n */\nJitsiConference.prototype.getBreakoutRooms = function() {\n return this.room?.getBreakoutRooms();\n};\n","import JitsiConference from './JitsiConference';\nimport * as JitsiConnectionEvents from './JitsiConnectionEvents';\nimport Statistics from './modules/statistics/statistics';\nimport XMPP from './modules/xmpp/xmpp';\nimport {\n CONNECTION_DISCONNECTED as ANALYTICS_CONNECTION_DISCONNECTED,\n createConnectionFailedEvent\n} from './service/statistics/AnalyticsEvents';\n\n/**\n * Creates a new connection object for the Jitsi Meet server side video\n * conferencing service. Provides access to the JitsiConference interface.\n * @param appID identification for the provider of Jitsi Meet video conferencing\n * services.\n * @param token the JWT token used to authenticate with the server(optional)\n * @param options Object with properties / settings related to connection with\n * the server.\n * @constructor\n */\nexport default function JitsiConnection(appID, token, options) {\n this.appID = appID;\n this.token = token;\n this.options = options;\n this.xmpp = new XMPP(options, token);\n\n /* eslint-disable max-params */\n this.addEventListener(JitsiConnectionEvents.CONNECTION_FAILED,\n (errType, msg, credentials, details) => {\n Statistics.sendAnalyticsAndLog(\n createConnectionFailedEvent(errType, msg, details));\n });\n /* eslint-enable max-params */\n\n this.addEventListener(JitsiConnectionEvents.CONNECTION_DISCONNECTED,\n msg => {\n // we can see disconnects from normal tab closing of the browser\n // and then there are no msgs, but we want to log only disconnects\n // when there is real error\n // XXX Do we need the difference in handling between the log and\n // analytics event here?\n if (msg) {\n Statistics.sendAnalytics(\n ANALYTICS_CONNECTION_DISCONNECTED,\n { message: msg });\n }\n Statistics.sendLog(\n JSON.stringify(\n {\n id: ANALYTICS_CONNECTION_DISCONNECTED,\n msg\n }));\n });\n}\n\n/**\n * Connect the client with the server.\n * @param options {object} connecting options\n * (for example authentications parameters).\n */\nJitsiConnection.prototype.connect = function(options = {}) {\n this.xmpp.connect(options.id, options.password);\n};\n\n/**\n * Attach to existing connection. Can be used for optimizations. For example:\n * if the connection is created on the server we can attach to it and start\n * using it.\n *\n * @param options {object} connecting options - rid, sid and jid.\n */\nJitsiConnection.prototype.attach = function(options) {\n this.xmpp.attach(options);\n};\n\n/**\n * Disconnect the client from the server.\n * @returns {Promise} - Resolves when the disconnect process is finished or rejects with an error.\n */\nJitsiConnection.prototype.disconnect = function(...args) {\n // XXX Forward any arguments passed to JitsiConnection.disconnect to\n // XMPP.disconnect. For example, the caller of JitsiConnection.disconnect\n // may optionally pass the event which triggered the disconnect in order to\n // provide the implementation with finer-grained context.\n return this.xmpp.disconnect(...args);\n};\n\n/**\n * Returns the jid of the participant associated with the XMPP connection.\n *\n * @returns {string} The jid of the participant.\n */\nJitsiConnection.prototype.getJid = function() {\n return this.xmpp.getJid();\n};\n\n/**\n * This method allows renewal of the tokens if they are expiring.\n * @param token the new token.\n */\nJitsiConnection.prototype.setToken = function(token) {\n this.token = token;\n};\n\n/**\n * Creates and joins new conference.\n * @param name the name of the conference; if null - a generated name will be\n * provided from the api\n * @param options Object with properties / settings related to the conference\n * that will be created.\n * @returns {JitsiConference} returns the new conference object.\n */\nJitsiConnection.prototype.initJitsiConference = function(name, options) {\n return new JitsiConference({\n name,\n config: options,\n connection: this\n });\n};\n\n/**\n * Subscribes the passed listener to the event.\n * @param event {JitsiConnectionEvents} the connection event.\n * @param listener {Function} the function that will receive the event\n */\nJitsiConnection.prototype.addEventListener = function(event, listener) {\n this.xmpp.addListener(event, listener);\n};\n\n/**\n * Unsubscribes the passed handler.\n * @param event {JitsiConnectionEvents} the connection event.\n * @param listener {Function} the function that will receive the event\n */\nJitsiConnection.prototype.removeEventListener = function(event, listener) {\n this.xmpp.removeListener(event, listener);\n};\n\n/**\n * Returns measured connectionTimes.\n */\nJitsiConnection.prototype.getConnectionTimes = function() {\n return this.xmpp.connectionTimes;\n};\n\n/**\n * Adds new feature to the list of supported features for the local\n * participant.\n * @param {String} feature the name of the feature.\n * @param {boolean} submit if true - the new list of features will be\n * immediately submitted to the others.\n */\nJitsiConnection.prototype.addFeature = function(feature, submit = false) {\n this.xmpp.caps.addFeature(feature, submit, true);\n};\n\n/**\n * Removes a feature from the list of supported features for the local\n * participant\n * @param {String} feature the name of the feature.\n * @param {boolean} submit if true - the new list of features will be\n * immediately submitted to the others.\n */\nJitsiConnection.prototype.removeFeature = function(feature, submit = false) {\n this.xmpp.caps.removeFeature(feature, submit, true);\n};\n\n/**\n * Get object with internal logs.\n */\nJitsiConnection.prototype.getLogs = function() {\n const data = this.xmpp.getJingleLog();\n\n const metadata = {};\n\n metadata.time = new Date();\n metadata.url = window.location.href;\n metadata.ua = navigator.userAgent;\n\n const log = this.xmpp.getXmppLog();\n\n if (log) {\n metadata.xmpp = log;\n }\n\n data.metadata = metadata;\n\n return data;\n};\n","/**\n * The events for the media devices.\n */\n\nexport enum JitsiMediaDevicesEvents {\n /**\n * Indicates that the list of available media devices has been changed. The\n * event provides the following parameters to its listeners:\n *\n * @param {MediaDeviceInfo[]} devices - array of MediaDeviceInfo or\n * MediaDeviceInfo-like objects that are currently connected.\n * @see https://developer.mozilla.org/en-US/docs/Web/API/MediaDeviceInfo\n */\n DEVICE_LIST_CHANGED = 'mediaDevices.devicechange',\n\n /**\n * Event emitted when the user granted/blocked a permission for the camera / mic.\n * Used to keep track of the granted permissions on browsers which don't\n * support the Permissions API.\n */\n PERMISSIONS_CHANGED = 'rtc.permissions_changed',\n\n /**\n * Indicates that the environment is currently showing permission prompt to\n * access camera and/or microphone. The event provides the following\n * parameters to its listeners:\n *\n * @param {'chrome'|'opera'|'firefox'|'safari'|'nwjs'\n * |'react-native'|'android'} environmentType - type of browser or\n * other execution environment.\n */\n PERMISSION_PROMPT_IS_SHOWN = 'mediaDevices.permissionPromptIsShown',\n\n SLOW_GET_USER_MEDIA = 'mediaDevices.slowGetUserMedia'\n};\n\n// exported for backward compatibility\nexport const DEVICE_LIST_CHANGED = JitsiMediaDevicesEvents.DEVICE_LIST_CHANGED;\nexport const PERMISSIONS_CHANGED = JitsiMediaDevicesEvents.PERMISSIONS_CHANGED;\nexport const PERMISSION_PROMPT_IS_SHOWN = JitsiMediaDevicesEvents.PERMISSION_PROMPT_IS_SHOWN;\nexport const SLOW_GET_USER_MEDIA = JitsiMediaDevicesEvents.SLOW_GET_USER_MEDIA;\n","import EventEmitter from 'events';\n\nimport * as JitsiMediaDevicesEvents from './JitsiMediaDevicesEvents';\nimport RTC from './modules/RTC/RTC';\nimport browser from './modules/browser';\nimport Statistics from './modules/statistics/statistics';\nimport * as MediaType from './service/RTC/MediaType';\nimport RTCEvents from './service/RTC/RTCEvents';\n\nconst AUDIO_PERMISSION_NAME = 'microphone';\nconst PERMISSION_GRANTED_STATUS = 'granted';\nconst VIDEO_PERMISSION_NAME = 'camera';\n\n/**\n * Media devices utilities for Jitsi.\n */\nclass JitsiMediaDevices {\n /**\n * Initializes a {@code JitsiMediaDevices} object. There will be a single\n * instance of this class.\n */\n constructor() {\n this._eventEmitter = new EventEmitter();\n this._permissions = {};\n\n RTC.addListener(\n RTCEvents.DEVICE_LIST_CHANGED,\n devices =>\n this._eventEmitter.emit(\n JitsiMediaDevicesEvents.DEVICE_LIST_CHANGED,\n devices));\n RTC.addListener(\n RTCEvents.DEVICE_LIST_AVAILABLE,\n devices =>\n this._logOutputDevice(\n this.getAudioOutputDevice(),\n devices));\n\n // We would still want to update the permissions cache in case the permissions API is not supported.\n RTC.addListener(\n RTCEvents.PERMISSIONS_CHANGED,\n permissions => this._handlePermissionsChange(permissions));\n\n // Test if the W3C Permissions API is implemented and the 'camera' and 'microphone' permissions are\n // implemented. If supported add onchange listeners.\n this._permissionsApiSupported = new Promise(resolve => {\n if (!navigator.permissions) {\n resolve(false);\n\n return;\n }\n\n const self = this;\n\n const promises = [];\n\n promises.push(navigator.permissions.query({ name: VIDEO_PERMISSION_NAME })\n .then(status => {\n this._handlePermissionsChange({\n [MediaType.VIDEO]: this._parsePermissionState(status)\n });\n status.onchange = function() {\n try {\n self._handlePermissionsChange({\n [MediaType.VIDEO]: self._parsePermissionState(this)\n });\n } catch (error) {\n // Nothing to do.\n }\n };\n\n return true;\n })\n .catch(() => false));\n\n promises.push(navigator.permissions.query({ name: AUDIO_PERMISSION_NAME })\n .then(status => {\n this._handlePermissionsChange({\n [MediaType.AUDIO]: this._parsePermissionState(status)\n });\n status.onchange = function() {\n try {\n self._handlePermissionsChange({\n [MediaType.AUDIO]: self._parsePermissionState(this)\n });\n } catch (error) {\n // Nothing to do.\n }\n };\n\n return true;\n })\n .catch(() => false));\n\n Promise.all(promises).then(results => resolve(results.every(supported => supported)));\n\n });\n }\n\n\n /**\n * Parses a PermissionState object and returns true for granted and false otherwise.\n *\n * @param {PermissionState} permissionStatus - The PermissionState object retrieved from the Permissions API.\n * @returns {boolean} - True for granted and false for denied.\n * @throws {TypeError}\n */\n _parsePermissionState(permissionStatus = {}) {\n // The status attribute is deprecated, and state\n // should be used instead, but check both for now\n // for backwards compatibility.\n const status = permissionStatus.state || permissionStatus.status;\n\n if (typeof status !== 'string') {\n throw new TypeError();\n }\n\n return status === PERMISSION_GRANTED_STATUS;\n }\n\n /**\n * Updates the local granted/denied permissions cache. A permissions might be\n * granted, denied, or undefined. This is represented by having its media\n * type key set to {@code true} or {@code false} respectively.\n *\n * @param {Object} permissions - Object with the permissions.\n */\n _handlePermissionsChange(permissions) {\n const hasPermissionsChanged\n = [ MediaType.AUDIO, MediaType.VIDEO ]\n .some(type => type in permissions && permissions[type] !== this._permissions[type]);\n\n if (hasPermissionsChanged) {\n this._permissions = {\n ...this._permissions,\n ...permissions\n };\n this._eventEmitter.emit(JitsiMediaDevicesEvents.PERMISSIONS_CHANGED, this._permissions);\n\n if (this._permissions[MediaType.AUDIO] || this._permissions[MediaType.VIDEO]) {\n // Triggering device list update when the permissiions are granted in order to update\n // the labels the devices.\n // eslint-disable-next-line no-empty-function\n this.enumerateDevices(() => {});\n }\n }\n }\n\n /**\n * Gathers data and sends it to statistics.\n * @param deviceID the device id to log\n * @param devices list of devices\n */\n _logOutputDevice(deviceID, devices) {\n const device\n = devices.find(\n d => d.kind === 'audiooutput' && d.deviceId === deviceID);\n\n if (device) {\n Statistics.sendActiveDeviceListEvent(\n RTC.getEventDataForActiveDevice(device));\n }\n }\n\n /**\n * Executes callback with list of media devices connected.\n * @param {function} callback\n */\n enumerateDevices(callback) {\n RTC.enumerateDevices(callback);\n }\n\n /**\n * Checks if its possible to enumerate available cameras/micropones.\n * @returns {Promise} a Promise which will be resolved only once\n * the WebRTC stack is ready, either with true if the device listing is\n * available available or with false otherwise.\n */\n isDeviceListAvailable() {\n return RTC.isDeviceListAvailable();\n }\n\n /**\n * Returns true if changing the input (camera / microphone) or output\n * (audio) device is supported and false if not.\n * @param {string} [deviceType] - type of device to change. Default is\n * undefined or 'input', 'output' - for audio output device change.\n * @returns {boolean} true if available, false otherwise.\n */\n isDeviceChangeAvailable(deviceType) {\n return RTC.isDeviceChangeAvailable(deviceType);\n }\n\n /**\n * Checks if the permission for the given device was granted.\n *\n * @param {'audio'|'video'} [type] - type of devices to check,\n * undefined stands for both 'audio' and 'video' together\n * @returns {Promise}\n */\n isDevicePermissionGranted(type) {\n return new Promise(resolve => {\n // Shortcut: first check if we already know the permission was\n // granted.\n if (type in this._permissions) {\n resolve(this._permissions[type]);\n\n return;\n }\n\n // Check using the Permissions API.\n this._permissionsApiSupported.then(supported => {\n if (!supported) {\n resolve(false);\n\n return;\n }\n\n const promises = [];\n\n switch (type) {\n case MediaType.VIDEO:\n promises.push(\n navigator.permissions.query({\n name: VIDEO_PERMISSION_NAME\n }));\n break;\n case MediaType.AUDIO:\n promises.push(\n navigator.permissions.query({\n name: AUDIO_PERMISSION_NAME\n }));\n break;\n default:\n promises.push(\n navigator.permissions.query({\n name: VIDEO_PERMISSION_NAME\n }));\n promises.push(\n navigator.permissions.query({\n name: AUDIO_PERMISSION_NAME\n }));\n }\n\n Promise.all(promises).then(\n results => resolve(results.every(permissionStatus => {\n try {\n return this._parsePermissionState(permissionStatus);\n } catch {\n return false;\n }\n })),\n () => resolve(false)\n );\n });\n });\n }\n\n /**\n * Returns true if it is possible to be simultaneously capturing audio from more than one device.\n *\n * @returns {boolean}\n */\n isMultipleAudioInputSupported() {\n return !(browser.isFirefox() || browser.isIosBrowser());\n }\n\n /**\n * Returns currently used audio output device id, 'default' stands\n * for default device\n * @returns {string}\n */\n getAudioOutputDevice() {\n return RTC.getAudioOutputDevice();\n }\n\n /**\n * Sets current audio output device.\n * @param {string} deviceId - id of 'audiooutput' device from\n * navigator.mediaDevices.enumerateDevices(), 'default' is for\n * default device\n * @returns {Promise} - resolves when audio output is changed, is rejected\n * otherwise\n */\n setAudioOutputDevice(deviceId) {\n const availableDevices = RTC.getCurrentlyAvailableMediaDevices();\n\n if (availableDevices.length > 0) {\n // if we have devices info report device to stats\n // normally this will not happen on startup as this method is called\n // too early. This will happen only on user selection of new device\n this._logOutputDevice(\n deviceId, RTC.getCurrentlyAvailableMediaDevices());\n }\n\n return RTC.setAudioOutputDevice(deviceId);\n }\n\n /**\n * Adds an event handler.\n * @param {string} event - event name\n * @param {function} handler - event handler\n */\n addEventListener(event, handler) {\n this._eventEmitter.addListener(event, handler);\n }\n\n /**\n * Removes event handler.\n * @param {string} event - event name\n * @param {function} handler - event handler\n */\n removeEventListener(event, handler) {\n this._eventEmitter.removeListener(event, handler);\n }\n\n /**\n * Emits an event.\n * @param {string} event - event name\n */\n emitEvent(event, ...args) {\n this._eventEmitter.emit(event, ...args);\n }\n}\n\nexport default new JitsiMediaDevices();\n","import { getLogger } from '@jitsi/logger';\n\nimport * as JitsiTrackEvents from '../../JitsiTrackEvents';\nimport RTC from '../RTC/RTC';\nimport Statistics from '../statistics/statistics';\n\n\nconst logger = getLogger(__filename);\n\n// If after 3000 ms the detector did not find any active devices consider that there aren't any usable ones available\n// i.e. audioLevel > 0.008\nconst DETECTION_TIMEOUT = 3000;\n\n\n/**\n * Go through all audio devices on the system and return one that is active, i.e. has audio signal.\n *\n * @returns Promise - Object containing information about the found device.\n */\nexport default function getActiveAudioDevice() {\n\n return new Promise(resolve => {\n RTC.enumerateDevices(devices => {\n const audioDevices = devices.filter(device => device.kind === 'audioinput');\n const devicePromiseArray = [];\n\n\n for (const micDevice of audioDevices) {\n const devicePromise = RTC.obtainAudioAndVideoPermissions({ devices: [ 'audio' ],\n micDeviceId: micDevice.deviceId }).then(tracks => {\n\n // We expect a single device to be available when obtained from obtainAudioAndVideoPermissions\n // that's why only take p.value[0].\n const track = tracks[0];\n const originalStream = track.getOriginalStream();\n\n Statistics.startLocalStats(originalStream, track.setAudioLevel.bind(track));\n track.addEventListener(JitsiTrackEvents.LOCAL_TRACK_STOPPED, () => {\n Statistics.stopLocalStats(originalStream);\n });\n\n return track;\n });\n\n devicePromiseArray.push(devicePromise);\n }\n\n Promise.allSettled(devicePromiseArray).then(outcomeArray => {\n const successfulPromises = outcomeArray.filter(p => p.status === 'fulfilled');\n const rejectedPromises = outcomeArray.filter(p => p.status === 'rejected');\n\n\n const availableDevices = successfulPromises.map(p => p.value);\n const rejectReasons = rejectedPromises.map(p => p.value);\n\n for (const reason of rejectReasons) {\n logger.error('Failed to acquire audio device with error: ', reason);\n }\n\n // Setup event handlers for monitored devices.\n for (const device of availableDevices) {\n device.on(JitsiTrackEvents.TRACK_AUDIO_LEVEL_CHANGED, audioLevel => {\n // This is a very naive approach but works, a more accurate one would be to use rnnoise in\n // order to limit the number of false positives. The 0.008 constant is due to how\n // LocalStatsCollector from lib-jitsi-meet publishes audio-levels, in this case 0.008 denotes //\n // no input.\n if (audioLevel > 0.008) {\n stopActiveDevices(availableDevices);\n resolve({ deviceId: device.deviceId,\n deviceLabel: device.track.label });\n }\n });\n }\n\n // Cancel the detection in case no devices was found with audioLevel > 0 in the set timeout.\n setTimeout(() => {\n stopActiveDevices(availableDevices);\n resolve({\n deviceId: '',\n deviceLabel: '' }\n );\n }, DETECTION_TIMEOUT);\n\n });\n\n });\n });\n}\n\n/**\n * Stop the streams of the provided JitsiLocalTracks.\n *\n * @param {Array} deviceList - Array of JitsiLocalTracks to stop.\n * @returns {void}\n */\nfunction stopActiveDevices(deviceList) {\n for (const device of deviceList) {\n device.stopStream();\n }\n}\n","/**\n * The know jingle actions that can be sent and should be acted upon by\n * {@code ProxyConnectionService} and {@code ProxyConnectionPC}.\n */\nexport const ACTIONS = {\n ACCEPT: 'session-accept',\n CONNECTION_ERROR: 'connection-error-encountered',\n INITIATE: 'session-initiate',\n TERMINATE: 'session-terminate',\n TRANSPORT_INFO: 'transport-info',\n UNAVAILABLE: 'unavailable'\n};\n","import { getLogger } from '@jitsi/logger';\n\nimport RTCEvents from '../../service/RTC/RTCEvents';\nimport XMPPEvents from '../../service/xmpp/XMPPEvents';\nimport RTC from '../RTC/RTC';\nimport JingleSessionPC from '../xmpp/JingleSessionPC';\nimport SignalingLayerImpl from '../xmpp/SignalingLayerImpl';\nimport { DEFAULT_STUN_SERVERS } from '../xmpp/xmpp';\n\nimport { ACTIONS } from './constants';\n\nconst logger = getLogger(__filename);\n\n/**\n * An adapter around {@code JingleSessionPC} so its logic can be re-used without\n * an XMPP connection. It is being re-used for consistency with the rest of the\n * codebase and to leverage existing peer connection event handling. Also\n * this class provides a facade to hide most of the API for\n * {@code JingleSessionPC}.\n */\nexport default class ProxyConnectionPC {\n /**\n * Initializes a new {@code ProxyConnectionPC} instance.\n *\n * @param {Object} options - Values to initialize the instance with.\n * @param {Object} [options.pcConfig] - The {@code RTCConfiguration} to use for the WebRTC peer connection.\n * @param {boolean} [options.isInitiator] - If true, the local client should send offers. If false, the local\n * client should send answers. Defaults to false.\n * @param {Function} options.onRemoteStream - Callback to invoke when a remote media stream has been received\n * through the peer connection.\n * @param {string} options.peerJid - The jid of the remote client with which the peer connection is being establish\n * and which should receive direct messages regarding peer connection updates.\n * @param {boolean} [options.receiveVideo] - Whether or not the peer connection should accept incoming video\n * streams. Defaults to false.\n * @param {Function} options.onSendMessage - Callback to invoke when a message has to be sent (signaled) out.\n */\n constructor(options = {}) {\n this._options = {\n pcConfig: {},\n isInitiator: false,\n receiveAudio: false,\n receiveVideo: false,\n ...options\n };\n\n /**\n * Instances of {@code JitsiTrack} associated with this instance of\n * {@code ProxyConnectionPC}.\n *\n * @type {Array}\n */\n this._tracks = [];\n\n /**\n * The active instance of {@code JingleSessionPC}.\n *\n * @type {JingleSessionPC|null}\n */\n this._peerConnection = null;\n\n // Bind event handlers so they are only bound once for every instance.\n this._onError = this._onError.bind(this);\n this._onRemoteStream = this._onRemoteStream.bind(this);\n this._onSendMessage = this._onSendMessage.bind(this);\n }\n\n /**\n * Returns the jid of the remote peer with which this peer connection should\n * be established with.\n *\n * @returns {string}\n */\n getPeerJid() {\n return this._options.peerJid;\n }\n\n /**\n * Updates the peer connection based on the passed in jingle.\n *\n * @param {Object} $jingle - An XML jingle element, wrapped in query,\n * describing how the peer connection should be updated.\n * @returns {void}\n */\n processMessage($jingle) {\n switch ($jingle.attr('action')) {\n case ACTIONS.ACCEPT:\n this._onSessionAccept($jingle);\n break;\n\n case ACTIONS.INITIATE:\n this._onSessionInitiate($jingle);\n break;\n\n case ACTIONS.TERMINATE:\n this._onSessionTerminate($jingle);\n break;\n\n case ACTIONS.TRANSPORT_INFO:\n this._onTransportInfo($jingle);\n break;\n }\n }\n\n /**\n * Instantiates a peer connection and starts the offer/answer cycle to\n * establish a connection with a remote peer.\n *\n * @param {Array} localTracks - Initial local tracks to add\n * to add to the peer connection.\n * @returns {void}\n */\n start(localTracks = []) {\n if (this._peerConnection) {\n return;\n }\n\n this._tracks = this._tracks.concat(localTracks);\n\n this._peerConnection = this._createPeerConnection();\n\n this._peerConnection.invite(localTracks);\n }\n\n /**\n * Begins the process of disconnecting from a remote peer and cleaning up\n * the peer connection.\n *\n * @returns {void}\n */\n stop() {\n if (this._peerConnection) {\n this._peerConnection.terminate();\n }\n\n this._onSessionTerminate();\n }\n\n /**\n * Instantiates a new {@code JingleSessionPC} by stubbing out the various\n * dependencies of {@code JingleSessionPC}.\n *\n * @private\n * @returns {JingleSessionPC}\n */\n _createPeerConnection() {\n /**\n * {@code JingleSessionPC} takes in the entire jitsi-meet config.js\n * object, which may not be accessible from the caller.\n *\n * @type {Object}\n */\n const configStub = {};\n\n /**\n * {@code JingleSessionPC} assumes an XMPP/Strophe connection object is\n * passed through, which also has the jingle plugin initialized on it.\n * This connection object is used to signal out peer connection updates\n * via iqs, and those updates need to be piped back out to the remote\n * peer.\n *\n * @type {Object}\n */\n const connectionStub = {\n // At the time this is used for Spot and it's okay to say the connection is always connected, because if\n // spot has no signalling it will not be in a meeting where this is used.\n connected: true,\n jingle: {\n terminate: () => { /** no-op */ }\n },\n sendIQ: this._onSendMessage,\n\n // Returns empty function, because it does not add any listeners for real\n // eslint-disable-next-line no-empty-function\n addEventListener: () => () => { }\n };\n\n /**\n * {@code JingleSessionPC} can take in a custom ice configuration,\n * depending on the peer connection type, peer-to-peer or other.\n * However, {@code ProxyConnectionPC} always assume a peer-to-peer\n * connection so the ice configuration is hard-coded with defaults.\n *\n * @type {Object}\n */\n const pcConfigStub = {\n iceServers: DEFAULT_STUN_SERVERS,\n ...this._options.pcConfig\n };\n\n /**\n * {@code JingleSessionPC} expects an instance of\n * {@code JitsiConference}, which has an event emitter that is used\n * to signal various connection updates that the local client should\n * act upon. The conference instance is not a dependency of a proxy\n * connection, but the emitted events can be relevant to the proxy\n * connection so the event emitter is stubbed.\n *\n * @param {string} event - The constant for the event type.\n * @type {Function}\n * @returns {void}\n */\n const emitter = event => {\n switch (event) {\n case XMPPEvents.CONNECTION_ICE_FAILED:\n case XMPPEvents.CONNECTION_FAILED:\n this._onError(ACTIONS.CONNECTION_ERROR, event);\n break;\n }\n };\n\n /**\n * {@link JingleSessionPC} expects an instance of\n * {@link ChatRoom} to be passed in. {@link ProxyConnectionPC}\n * is instantiated outside of the {@code JitsiConference}, so it must be\n * stubbed to prevent errors.\n *\n * @type {Object}\n */\n const roomStub = {\n addPresenceListener: () => { /** no-op */ },\n connectionTimes: [],\n eventEmitter: { emit: emitter },\n getMediaPresenceInfo: () => {\n // Errors occur if this function does not return an object\n\n return {};\n },\n removePresenceListener: () => { /** no-op */ },\n supportsRestartByTerminate: () => false\n };\n\n /**\n * A {@code JitsiConference} stub passed to the {@link RTC} module.\n * @type {Object}\n */\n const conferenceStub = {\n myUserId: () => ''\n };\n\n /**\n * Create an instance of {@code RTC} as it is required for peer\n * connection creation by {@code JingleSessionPC}. An existing instance\n * of {@code RTC} from elsewhere should not be re-used because it is\n * a stateful grouping of utilities.\n */\n this._rtc = new RTC(conferenceStub, {});\n\n /**\n * Add the remote track listener here as {@code JingleSessionPC} has\n * {@code TraceablePeerConnection} which uses {@code RTC}'s event\n * emitter.\n */\n this._rtc.addListener(\n RTCEvents.REMOTE_TRACK_ADDED,\n this._onRemoteStream\n );\n\n const peerConnection = new JingleSessionPC(\n undefined, // sid\n undefined, // localJid\n this._options.peerJid, // remoteJid\n connectionStub, // connection\n {\n offerToReceiveAudio: this._options.receiveAudio,\n offerToReceiveVideo: this._options.receiveVideo\n }, // mediaConstraints\n pcConfigStub, // pcConfig\n true, // isP2P\n this._options.isInitiator // isInitiator\n );\n\n const signalingLayer = new SignalingLayerImpl();\n\n signalingLayer.setChatRoom(roomStub);\n\n /**\n * An additional initialize call is necessary to properly set instance\n * variable for calling.\n */\n peerConnection.initialize(roomStub, this._rtc, signalingLayer, configStub);\n\n return peerConnection;\n }\n\n /**\n * Invoked when a connection related issue has been encountered.\n *\n * @param {string} errorType - The constant indicating the type of the error\n * that occured.\n * @param {string} details - Optional additional data about the error.\n * @private\n * @returns {void}\n */\n _onError(errorType, details = '') {\n this._options.onError(this._options.peerJid, errorType, details);\n }\n\n /**\n * Callback invoked when the peer connection has received a remote media\n * stream.\n *\n * @param {JitsiRemoteTrack} jitsiRemoteTrack - The remote media stream\n * wrapped in {@code JitsiRemoteTrack}.\n * @private\n * @returns {void}\n */\n _onRemoteStream(jitsiRemoteTrack) {\n this._tracks.push(jitsiRemoteTrack);\n\n this._options.onRemoteStream(jitsiRemoteTrack);\n }\n\n /**\n * Callback invoked when {@code JingleSessionPC} needs to signal a message\n * out to the remote peer.\n *\n * @param {XML} iq - The message to signal out.\n * @private\n * @returns {void}\n */\n _onSendMessage(iq) {\n this._options.onSendMessage(this._options.peerJid, iq);\n }\n\n /**\n * Callback invoked in response to an agreement to start a proxy connection.\n * The passed in jingle element should contain an SDP answer to a previously\n * sent SDP offer.\n *\n * @param {Object} $jingle - The jingle element wrapped in jQuery.\n * @private\n * @returns {void}\n */\n _onSessionAccept($jingle) {\n if (!this._peerConnection) {\n logger.error('Received an answer when no peer connection exists.');\n\n return;\n }\n\n this._peerConnection.setAnswer($jingle);\n }\n\n /**\n * Callback invoked in response to a request to start a proxy connection.\n * The passed in jingle element should contain an SDP offer.\n *\n * @param {Object} $jingle - The jingle element wrapped in jQuery.\n * @private\n * @returns {void}\n */\n _onSessionInitiate($jingle) {\n if (this._peerConnection) {\n logger.error('Received an offer when an offer was already sent.');\n\n return;\n }\n\n this._peerConnection = this._createPeerConnection();\n\n this._peerConnection.acceptOffer(\n $jingle,\n () => { /** no-op */ },\n () => this._onError(\n this._options.peerJid,\n ACTIONS.CONNECTION_ERROR,\n 'session initiate error'\n )\n );\n }\n\n /**\n * Callback invoked in response to a request to disconnect an active proxy\n * connection. Cleans up tracks and the peer connection.\n *\n * @private\n * @returns {void}\n */\n _onSessionTerminate() {\n this._tracks.forEach(track => track.dispose());\n this._tracks = [];\n\n if (this._peerConnection) {\n this._peerConnection.onTerminated();\n }\n\n if (this._rtc) {\n this._rtc.removeListener(\n RTCEvents.REMOTE_TRACK_ADDED,\n this._onRemoteStream\n );\n\n this._rtc.destroy();\n }\n }\n\n /**\n * Callback invoked in response to ICE candidates from the remote peer.\n * The passed in jingle element should contain an ICE candidate.\n *\n * @param {Object} $jingle - The jingle element wrapped in jQuery.\n * @private\n * @returns {void}\n */\n _onTransportInfo($jingle) {\n this._peerConnection.addIceCandidates($jingle);\n }\n}\n","/* globals $ */\n\nimport { getLogger } from '@jitsi/logger';\nimport { $iq } from 'strophe.js';\n\nimport * as MediaType from '../../service/RTC/MediaType';\nimport VideoType from '../../service/RTC/VideoType';\nimport RTC from '../RTC/RTC';\n\nimport ProxyConnectionPC from './ProxyConnectionPC';\nimport { ACTIONS } from './constants';\n\nconst logger = getLogger(__filename);\n\n/**\n * Instantiates a new ProxyConnectionPC and ensures only one exists at a given\n * time. Currently it assumes ProxyConnectionPC is used only for screensharing\n * and assumes IQs to be used for communication.\n */\nexport default class ProxyConnectionService {\n /**\n * Initializes a new {@code ProxyConnectionService} instance.\n *\n * @param {Object} options - Values to initialize the instance with.\n * @param {boolean} [options.convertVideoToDesktop] - Whether or not proxied video should be returned as a desktop\n * stream. Defaults to false.\n * @param {Object} [options.pcConfig] - The {@code RTCConfiguration} to use for the WebRTC peer connection.\n * @param {JitsiConnection} [options.jitsiConnection] - The {@code JitsiConnection} which will be used to fetch\n * TURN credentials for the P2P connection.\n * @param {Function} options.onRemoteStream - Callback to invoke when a remote video stream has been received and\n * converted to a {@code JitsiLocakTrack}. The {@code JitsiLocakTrack} will be passed in.\n * @param {Function} options.onSendMessage - Callback to invoke when a message has to be sent (signaled) out. The\n * arguments passed in are the jid to send the message to and the message.\n */\n constructor(options = {}) {\n const {\n jitsiConnection,\n ...otherOptions\n } = options;\n\n /**\n * Holds a reference to the collection of all callbacks.\n *\n * @type {Object}\n */\n this._options = {\n pcConfig: jitsiConnection && jitsiConnection.xmpp.connection.jingle.p2pIceConfig,\n ...otherOptions\n };\n\n /**\n * The active instance of {@code ProxyConnectionService}.\n *\n * @type {ProxyConnectionPC|null}\n */\n this._peerConnection = null;\n\n // Bind event handlers so they are only bound once for every instance.\n this._onFatalError = this._onFatalError.bind(this);\n this._onSendMessage = this._onSendMessage.bind(this);\n this._onRemoteStream = this._onRemoteStream.bind(this);\n }\n\n /**\n * Parses a message object regarding a proxy connection to create a new\n * proxy connection or update and existing connection.\n *\n * @param {Object} message - A message object regarding establishing or\n * updating a proxy connection.\n * @param {Object} message.data - An object containing additional message\n * details.\n * @param {string} message.data.iq - The stringified iq which explains how\n * and what to update regarding the proxy connection.\n * @param {string} message.from - The message sender's full jid. Used for\n * sending replies.\n * @returns {void}\n */\n processMessage(message) {\n const peerJid = message.from;\n\n if (!peerJid) {\n return;\n }\n\n // If a proxy connection has already been established and messages come\n // from another peer jid then those messages should be replied to with\n // a rejection.\n if (this._peerConnection\n && this._peerConnection.getPeerJid() !== peerJid) {\n this._onFatalError(\n peerJid,\n ACTIONS.CONNECTION_ERROR,\n 'rejected'\n );\n\n return;\n }\n\n const iq = this._convertStringToXML(message.data.iq);\n const $jingle = iq && iq.find('jingle');\n const action = $jingle && $jingle.attr('action');\n\n if (action === ACTIONS.INITIATE) {\n this._peerConnection = this._createPeerConnection(peerJid, {\n isInitiator: false,\n receiveVideo: true\n });\n }\n\n // Truthy check for peer connection added to protect against possibly\n // receiving actions before an ACTIONS.INITIATE.\n if (this._peerConnection) {\n this._peerConnection.processMessage($jingle);\n }\n\n // Take additional steps to ensure the peer connection is cleaned up\n // if it is to be closed.\n if (action === ACTIONS.CONNECTION_ERROR\n || action === ACTIONS.UNAVAILABLE\n || action === ACTIONS.TERMINATE) {\n this._selfCloseConnection();\n }\n\n return;\n }\n\n /**\n * Instantiates and initiates a proxy peer connection.\n *\n * @param {string} peerJid - The jid of the remote client that should\n * receive messages.\n * @param {Array} localTracks - Initial media tracks to\n * send through to the peer.\n * @returns {void}\n */\n start(peerJid, localTracks = []) {\n this._peerConnection = this._createPeerConnection(peerJid, {\n isInitiator: true,\n receiveVideo: false\n });\n\n this._peerConnection.start(localTracks);\n }\n\n /**\n * Terminates any active proxy peer connection.\n *\n * @returns {void}\n */\n stop() {\n if (this._peerConnection) {\n this._peerConnection.stop();\n }\n\n this._peerConnection = null;\n }\n\n /**\n * Transforms a stringified xML into a XML wrapped in jQuery.\n *\n * @param {string} xml - The XML in string form.\n * @private\n * @returns {Object|null} A jQuery version of the xml. Null will be returned\n * if an error is encountered during transformation.\n */\n _convertStringToXML(xml) {\n try {\n const xmlDom = new DOMParser().parseFromString(xml, 'text/xml');\n\n return $(xmlDom);\n } catch (e) {\n logger.error('Attempted to convert incorrectly formatted xml');\n\n return null;\n }\n }\n\n /**\n * Helper for creating an instance of {@code ProxyConnectionPC}.\n *\n * @param {string} peerJid - The jid of the remote peer with which the\n * {@code ProxyConnectionPC} will be established with.\n * @param {Object} options - Additional defaults to instantiate the\n * {@code ProxyConnectionPC} with. See the constructor of ProxyConnectionPC\n * for more details.\n * @private\n * @returns {ProxyConnectionPC}\n */\n _createPeerConnection(peerJid, options = {}) {\n if (!peerJid) {\n throw new Error('Cannot create ProxyConnectionPC without a peer.');\n }\n\n const pcOptions = {\n pcConfig: this._options.pcConfig,\n onError: this._onFatalError,\n onRemoteStream: this._onRemoteStream,\n onSendMessage: this._onSendMessage,\n peerJid,\n ...options\n };\n\n return new ProxyConnectionPC(pcOptions);\n }\n\n /**\n * Callback invoked when an error occurs that should cause\n * {@code ProxyConnectionPC} to be closed if the peer is currently\n * connected. Sends an error message/reply back to the peer.\n *\n * @param {string} peerJid - The peer jid with which the connection was\n * attempted or started, and to which an iq with error details should be\n * sent.\n * @param {string} errorType - The constant indicating the type of the error\n * that occured.\n * @param {string} details - Optional additional data about the error.\n * @private\n * @returns {void}\n */\n _onFatalError(peerJid, errorType, details = '') {\n logger.error(\n 'Received a proxy connection error', peerJid, errorType, details);\n\n const iq = $iq({\n to: peerJid,\n type: 'set'\n })\n .c('jingle', {\n xmlns: 'urn:xmpp:jingle:1',\n action: errorType\n })\n .c('details')\n .t(details)\n .up();\n\n this._onSendMessage(peerJid, iq);\n\n if (this._peerConnection\n && this._peerConnection.getPeerJid() === peerJid) {\n this._selfCloseConnection();\n }\n }\n\n /**\n * Callback invoked when the remote peer of the {@code ProxyConnectionPC}\n * has offered a media stream. The stream is converted into a\n * {@code JitsiLocalTrack} for local usage if the {@code onRemoteStream}\n * callback is defined.\n *\n * @param {JitsiRemoteTrack} jitsiRemoteTrack - The {@code JitsiRemoteTrack}\n * for the peer's media stream.\n * @private\n * @returns {void}\n */\n _onRemoteStream(jitsiRemoteTrack) {\n if (!this._options.onRemoteStream) {\n logger.error('Remote track received without callback.');\n jitsiRemoteTrack.dispose();\n\n return;\n }\n\n const isVideo = jitsiRemoteTrack.isVideoTrack();\n let videoType;\n\n if (isVideo) {\n videoType = this._options.convertVideoToDesktop\n ? VideoType.DESKTOP : VideoType.CAMERA;\n }\n\n // Grab the webrtc media stream and pipe it through the same processing\n // that would occur for a locally obtained media stream.\n const mediaStream = jitsiRemoteTrack.getOriginalStream();\n const jitsiLocalTracks = RTC.createLocalTracks(\n [\n {\n deviceId:\n `proxy:${this._peerConnection.getPeerJid()}`,\n mediaType: isVideo ? MediaType.VIDEO : MediaType.AUDIO,\n sourceType: 'proxy',\n stream: mediaStream,\n track: mediaStream.getVideoTracks()[0],\n videoType\n }\n ]);\n\n this._options.onRemoteStream(jitsiLocalTracks[0]);\n }\n\n /**\n * Formats and forwards a message an iq to be sent to a peer jid.\n *\n * @param {string} peerJid - The jid the iq should be sent to.\n * @param {Object} iq - The iq which would be sent to the peer jid.\n * @private\n * @returns {void}\n */\n _onSendMessage(peerJid, iq) {\n if (!this._options.onSendMessage) {\n return;\n }\n\n try {\n const stringifiedIq\n = new XMLSerializer().serializeToString(iq.nodeTree || iq);\n\n this._options.onSendMessage(peerJid, { iq: stringifiedIq });\n } catch (e) {\n logger.error('Attempted to send an incorrectly formatted iq.');\n }\n }\n\n /**\n * Invoked when preemptively closing the {@code ProxyConnectionPC}.\n *\n * @private\n * @returns {void}\n */\n _selfCloseConnection() {\n this.stop();\n\n this._options.onConnectionClosed\n && this._options.onConnectionClosed();\n }\n}\n","import EventEmitter from 'events';\n\nimport browser from '../browser';\nimport Settings from '../settings/Settings';\nimport ScriptUtil from '../util/ScriptUtil';\n\nimport { CALLSTATS_SCRIPT_URL } from './constants';\n\nconst PRECALL_TEST_RESULTS = 'preCallTestResults';\nconst emitter = new EventEmitter();\nlet _initialized = false;\nlet api = null;\n\n/**\n * Loads the callstats io script.\n *\n * @returns {Promise}\n */\nfunction _loadScript(options) {\n if (browser.isReactNative()) {\n return;\n }\n\n return new Promise(resolve => {\n ScriptUtil.loadScript(\n options.callStatsCustomScriptUrl || CALLSTATS_SCRIPT_URL,\n /* async */ true,\n /* prepend */ true,\n /* relativeURL */ undefined,\n /* loadCallback */ resolve);\n });\n}\n\n/**\n * Initializes the callstats lib and registers a callback to be invoked\n * when there are 'preCallTestResults'.\n *\n * @typedef PrecallTestOptions\n * @type {Object}\n * @property {string} callStatsID - Callstats credentials - the id.\n * @property {string} callStatsSecret - Callstats credentials - the secret.\n * @property {string} statisticsId - The user name to use when initializing callstats.\n * @property {string} statisticsDisplayName - The user display name.\n *\n * @param { PrecallTestOptions} options - The init options.\n * @returns {Promise}\n */\nfunction _initialize(options) {\n return new Promise((resolve, reject) => {\n const appId = options.callStatsID;\n const appSecret = options.callStatsSecret;\n const userId = options.statisticsId || options.statisticsDisplayName || Settings.callStatsUserName;\n\n api.initialize(appId, appSecret, userId, (status, message) => {\n if (status === 'success') {\n api.on(PRECALL_TEST_RESULTS, (...args) => {\n emitter.emit(PRECALL_TEST_RESULTS, ...args);\n });\n _initialized = true;\n resolve();\n } else {\n reject({\n status,\n message\n });\n }\n }, null, { disablePrecalltest: true });\n });\n}\n\n/**\n * Loads the callstats script and initializes the library.\n *\n * @param {Function} onResult - The callback to be invoked when results are received.\n * @returns {Promise}\n */\nexport async function init(options) {\n if (_initialized) {\n throw new Error('Precall Test already initialized');\n }\n\n const { callStatsID, callStatsSecret, disableThirdPartyRequests } = options;\n\n if (!callStatsID || !callStatsSecret || disableThirdPartyRequests) {\n throw new Error('Callstats is disabled');\n }\n\n await _loadScript(options);\n // eslint-disable-next-line new-cap\n api = new window.callstats();\n\n return _initialize(options);\n}\n\n/**\n * Executes a pre call test.\n *\n * @typedef PrecallTestResults\n * @type {Object}\n * @property {boolean} mediaConnectivity - If there is media connectivity or not.\n * @property {number} throughput - The average throughput.\n * @property {number} fractionalLoss - The packet loss.\n * @property {number} rtt - The round trip time.\n * @property {string} provider - It is usually 'callstats'.\n *\n * @returns {Promise<{PrecallTestResults}>}\n */\nexport function execute() {\n if (!_initialized) {\n return Promise.reject('uninitialized');\n }\n\n return new Promise((resolve, reject) => {\n emitter.on(PRECALL_TEST_RESULTS, (status, payload) => {\n if (status === 'success') {\n resolve(payload);\n } else {\n reject({\n status,\n payload\n });\n }\n\n });\n\n api.makePrecallTest();\n });\n}\n\nexport default {\n init,\n execute\n};\n","import { getLogger } from '@jitsi/logger';\n\nimport { createAudioContext } from './WebAudioUtils';\n\nconst logger = getLogger(__filename);\n\n/**\n * The AudioMixer, as the name implies, mixes a number of MediaStreams containing audio tracks into a single\n * MediaStream.\n */\nexport default class AudioMixer {\n /**\n * Create AudioMixer instance.\n */\n constructor() {\n this._started = false;\n this._streamsToMix = [];\n this._streamMSSArray = [];\n }\n\n /**\n * Add audio MediaStream to be mixed, if the stream doesn't contain any audio tracks it will be ignored.\n *\n * @param {MediaStream} stream - MediaStream to be mixed.\n */\n addMediaStream(stream) {\n if (!stream.getAudioTracks()) {\n logger.warn('Added MediaStream doesn\\'t contain audio tracks.');\n }\n\n this._streamsToMix.push(stream);\n }\n\n /**\n * At this point a WebAudio ChannelMergerNode is created and and the two associated MediaStreams are connected to\n * it; the resulting mixed MediaStream is returned.\n *\n * @returns {MediaStream} - MediaStream containing added streams mixed together, or null if no MediaStream\n * is added.\n */\n start() {\n // If the mixer was already started just return the existing mixed stream.\n if (this._started) {\n return this._mixedMSD.stream;\n }\n\n this._audioContext = createAudioContext();\n\n if (!this._streamsToMix.length) {\n logger.warn('No MediaStream\\'s added to AudioMixer, nothing will happen.');\n\n return null;\n }\n\n this._started = true;\n\n this._mixedMSD = this._audioContext.createMediaStreamDestination();\n\n for (const stream of this._streamsToMix) {\n const streamMSS = this._audioContext.createMediaStreamSource(stream);\n\n streamMSS.connect(this._mixedMSD);\n\n // Maintain a list of MediaStreamAudioSourceNode so we can disconnect them on reset.\n this._streamMSSArray.push(streamMSS);\n }\n\n return this._mixedMSD.stream;\n }\n\n /**\n * Disconnect MediaStreamAudioSourceNode and clear references.\n *\n * @returns {void}\n */\n reset() {\n this._started = false;\n this._streamsToMix = [];\n\n // Clean up created MediaStreamAudioSourceNode.\n for (const streamMSS of this._streamMSSArray) {\n streamMSS.disconnect();\n }\n\n this._streamMSSArray = [];\n\n if (this._audioContext) {\n this._audioContext = undefined;\n }\n }\n}\n","import Logger from '@jitsi/logger';\n\nimport * as JitsiConferenceErrors from './JitsiConferenceErrors';\nimport * as JitsiConferenceEvents from './JitsiConferenceEvents';\nimport JitsiConnection from './JitsiConnection';\nimport * as JitsiConnectionErrors from './JitsiConnectionErrors';\nimport * as JitsiConnectionEvents from './JitsiConnectionEvents';\nimport JitsiMediaDevices from './JitsiMediaDevices';\nimport * as JitsiMediaDevicesEvents from './JitsiMediaDevicesEvents';\nimport JitsiTrackError from './JitsiTrackError';\nimport * as JitsiTrackErrors from './JitsiTrackErrors';\nimport * as JitsiTrackEvents from './JitsiTrackEvents';\nimport * as JitsiTranscriptionStatus from './JitsiTranscriptionStatus';\nimport RTC from './modules/RTC/RTC';\nimport browser from './modules/browser';\nimport NetworkInfo from './modules/connectivity/NetworkInfo';\nimport { ParticipantConnectionStatus }\n from './modules/connectivity/ParticipantConnectionStatus';\nimport { TrackStreamingStatus } from './modules/connectivity/TrackStreamingStatus';\nimport getActiveAudioDevice from './modules/detection/ActiveDeviceDetector';\nimport * as DetectionEvents from './modules/detection/DetectionEvents';\nimport TrackVADEmitter from './modules/detection/TrackVADEmitter';\nimport FeatureFlags from './modules/flags/FeatureFlags';\nimport ProxyConnectionService\n from './modules/proxyconnection/ProxyConnectionService';\nimport recordingConstants from './modules/recording/recordingConstants';\nimport Settings from './modules/settings/Settings';\nimport LocalStatsCollector from './modules/statistics/LocalStatsCollector';\nimport precallTest from './modules/statistics/PrecallTest';\nimport Statistics from './modules/statistics/statistics';\nimport AuthUtil from './modules/util/AuthUtil';\nimport GlobalOnErrorHandler from './modules/util/GlobalOnErrorHandler';\nimport ScriptUtil from './modules/util/ScriptUtil';\nimport * as VideoSIPGWConstants from './modules/videosipgw/VideoSIPGWConstants';\nimport AudioMixer from './modules/webaudio/AudioMixer';\nimport * as MediaType from './service/RTC/MediaType';\nimport * as ConnectionQualityEvents\n from './service/connectivity/ConnectionQualityEvents';\nimport * as E2ePingEvents from './service/e2eping/E2ePingEvents';\nimport { createGetUserMediaEvent } from './service/statistics/AnalyticsEvents';\n\nconst logger = Logger.getLogger(__filename);\n\n/**\n * The amount of time to wait until firing\n * {@link JitsiMediaDevicesEvents.PERMISSION_PROMPT_IS_SHOWN} event.\n */\nconst USER_MEDIA_SLOW_PROMISE_TIMEOUT = 1000;\n\n/**\n * Extracts from an 'options' objects with a specific format (TODO what IS the\n * format?) the attributes which are to be logged in analytics events.\n *\n * @param options gum options (???)\n * @returns {*} the attributes to attach to analytics events.\n */\nfunction getAnalyticsAttributesFromOptions(options) {\n const attributes = {\n 'audio_requested':\n options.devices.includes('audio'),\n 'video_requested':\n options.devices.includes('video'),\n 'screen_sharing_requested':\n options.devices.includes('desktop')\n };\n\n if (attributes.video_requested) {\n attributes.resolution = options.resolution;\n }\n\n return attributes;\n}\n\n/**\n * Tries to deal with the following problem: {@code JitsiMeetJS} is not only\n * this module, it's also a global (i.e. attached to {@code window}) namespace\n * for all globals of the projects in the Jitsi Meet family. If lib-jitsi-meet\n * is loaded through an HTML {@code script} tag, {@code JitsiMeetJS} will\n * automatically be attached to {@code window} by webpack. Unfortunately,\n * webpack's source code does not check whether the global variable has already\n * been assigned and overwrites it. Which is OK for the module\n * {@code JitsiMeetJS} but is not OK for the namespace {@code JitsiMeetJS}\n * because it may already contain the values of other projects in the Jitsi Meet\n * family. The solution offered here works around webpack by merging all\n * existing values of the namespace {@code JitsiMeetJS} into the module\n * {@code JitsiMeetJS}.\n *\n * @param {Object} module - The module {@code JitsiMeetJS} (which will be\n * exported and may be attached to {@code window} by webpack later on).\n * @private\n * @returns {Object} - A {@code JitsiMeetJS} module which contains all existing\n * value of the namespace {@code JitsiMeetJS} (if any).\n */\nfunction _mergeNamespaceAndModule(module) {\n return (\n typeof window.JitsiMeetJS === 'object'\n ? Object.assign({}, window.JitsiMeetJS, module)\n : module);\n}\n\n/**\n * The public API of the Jitsi Meet library (a.k.a. {@code JitsiMeetJS}).\n */\nexport default _mergeNamespaceAndModule({\n\n version: '{#COMMIT_HASH#}',\n\n JitsiConnection,\n\n /**\n * {@code ProxyConnectionService} is used to connect a remote peer to a\n * local Jitsi participant without going through a Jitsi conference. It is\n * currently used for room integration development, specifically wireless\n * screensharing. Its API is experimental and will likely change; usage of\n * it is advised against.\n */\n ProxyConnectionService,\n\n constants: {\n participantConnectionStatus: ParticipantConnectionStatus,\n recording: recordingConstants,\n sipVideoGW: VideoSIPGWConstants,\n transcriptionStatus: JitsiTranscriptionStatus,\n trackStreamingStatus: TrackStreamingStatus\n },\n events: {\n conference: JitsiConferenceEvents,\n connection: JitsiConnectionEvents,\n detection: DetectionEvents,\n track: JitsiTrackEvents,\n mediaDevices: JitsiMediaDevicesEvents,\n connectionQuality: ConnectionQualityEvents,\n e2eping: E2ePingEvents\n },\n errors: {\n conference: JitsiConferenceErrors,\n connection: JitsiConnectionErrors,\n track: JitsiTrackErrors\n },\n errorTypes: {\n JitsiTrackError\n },\n logLevels: Logger.levels,\n mediaDevices: JitsiMediaDevices,\n analytics: Statistics.analytics,\n init(options = {}) {\n Settings.init(options.externalStorage);\n Statistics.init(options);\n\n // Configure the feature flags.\n FeatureFlags.init(options.flags || { });\n\n // Initialize global window.connectionTimes\n // FIXME do not use 'window'\n if (!window.connectionTimes) {\n window.connectionTimes = {};\n }\n\n if (options.enableAnalyticsLogging !== true) {\n logger.warn('Analytics disabled, disposing.');\n this.analytics.dispose();\n }\n\n if (options.enableWindowOnErrorHandler) {\n GlobalOnErrorHandler.addHandler(\n this.getGlobalOnErrorHandler.bind(this));\n }\n\n if (this.version) {\n const logObject = {\n id: 'component_version',\n component: 'lib-jitsi-meet',\n version: this.version\n };\n\n Statistics.sendLog(JSON.stringify(logObject));\n }\n\n return RTC.init(options);\n },\n\n /**\n * Returns whether the desktop sharing is enabled or not.\n *\n * @returns {boolean}\n */\n isDesktopSharingEnabled() {\n return RTC.isDesktopSharingEnabled();\n },\n\n /**\n * Returns whether the current execution environment supports WebRTC (for\n * use within this library).\n *\n * @returns {boolean} {@code true} if WebRTC is supported in the current\n * execution environment (for use within this library); {@code false},\n * otherwise.\n */\n isWebRtcSupported() {\n return RTC.isWebRtcSupported();\n },\n\n setLogLevel(level) {\n Logger.setLogLevel(level);\n },\n\n /**\n * Sets the log level to the Logger instance with given id.\n *\n * @param {Logger.levels} level the logging level to be set\n * @param {string} id the logger id to which new logging level will be set.\n * Usually it's the name of the JavaScript source file including the path\n * ex. \"modules/xmpp/ChatRoom.js\"\n */\n setLogLevelById(level, id) {\n Logger.setLogLevelById(level, id);\n },\n\n /**\n * Registers new global logger transport to the library logging framework.\n *\n * @param globalTransport\n * @see Logger.addGlobalTransport\n */\n addGlobalLogTransport(globalTransport) {\n Logger.addGlobalTransport(globalTransport);\n },\n\n /**\n * Removes global logging transport from the library logging framework.\n *\n * @param globalTransport\n * @see Logger.removeGlobalTransport\n */\n removeGlobalLogTransport(globalTransport) {\n Logger.removeGlobalTransport(globalTransport);\n },\n\n /**\n * Sets global options which will be used by all loggers. Changing these\n * works even after other loggers are created.\n *\n * @param options\n * @see Logger.setGlobalOptions\n */\n setGlobalLogOptions(options) {\n Logger.setGlobalOptions(options);\n },\n\n /**\n * Creates the media tracks and returns them trough the callback.\n *\n * @param options Object with properties / settings specifying the tracks\n * which should be created. should be created or some additional\n * configurations about resolution for example.\n * @param {Array} options.effects optional effects array for the track\n * @param {boolean} options.firePermissionPromptIsShownEvent - if event\n * JitsiMediaDevicesEvents.PERMISSION_PROMPT_IS_SHOWN should be fired\n * @param {boolean} options.fireSlowPromiseEvent - if event\n * JitsiMediaDevicesEvents.USER_MEDIA_SLOW_PROMISE_TIMEOUT should be fired\n * @param {Array} options.devices the devices that will be requested\n * @param {string} options.resolution resolution constraints\n * @param {string} options.cameraDeviceId\n * @param {string} options.micDeviceId\n * @param {intiger} interval - the interval (in ms) for\n * checking whether the desktop sharing extension is installed or not\n * @param {Function} checkAgain - returns boolean. While checkAgain()==true\n * createLocalTracks will wait and check on every \"interval\" ms for the\n * extension. If the desktop extension is not install and checkAgain()==true\n * createLocalTracks will finish with rejected Promise.\n * @param {Function} listener - The listener will be called to notify the\n * user of lib-jitsi-meet that createLocalTracks is starting external\n * extension installation process.\n * NOTE: If the inline installation process is not possible and external\n * installation is enabled the listener property will be called to notify\n * the start of external installation process. After that createLocalTracks\n * will start to check for the extension on every interval ms until the\n * plugin is installed or until checkAgain return false. If the extension\n * is found createLocalTracks will try to get the desktop sharing track and\n * will finish the execution. If checkAgain returns false, createLocalTracks\n * will finish the execution with rejected Promise.\n *\n * @deprecated old firePermissionPromptIsShownEvent\n * @returns {Promise.<{Array.}, JitsiConferenceError>} A promise\n * that returns an array of created JitsiTracks if resolved, or a\n * JitsiConferenceError if rejected.\n */\n createLocalTracks(options = {}, oldfirePermissionPromptIsShownEvent) {\n let promiseFulfilled = false;\n\n const { firePermissionPromptIsShownEvent, fireSlowPromiseEvent, ...restOptions } = options;\n const firePermissionPrompt = firePermissionPromptIsShownEvent || oldfirePermissionPromptIsShownEvent;\n\n if (firePermissionPrompt && !RTC.arePermissionsGrantedForAvailableDevices()) {\n JitsiMediaDevices.emitEvent(\n JitsiMediaDevicesEvents.PERMISSION_PROMPT_IS_SHOWN,\n browser.getName());\n } else if (fireSlowPromiseEvent) {\n window.setTimeout(() => {\n if (!promiseFulfilled) {\n JitsiMediaDevices.emitEvent(JitsiMediaDevicesEvents.SLOW_GET_USER_MEDIA);\n }\n }, USER_MEDIA_SLOW_PROMISE_TIMEOUT);\n }\n\n if (!window.connectionTimes) {\n window.connectionTimes = {};\n }\n window.connectionTimes['obtainPermissions.start']\n = window.performance.now();\n\n return RTC.obtainAudioAndVideoPermissions(restOptions)\n .then(tracks => {\n promiseFulfilled = true;\n\n window.connectionTimes['obtainPermissions.end']\n = window.performance.now();\n\n Statistics.sendAnalytics(\n createGetUserMediaEvent(\n 'success',\n getAnalyticsAttributesFromOptions(restOptions)));\n\n if (!RTC.options.disableAudioLevels) {\n for (let i = 0; i < tracks.length; i++) {\n const track = tracks[i];\n const mStream = track.getOriginalStream();\n\n if (track.getType() === MediaType.AUDIO) {\n Statistics.startLocalStats(mStream,\n track.setAudioLevel.bind(track));\n track.addEventListener(\n JitsiTrackEvents.LOCAL_TRACK_STOPPED,\n () => {\n Statistics.stopLocalStats(mStream);\n });\n }\n }\n }\n\n // set real device ids\n const currentlyAvailableMediaDevices\n = RTC.getCurrentlyAvailableMediaDevices();\n\n if (currentlyAvailableMediaDevices) {\n for (let i = 0; i < tracks.length; i++) {\n const track = tracks[i];\n\n track._setRealDeviceIdFromDeviceList(\n currentlyAvailableMediaDevices);\n }\n }\n\n // set the contentHint to \"detail\" for desktop tracks\n // eslint-disable-next-line prefer-const\n for (const track of tracks) {\n if (track.type === MediaType.VIDEO\n && track.videoType === 'desktop') {\n this.setVideoTrackContentHints(track.track, 'detail');\n }\n }\n\n return tracks;\n })\n .catch(error => {\n promiseFulfilled = true;\n\n if (error.name === JitsiTrackErrors.SCREENSHARING_USER_CANCELED) {\n // User cancelled action is not really an error, so only\n // log it as an event to avoid having conference classified\n // as partially failed\n const logObject = {\n id: 'screensharing_user_canceled',\n message: error.message\n };\n\n Statistics.sendLog(JSON.stringify(logObject));\n\n Statistics.sendAnalytics(\n createGetUserMediaEvent(\n 'warning',\n {\n reason: 'extension install user canceled'\n }));\n } else if (error.name === JitsiTrackErrors.NOT_FOUND) {\n // logs not found devices with just application log to cs\n const logObject = {\n id: 'usermedia_missing_device',\n status: error.gum.devices\n };\n\n Statistics.sendLog(JSON.stringify(logObject));\n\n const attributes\n = getAnalyticsAttributesFromOptions(options);\n\n attributes.reason = 'device not found';\n attributes.devices = error.gum.devices.join('.');\n Statistics.sendAnalytics(\n createGetUserMediaEvent('error', attributes));\n } else {\n // Report gUM failed to the stats\n Statistics.sendGetUserMediaFailed(error);\n\n const attributes\n = getAnalyticsAttributesFromOptions(options);\n\n attributes.reason = error.name;\n Statistics.sendAnalytics(\n createGetUserMediaEvent('error', attributes));\n }\n\n window.connectionTimes['obtainPermissions.end']\n = window.performance.now();\n\n return Promise.reject(error);\n });\n },\n\n /**\n * Create a TrackVADEmitter service that connects an audio track to an VAD (voice activity detection) processor in\n * order to obtain VAD scores for individual PCM audio samples.\n * @param {string} localAudioDeviceId - The target local audio device.\n * @param {number} sampleRate - Sample rate at which the emitter will operate. Possible values 256, 512, 1024,\n * 4096, 8192, 16384. Passing other values will default to closes neighbor.\n * I.e. Providing a value of 4096 means that the emitter will process 4096 PCM samples at a time, higher values mean\n * longer calls, lowers values mean more calls but shorter.\n * @param {Object} vadProcessor - VAD Processors that does the actual compute on a PCM sample.The processor needs\n * to implement the following functions:\n * - getSampleLength() - Returns the sample size accepted by calculateAudioFrameVAD.\n * - getRequiredPCMFrequency() - Returns the PCM frequency at which the processor operates.\n * i.e. (16KHz, 44.1 KHz etc.)\n * - calculateAudioFrameVAD(pcmSample) - Process a 32 float pcm sample of getSampleLength size.\n * @returns {Promise}\n */\n createTrackVADEmitter(localAudioDeviceId, sampleRate, vadProcessor) {\n return TrackVADEmitter.create(localAudioDeviceId, sampleRate, vadProcessor);\n },\n\n /**\n * Create AudioMixer, which is essentially a wrapper over web audio ChannelMergerNode. It essentially allows the\n * user to mix multiple MediaStreams into a single one.\n *\n * @returns {AudioMixer}\n */\n createAudioMixer() {\n return new AudioMixer();\n },\n\n /**\n * Go through all audio devices on the system and return one that is active, i.e. has audio signal.\n *\n * @returns Promise - Object containing information about the found device.\n */\n getActiveAudioDevice() {\n return getActiveAudioDevice();\n },\n\n /**\n * Checks if its possible to enumerate available cameras/microphones.\n *\n * @returns {Promise} a Promise which will be resolved only once\n * the WebRTC stack is ready, either with true if the device listing is\n * available available or with false otherwise.\n * @deprecated use JitsiMeetJS.mediaDevices.isDeviceListAvailable instead\n */\n isDeviceListAvailable() {\n logger.warn('This method is deprecated, use '\n + 'JitsiMeetJS.mediaDevices.isDeviceListAvailable instead');\n\n return this.mediaDevices.isDeviceListAvailable();\n },\n\n /**\n * Returns true if changing the input (camera / microphone) or output\n * (audio) device is supported and false if not.\n *\n * @param {string} [deviceType] - type of device to change. Default is\n * {@code undefined} or 'input', 'output' - for audio output device change.\n * @returns {boolean} {@code true} if available; {@code false}, otherwise.\n * @deprecated use JitsiMeetJS.mediaDevices.isDeviceChangeAvailable instead\n */\n isDeviceChangeAvailable(deviceType) {\n logger.warn('This method is deprecated, use '\n + 'JitsiMeetJS.mediaDevices.isDeviceChangeAvailable instead');\n\n return this.mediaDevices.isDeviceChangeAvailable(deviceType);\n },\n\n\n /**\n * Checks if the current environment supports having multiple audio\n * input devices in use simultaneously.\n *\n * @returns {boolean} True if multiple audio input devices can be used.\n */\n isMultipleAudioInputSupported() {\n return this.mediaDevices.isMultipleAudioInputSupported();\n },\n\n /**\n * Checks if local tracks can collect stats and collection is enabled.\n *\n * @param {boolean} True if stats are being collected for local tracks.\n */\n isCollectingLocalStats() {\n return Statistics.audioLevelsEnabled\n && LocalStatsCollector.isLocalStatsSupported();\n },\n\n /**\n * Executes callback with list of media devices connected.\n *\n * @param {function} callback\n * @deprecated use JitsiMeetJS.mediaDevices.enumerateDevices instead\n */\n enumerateDevices(callback) {\n logger.warn('This method is deprecated, use '\n + 'JitsiMeetJS.mediaDevices.enumerateDevices instead');\n this.mediaDevices.enumerateDevices(callback);\n },\n\n /* eslint-disable max-params */\n\n /**\n * @returns function that can be used to be attached to window.onerror and\n * if options.enableWindowOnErrorHandler is enabled returns\n * the function used by the lib.\n * (function(message, source, lineno, colno, error)).\n */\n getGlobalOnErrorHandler(message, source, lineno, colno, error) {\n logger.error(\n `UnhandledError: ${message}`,\n `Script: ${source}`,\n `Line: ${lineno}`,\n `Column: ${colno}`,\n 'StackTrace: ', error);\n Statistics.reportGlobalError(error);\n },\n\n /**\n * Informs lib-jitsi-meet about the current network status.\n *\n * @param {boolean} isOnline - {@code true} if the internet connectivity is online or {@code false}\n * otherwise.\n */\n setNetworkInfo({ isOnline }) {\n NetworkInfo.updateNetworkInfo({ isOnline });\n },\n\n /**\n * Set the contentHint on the transmitted stream track to indicate\n * charaterstics in the video stream, which informs PeerConnection\n * on how to encode the track (to prefer motion or individual frame detail)\n * @param {MediaStreamTrack} track - the track that is transmitted\n * @param {String} hint - contentHint value that needs to be set on the track\n */\n setVideoTrackContentHints(track, hint) {\n if ('contentHint' in track) {\n track.contentHint = hint;\n if (track.contentHint !== hint) {\n logger.debug('Invalid video track contentHint');\n }\n } else {\n logger.debug('MediaStreamTrack contentHint attribute not supported');\n }\n },\n\n precallTest,\n\n /* eslint-enable max-params */\n\n /**\n * Represents a hub/namespace for utility functionality which may be of\n * interest to lib-jitsi-meet clients.\n */\n util: {\n AuthUtil,\n ScriptUtil,\n browser\n }\n});\n","export default {\n error: {\n BUSY: 'busy',\n ERROR: 'error',\n RESOURCE_CONSTRAINT: 'resource-constraint',\n UNEXPECTED_REQUEST: 'unexpected-request',\n SERVICE_UNAVAILABLE: 'service-unavailable'\n },\n mode: {\n FILE: 'file',\n STREAM: 'stream'\n },\n status: {\n OFF: 'off',\n ON: 'on',\n PENDING: 'pending'\n }\n};\n","(function (global, factory) {\n typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :\n typeof define === 'function' && define.amd ? define(factory) :\n (global = global || self, global.strophe = factory());\n}(this, function () { 'use strict';\n\n var global$1 = (typeof global !== \"undefined\" ? global :\n typeof self !== \"undefined\" ? self :\n typeof window !== \"undefined\" ? window : {});\n\n function _typeof(obj) {\n if (typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\") {\n _typeof = function (obj) {\n return typeof obj;\n };\n } else {\n _typeof = function (obj) {\n return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj;\n };\n }\n\n return _typeof(obj);\n }\n\n function _toConsumableArray(arr) {\n return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread();\n }\n\n function _arrayWithoutHoles(arr) {\n if (Array.isArray(arr)) {\n for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) arr2[i] = arr[i];\n\n return arr2;\n }\n }\n\n function _iterableToArray(iter) {\n if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === \"[object Arguments]\") return Array.from(iter);\n }\n\n function _nonIterableSpread() {\n throw new TypeError(\"Invalid attempt to spread non-iterable instance\");\n }\n\n /*\n * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message\n * Digest Algorithm, as defined in RFC 1321.\n * Version 2.1 Copyright (C) Paul Johnston 1999 - 2002.\n * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet\n * Distributed under the BSD License\n * See http://pajhome.org.uk/crypt/md5 for more info.\n */\n\n /*\n * Everything that isn't used by Strophe has been stripped here!\n */\n\n /*\n * Add integers, wrapping at 2^32. This uses 16-bit operations internally\n * to work around bugs in some JS interpreters.\n */\n var safe_add = function safe_add(x, y) {\n var lsw = (x & 0xFFFF) + (y & 0xFFFF);\n var msw = (x >> 16) + (y >> 16) + (lsw >> 16);\n return msw << 16 | lsw & 0xFFFF;\n };\n /*\n * Bitwise rotate a 32-bit number to the left.\n */\n\n\n var bit_rol = function bit_rol(num, cnt) {\n return num << cnt | num >>> 32 - cnt;\n };\n /*\n * Convert a string to an array of little-endian words\n */\n\n\n var str2binl = function str2binl(str) {\n if (typeof str !== \"string\") {\n throw new Error(\"str2binl was passed a non-string\");\n }\n\n var bin = [];\n\n for (var i = 0; i < str.length * 8; i += 8) {\n bin[i >> 5] |= (str.charCodeAt(i / 8) & 255) << i % 32;\n }\n\n return bin;\n };\n /*\n * Convert an array of little-endian words to a string\n */\n\n\n var binl2str = function binl2str(bin) {\n var str = \"\";\n\n for (var i = 0; i < bin.length * 32; i += 8) {\n str += String.fromCharCode(bin[i >> 5] >>> i % 32 & 255);\n }\n\n return str;\n };\n /*\n * Convert an array of little-endian words to a hex string.\n */\n\n\n var binl2hex = function binl2hex(binarray) {\n var hex_tab = \"0123456789abcdef\";\n var str = \"\";\n\n for (var i = 0; i < binarray.length * 4; i++) {\n str += hex_tab.charAt(binarray[i >> 2] >> i % 4 * 8 + 4 & 0xF) + hex_tab.charAt(binarray[i >> 2] >> i % 4 * 8 & 0xF);\n }\n\n return str;\n };\n /*\n * These functions implement the four basic operations the algorithm uses.\n */\n\n\n var md5_cmn = function md5_cmn(q, a, b, x, s, t) {\n return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s), b);\n };\n\n var md5_ff = function md5_ff(a, b, c, d, x, s, t) {\n return md5_cmn(b & c | ~b & d, a, b, x, s, t);\n };\n\n var md5_gg = function md5_gg(a, b, c, d, x, s, t) {\n return md5_cmn(b & d | c & ~d, a, b, x, s, t);\n };\n\n var md5_hh = function md5_hh(a, b, c, d, x, s, t) {\n return md5_cmn(b ^ c ^ d, a, b, x, s, t);\n };\n\n var md5_ii = function md5_ii(a, b, c, d, x, s, t) {\n return md5_cmn(c ^ (b | ~d), a, b, x, s, t);\n };\n /*\n * Calculate the MD5 of an array of little-endian words, and a bit length\n */\n\n\n var core_md5 = function core_md5(x, len) {\n /* append padding */\n x[len >> 5] |= 0x80 << len % 32;\n x[(len + 64 >>> 9 << 4) + 14] = len;\n var a = 1732584193;\n var b = -271733879;\n var c = -1732584194;\n var d = 271733878;\n var olda, oldb, oldc, oldd;\n\n for (var i = 0; i < x.length; i += 16) {\n olda = a;\n oldb = b;\n oldc = c;\n oldd = d;\n a = md5_ff(a, b, c, d, x[i + 0], 7, -680876936);\n d = md5_ff(d, a, b, c, x[i + 1], 12, -389564586);\n c = md5_ff(c, d, a, b, x[i + 2], 17, 606105819);\n b = md5_ff(b, c, d, a, x[i + 3], 22, -1044525330);\n a = md5_ff(a, b, c, d, x[i + 4], 7, -176418897);\n d = md5_ff(d, a, b, c, x[i + 5], 12, 1200080426);\n c = md5_ff(c, d, a, b, x[i + 6], 17, -1473231341);\n b = md5_ff(b, c, d, a, x[i + 7], 22, -45705983);\n a = md5_ff(a, b, c, d, x[i + 8], 7, 1770035416);\n d = md5_ff(d, a, b, c, x[i + 9], 12, -1958414417);\n c = md5_ff(c, d, a, b, x[i + 10], 17, -42063);\n b = md5_ff(b, c, d, a, x[i + 11], 22, -1990404162);\n a = md5_ff(a, b, c, d, x[i + 12], 7, 1804603682);\n d = md5_ff(d, a, b, c, x[i + 13], 12, -40341101);\n c = md5_ff(c, d, a, b, x[i + 14], 17, -1502002290);\n b = md5_ff(b, c, d, a, x[i + 15], 22, 1236535329);\n a = md5_gg(a, b, c, d, x[i + 1], 5, -165796510);\n d = md5_gg(d, a, b, c, x[i + 6], 9, -1069501632);\n c = md5_gg(c, d, a, b, x[i + 11], 14, 643717713);\n b = md5_gg(b, c, d, a, x[i + 0], 20, -373897302);\n a = md5_gg(a, b, c, d, x[i + 5], 5, -701558691);\n d = md5_gg(d, a, b, c, x[i + 10], 9, 38016083);\n c = md5_gg(c, d, a, b, x[i + 15], 14, -660478335);\n b = md5_gg(b, c, d, a, x[i + 4], 20, -405537848);\n a = md5_gg(a, b, c, d, x[i + 9], 5, 568446438);\n d = md5_gg(d, a, b, c, x[i + 14], 9, -1019803690);\n c = md5_gg(c, d, a, b, x[i + 3], 14, -187363961);\n b = md5_gg(b, c, d, a, x[i + 8], 20, 1163531501);\n a = md5_gg(a, b, c, d, x[i + 13], 5, -1444681467);\n d = md5_gg(d, a, b, c, x[i + 2], 9, -51403784);\n c = md5_gg(c, d, a, b, x[i + 7], 14, 1735328473);\n b = md5_gg(b, c, d, a, x[i + 12], 20, -1926607734);\n a = md5_hh(a, b, c, d, x[i + 5], 4, -378558);\n d = md5_hh(d, a, b, c, x[i + 8], 11, -2022574463);\n c = md5_hh(c, d, a, b, x[i + 11], 16, 1839030562);\n b = md5_hh(b, c, d, a, x[i + 14], 23, -35309556);\n a = md5_hh(a, b, c, d, x[i + 1], 4, -1530992060);\n d = md5_hh(d, a, b, c, x[i + 4], 11, 1272893353);\n c = md5_hh(c, d, a, b, x[i + 7], 16, -155497632);\n b = md5_hh(b, c, d, a, x[i + 10], 23, -1094730640);\n a = md5_hh(a, b, c, d, x[i + 13], 4, 681279174);\n d = md5_hh(d, a, b, c, x[i + 0], 11, -358537222);\n c = md5_hh(c, d, a, b, x[i + 3], 16, -722521979);\n b = md5_hh(b, c, d, a, x[i + 6], 23, 76029189);\n a = md5_hh(a, b, c, d, x[i + 9], 4, -640364487);\n d = md5_hh(d, a, b, c, x[i + 12], 11, -421815835);\n c = md5_hh(c, d, a, b, x[i + 15], 16, 530742520);\n b = md5_hh(b, c, d, a, x[i + 2], 23, -995338651);\n a = md5_ii(a, b, c, d, x[i + 0], 6, -198630844);\n d = md5_ii(d, a, b, c, x[i + 7], 10, 1126891415);\n c = md5_ii(c, d, a, b, x[i + 14], 15, -1416354905);\n b = md5_ii(b, c, d, a, x[i + 5], 21, -57434055);\n a = md5_ii(a, b, c, d, x[i + 12], 6, 1700485571);\n d = md5_ii(d, a, b, c, x[i + 3], 10, -1894986606);\n c = md5_ii(c, d, a, b, x[i + 10], 15, -1051523);\n b = md5_ii(b, c, d, a, x[i + 1], 21, -2054922799);\n a = md5_ii(a, b, c, d, x[i + 8], 6, 1873313359);\n d = md5_ii(d, a, b, c, x[i + 15], 10, -30611744);\n c = md5_ii(c, d, a, b, x[i + 6], 15, -1560198380);\n b = md5_ii(b, c, d, a, x[i + 13], 21, 1309151649);\n a = md5_ii(a, b, c, d, x[i + 4], 6, -145523070);\n d = md5_ii(d, a, b, c, x[i + 11], 10, -1120210379);\n c = md5_ii(c, d, a, b, x[i + 2], 15, 718787259);\n b = md5_ii(b, c, d, a, x[i + 9], 21, -343485551);\n a = safe_add(a, olda);\n b = safe_add(b, oldb);\n c = safe_add(c, oldc);\n d = safe_add(d, oldd);\n }\n\n return [a, b, c, d];\n };\n /*\n * These are the functions you'll usually want to call.\n * They take string arguments and return either hex or base-64 encoded\n * strings.\n */\n\n\n var MD5 = {\n hexdigest: function hexdigest(s) {\n return binl2hex(core_md5(str2binl(s), s.length * 8));\n },\n hash: function hash(s) {\n return binl2str(core_md5(str2binl(s), s.length * 8));\n }\n };\n\n /*\n * A JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined\n * in FIPS PUB 180-1\n * Version 2.1a Copyright Paul Johnston 2000 - 2002.\n * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet\n * Distributed under the BSD License\n * See http://pajhome.org.uk/crypt/md5 for details.\n */\n\n /* global define */\n\n /* Some functions and variables have been stripped for use with Strophe */\n\n /*\n * Calculate the SHA-1 of an array of big-endian words, and a bit length\n */\n function core_sha1(x, len) {\n /* append padding */\n x[len >> 5] |= 0x80 << 24 - len % 32;\n x[(len + 64 >> 9 << 4) + 15] = len;\n var w = new Array(80);\n var a = 1732584193;\n var b = -271733879;\n var c = -1732584194;\n var d = 271733878;\n var e = -1009589776;\n var i, j, t, olda, oldb, oldc, oldd, olde;\n\n for (i = 0; i < x.length; i += 16) {\n olda = a;\n oldb = b;\n oldc = c;\n oldd = d;\n olde = e;\n\n for (j = 0; j < 80; j++) {\n if (j < 16) {\n w[j] = x[i + j];\n } else {\n w[j] = rol(w[j - 3] ^ w[j - 8] ^ w[j - 14] ^ w[j - 16], 1);\n }\n\n t = safe_add$1(safe_add$1(rol(a, 5), sha1_ft(j, b, c, d)), safe_add$1(safe_add$1(e, w[j]), sha1_kt(j)));\n e = d;\n d = c;\n c = rol(b, 30);\n b = a;\n a = t;\n }\n\n a = safe_add$1(a, olda);\n b = safe_add$1(b, oldb);\n c = safe_add$1(c, oldc);\n d = safe_add$1(d, oldd);\n e = safe_add$1(e, olde);\n }\n\n return [a, b, c, d, e];\n }\n /*\n * Perform the appropriate triplet combination function for the current\n * iteration\n */\n\n\n function sha1_ft(t, b, c, d) {\n if (t < 20) {\n return b & c | ~b & d;\n }\n\n if (t < 40) {\n return b ^ c ^ d;\n }\n\n if (t < 60) {\n return b & c | b & d | c & d;\n }\n\n return b ^ c ^ d;\n }\n /*\n * Determine the appropriate additive constant for the current iteration\n */\n\n\n function sha1_kt(t) {\n return t < 20 ? 1518500249 : t < 40 ? 1859775393 : t < 60 ? -1894007588 : -899497514;\n }\n /*\n * Calculate the HMAC-SHA1 of a key and some data\n */\n\n\n function core_hmac_sha1(key, data) {\n var bkey = str2binb(key);\n\n if (bkey.length > 16) {\n bkey = core_sha1(bkey, key.length * 8);\n }\n\n var ipad = new Array(16),\n opad = new Array(16);\n\n for (var i = 0; i < 16; i++) {\n ipad[i] = bkey[i] ^ 0x36363636;\n opad[i] = bkey[i] ^ 0x5C5C5C5C;\n }\n\n var hash = core_sha1(ipad.concat(str2binb(data)), 512 + data.length * 8);\n return core_sha1(opad.concat(hash), 512 + 160);\n }\n /*\n * Add integers, wrapping at 2^32. This uses 16-bit operations internally\n * to work around bugs in some JS interpreters.\n */\n\n\n function safe_add$1(x, y) {\n var lsw = (x & 0xFFFF) + (y & 0xFFFF);\n var msw = (x >> 16) + (y >> 16) + (lsw >> 16);\n return msw << 16 | lsw & 0xFFFF;\n }\n /*\n * Bitwise rotate a 32-bit number to the left.\n */\n\n\n function rol(num, cnt) {\n return num << cnt | num >>> 32 - cnt;\n }\n /*\n * Convert an 8-bit or 16-bit string to an array of big-endian words\n * In 8-bit function, characters >255 have their hi-byte silently ignored.\n */\n\n\n function str2binb(str) {\n var bin = [];\n var mask = 255;\n\n for (var i = 0; i < str.length * 8; i += 8) {\n bin[i >> 5] |= (str.charCodeAt(i / 8) & mask) << 24 - i % 32;\n }\n\n return bin;\n }\n /*\n * Convert an array of big-endian words to a base-64 string\n */\n\n\n function binb2b64(binarray) {\n var tab = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\";\n var str = \"\";\n var triplet, j;\n\n for (var i = 0; i < binarray.length * 4; i += 3) {\n triplet = (binarray[i >> 2] >> 8 * (3 - i % 4) & 0xFF) << 16 | (binarray[i + 1 >> 2] >> 8 * (3 - (i + 1) % 4) & 0xFF) << 8 | binarray[i + 2 >> 2] >> 8 * (3 - (i + 2) % 4) & 0xFF;\n\n for (j = 0; j < 4; j++) {\n if (i * 8 + j * 6 > binarray.length * 32) {\n str += \"=\";\n } else {\n str += tab.charAt(triplet >> 6 * (3 - j) & 0x3F);\n }\n }\n }\n\n return str;\n }\n /*\n * Convert an array of big-endian words to a string\n */\n\n\n function binb2str(bin) {\n var str = \"\";\n var mask = 255;\n\n for (var i = 0; i < bin.length * 32; i += 8) {\n str += String.fromCharCode(bin[i >> 5] >>> 24 - i % 32 & mask);\n }\n\n return str;\n }\n /*\n * These are the functions you'll usually want to call\n * They take string arguments and return either hex or base-64 encoded strings\n */\n\n\n var SHA1 = {\n b64_hmac_sha1: function b64_hmac_sha1(key, data) {\n return binb2b64(core_hmac_sha1(key, data));\n },\n b64_sha1: function b64_sha1(s) {\n return binb2b64(core_sha1(str2binb(s), s.length * 8));\n },\n binb2str: binb2str,\n core_hmac_sha1: core_hmac_sha1,\n str_hmac_sha1: function str_hmac_sha1(key, data) {\n return binb2str(core_hmac_sha1(key, data));\n },\n str_sha1: function str_sha1(s) {\n return binb2str(core_sha1(str2binb(s), s.length * 8));\n }\n };\n\n var utils = {\n utf16to8: function utf16to8(str) {\n var i, c;\n var out = \"\";\n var len = str.length;\n\n for (i = 0; i < len; i++) {\n c = str.charCodeAt(i);\n\n if (c >= 0x0000 && c <= 0x007F) {\n out += str.charAt(i);\n } else if (c > 0x07FF) {\n out += String.fromCharCode(0xE0 | c >> 12 & 0x0F);\n out += String.fromCharCode(0x80 | c >> 6 & 0x3F);\n out += String.fromCharCode(0x80 | c >> 0 & 0x3F);\n } else {\n out += String.fromCharCode(0xC0 | c >> 6 & 0x1F);\n out += String.fromCharCode(0x80 | c >> 0 & 0x3F);\n }\n }\n\n return out;\n },\n addCookies: function addCookies(cookies) {\n /* Parameters:\n * (Object) cookies - either a map of cookie names\n * to string values or to maps of cookie values.\n *\n * For example:\n * { \"myCookie\": \"1234\" }\n *\n * or:\n * { \"myCookie\": {\n * \"value\": \"1234\",\n * \"domain\": \".example.org\",\n * \"path\": \"/\",\n * \"expires\": expirationDate\n * }\n * }\n *\n * These values get passed to Strophe.Connection via\n * options.cookies\n */\n cookies = cookies || {};\n\n for (var cookieName in cookies) {\n if (Object.prototype.hasOwnProperty.call(cookies, cookieName)) {\n var expires = '';\n var domain = '';\n var path = '';\n var cookieObj = cookies[cookieName];\n var isObj = _typeof(cookieObj) === \"object\";\n var cookieValue = escape(unescape(isObj ? cookieObj.value : cookieObj));\n\n if (isObj) {\n expires = cookieObj.expires ? \";expires=\" + cookieObj.expires : '';\n domain = cookieObj.domain ? \";domain=\" + cookieObj.domain : '';\n path = cookieObj.path ? \";path=\" + cookieObj.path : '';\n }\n\n document.cookie = cookieName + '=' + cookieValue + expires + domain + path;\n }\n }\n }\n };\n\n /** Function: $build\n * Create a Strophe.Builder.\n * This is an alias for 'new Strophe.Builder(name, attrs)'.\n *\n * Parameters:\n * (String) name - The root element name.\n * (Object) attrs - The attributes for the root element in object notation.\n *\n * Returns:\n * A new Strophe.Builder object.\n */\n\n function $build(name, attrs) {\n return new Strophe.Builder(name, attrs);\n }\n /** Function: $msg\n * Create a Strophe.Builder with a element as the root.\n *\n * Parameters:\n * (Object) attrs - The element attributes in object notation.\n *\n * Returns:\n * A new Strophe.Builder object.\n */\n\n\n function $msg(attrs) {\n return new Strophe.Builder(\"message\", attrs);\n }\n /** Function: $iq\n * Create a Strophe.Builder with an element as the root.\n *\n * Parameters:\n * (Object) attrs - The element attributes in object notation.\n *\n * Returns:\n * A new Strophe.Builder object.\n */\n\n\n function $iq(attrs) {\n return new Strophe.Builder(\"iq\", attrs);\n }\n /** Function: $pres\n * Create a Strophe.Builder with a element as the root.\n *\n * Parameters:\n * (Object) attrs - The element attributes in object notation.\n *\n * Returns:\n * A new Strophe.Builder object.\n */\n\n\n function $pres(attrs) {\n return new Strophe.Builder(\"presence\", attrs);\n }\n /** Class: Strophe\n * An object container for all Strophe library functions.\n *\n * This class is just a container for all the objects and constants\n * used in the library. It is not meant to be instantiated, but to\n * provide a namespace for library objects, constants, and functions.\n */\n\n\n var Strophe = {\n /** Constant: VERSION */\n VERSION: \"@VERSION@\",\n\n /** Constants: XMPP Namespace Constants\n * Common namespace constants from the XMPP RFCs and XEPs.\n *\n * NS.HTTPBIND - HTTP BIND namespace from XEP 124.\n * NS.BOSH - BOSH namespace from XEP 206.\n * NS.CLIENT - Main XMPP client namespace.\n * NS.AUTH - Legacy authentication namespace.\n * NS.ROSTER - Roster operations namespace.\n * NS.PROFILE - Profile namespace.\n * NS.DISCO_INFO - Service discovery info namespace from XEP 30.\n * NS.DISCO_ITEMS - Service discovery items namespace from XEP 30.\n * NS.MUC - Multi-User Chat namespace from XEP 45.\n * NS.SASL - XMPP SASL namespace from RFC 3920.\n * NS.STREAM - XMPP Streams namespace from RFC 3920.\n * NS.BIND - XMPP Binding namespace from RFC 3920 and RFC 6120.\n * NS.SESSION - XMPP Session namespace from RFC 3920.\n * NS.XHTML_IM - XHTML-IM namespace from XEP 71.\n * NS.XHTML - XHTML body namespace from XEP 71.\n */\n NS: {\n HTTPBIND: \"http://jabber.org/protocol/httpbind\",\n BOSH: \"urn:xmpp:xbosh\",\n CLIENT: \"jabber:client\",\n AUTH: \"jabber:iq:auth\",\n ROSTER: \"jabber:iq:roster\",\n PROFILE: \"jabber:iq:profile\",\n DISCO_INFO: \"http://jabber.org/protocol/disco#info\",\n DISCO_ITEMS: \"http://jabber.org/protocol/disco#items\",\n MUC: \"http://jabber.org/protocol/muc\",\n SASL: \"urn:ietf:params:xml:ns:xmpp-sasl\",\n STREAM: \"http://etherx.jabber.org/streams\",\n FRAMING: \"urn:ietf:params:xml:ns:xmpp-framing\",\n BIND: \"urn:ietf:params:xml:ns:xmpp-bind\",\n SESSION: \"urn:ietf:params:xml:ns:xmpp-session\",\n VERSION: \"jabber:iq:version\",\n STANZAS: \"urn:ietf:params:xml:ns:xmpp-stanzas\",\n XHTML_IM: \"http://jabber.org/protocol/xhtml-im\",\n XHTML: \"http://www.w3.org/1999/xhtml\"\n },\n\n /** Constants: XHTML_IM Namespace\n * contains allowed tags, tag attributes, and css properties.\n * Used in the createHtml function to filter incoming html into the allowed XHTML-IM subset.\n * See http://xmpp.org/extensions/xep-0071.html#profile-summary for the list of recommended\n * allowed tags and their attributes.\n */\n XHTML: {\n tags: ['a', 'blockquote', 'br', 'cite', 'em', 'img', 'li', 'ol', 'p', 'span', 'strong', 'ul', 'body'],\n attributes: {\n 'a': ['href'],\n 'blockquote': ['style'],\n 'br': [],\n 'cite': ['style'],\n 'em': [],\n 'img': ['src', 'alt', 'style', 'height', 'width'],\n 'li': ['style'],\n 'ol': ['style'],\n 'p': ['style'],\n 'span': ['style'],\n 'strong': [],\n 'ul': ['style'],\n 'body': []\n },\n css: ['background-color', 'color', 'font-family', 'font-size', 'font-style', 'font-weight', 'margin-left', 'margin-right', 'text-align', 'text-decoration'],\n\n /** Function: XHTML.validTag\n *\n * Utility method to determine whether a tag is allowed\n * in the XHTML_IM namespace.\n *\n * XHTML tag names are case sensitive and must be lower case.\n */\n validTag: function validTag(tag) {\n for (var i = 0; i < Strophe.XHTML.tags.length; i++) {\n if (tag === Strophe.XHTML.tags[i]) {\n return true;\n }\n }\n\n return false;\n },\n\n /** Function: XHTML.validAttribute\n *\n * Utility method to determine whether an attribute is allowed\n * as recommended per XEP-0071\n *\n * XHTML attribute names are case sensitive and must be lower case.\n */\n validAttribute: function validAttribute(tag, attribute) {\n if (typeof Strophe.XHTML.attributes[tag] !== 'undefined' && Strophe.XHTML.attributes[tag].length > 0) {\n for (var i = 0; i < Strophe.XHTML.attributes[tag].length; i++) {\n if (attribute === Strophe.XHTML.attributes[tag][i]) {\n return true;\n }\n }\n }\n\n return false;\n },\n validCSS: function validCSS(style) {\n for (var i = 0; i < Strophe.XHTML.css.length; i++) {\n if (style === Strophe.XHTML.css[i]) {\n return true;\n }\n }\n\n return false;\n }\n },\n\n /** Constants: Connection Status Constants\n * Connection status constants for use by the connection handler\n * callback.\n *\n * Status.ERROR - An error has occurred\n * Status.CONNECTING - The connection is currently being made\n * Status.CONNFAIL - The connection attempt failed\n * Status.AUTHENTICATING - The connection is authenticating\n * Status.AUTHFAIL - The authentication attempt failed\n * Status.CONNECTED - The connection has succeeded\n * Status.DISCONNECTED - The connection has been terminated\n * Status.DISCONNECTING - The connection is currently being terminated\n * Status.ATTACHED - The connection has been attached\n * Status.REDIRECT - The connection has been redirected\n * Status.CONNTIMEOUT - The connection has timed out\n */\n Status: {\n ERROR: 0,\n CONNECTING: 1,\n CONNFAIL: 2,\n AUTHENTICATING: 3,\n AUTHFAIL: 4,\n CONNECTED: 5,\n DISCONNECTED: 6,\n DISCONNECTING: 7,\n ATTACHED: 8,\n REDIRECT: 9,\n CONNTIMEOUT: 10,\n BINDREQUIRED: 11\n },\n ErrorCondition: {\n BAD_FORMAT: \"bad-format\",\n CONFLICT: \"conflict\",\n MISSING_JID_NODE: \"x-strophe-bad-non-anon-jid\",\n NO_AUTH_MECH: \"no-auth-mech\",\n UNKNOWN_REASON: \"unknown\"\n },\n\n /** Constants: Log Level Constants\n * Logging level indicators.\n *\n * LogLevel.DEBUG - Debug output\n * LogLevel.INFO - Informational output\n * LogLevel.WARN - Warnings\n * LogLevel.ERROR - Errors\n * LogLevel.FATAL - Fatal errors\n */\n LogLevel: {\n DEBUG: 0,\n INFO: 1,\n WARN: 2,\n ERROR: 3,\n FATAL: 4\n },\n\n /** PrivateConstants: DOM Element Type Constants\n * DOM element types.\n *\n * ElementType.NORMAL - Normal element.\n * ElementType.TEXT - Text data element.\n * ElementType.FRAGMENT - XHTML fragment element.\n */\n ElementType: {\n NORMAL: 1,\n TEXT: 3,\n CDATA: 4,\n FRAGMENT: 11\n },\n\n /** PrivateConstants: Timeout Values\n * Timeout values for error states. These values are in seconds.\n * These should not be changed unless you know exactly what you are\n * doing.\n *\n * TIMEOUT - Timeout multiplier. A waiting request will be considered\n * failed after Math.floor(TIMEOUT * wait) seconds have elapsed.\n * This defaults to 1.1, and with default wait, 66 seconds.\n * SECONDARY_TIMEOUT - Secondary timeout multiplier. In cases where\n * Strophe can detect early failure, it will consider the request\n * failed if it doesn't return after\n * Math.floor(SECONDARY_TIMEOUT * wait) seconds have elapsed.\n * This defaults to 0.1, and with default wait, 6 seconds.\n */\n TIMEOUT: 1.1,\n SECONDARY_TIMEOUT: 0.1,\n\n /** Function: addNamespace\n * This function is used to extend the current namespaces in\n * Strophe.NS. It takes a key and a value with the key being the\n * name of the new namespace, with its actual value.\n * For example:\n * Strophe.addNamespace('PUBSUB', \"http://jabber.org/protocol/pubsub\");\n *\n * Parameters:\n * (String) name - The name under which the namespace will be\n * referenced under Strophe.NS\n * (String) value - The actual namespace.\n */\n addNamespace: function addNamespace(name, value) {\n Strophe.NS[name] = value;\n },\n\n /** Function: forEachChild\n * Map a function over some or all child elements of a given element.\n *\n * This is a small convenience function for mapping a function over\n * some or all of the children of an element. If elemName is null, all\n * children will be passed to the function, otherwise only children\n * whose tag names match elemName will be passed.\n *\n * Parameters:\n * (XMLElement) elem - The element to operate on.\n * (String) elemName - The child element tag name filter.\n * (Function) func - The function to apply to each child. This\n * function should take a single argument, a DOM element.\n */\n forEachChild: function forEachChild(elem, elemName, func) {\n for (var i = 0; i < elem.childNodes.length; i++) {\n var childNode = elem.childNodes[i];\n\n if (childNode.nodeType === Strophe.ElementType.NORMAL && (!elemName || this.isTagEqual(childNode, elemName))) {\n func(childNode);\n }\n }\n },\n\n /** Function: isTagEqual\n * Compare an element's tag name with a string.\n *\n * This function is case sensitive.\n *\n * Parameters:\n * (XMLElement) el - A DOM element.\n * (String) name - The element name.\n *\n * Returns:\n * true if the element's tag name matches _el_, and false\n * otherwise.\n */\n isTagEqual: function isTagEqual(el, name) {\n return el.tagName === name;\n },\n\n /** PrivateVariable: _xmlGenerator\n * _Private_ variable that caches a DOM document to\n * generate elements.\n */\n _xmlGenerator: null,\n\n /** PrivateFunction: _makeGenerator\n * _Private_ function that creates a dummy XML DOM document to serve as\n * an element and text node generator.\n */\n _makeGenerator: function _makeGenerator() {\n var doc; // IE9 does implement createDocument(); however, using it will cause the browser to leak memory on page unload.\n // Here, we test for presence of createDocument() plus IE's proprietary documentMode attribute, which would be\n // less than 10 in the case of IE9 and below.\n\n if (document.implementation.createDocument === undefined || document.implementation.createDocument && document.documentMode && document.documentMode < 10) {\n doc = this._getIEXmlDom();\n doc.appendChild(doc.createElement('strophe'));\n } else {\n doc = document.implementation.createDocument('jabber:client', 'strophe', null);\n }\n\n return doc;\n },\n\n /** Function: xmlGenerator\n * Get the DOM document to generate elements.\n *\n * Returns:\n * The currently used DOM document.\n */\n xmlGenerator: function xmlGenerator() {\n if (!Strophe._xmlGenerator) {\n Strophe._xmlGenerator = Strophe._makeGenerator();\n }\n\n return Strophe._xmlGenerator;\n },\n\n /** PrivateFunction: _getIEXmlDom\n * Gets IE xml doc object\n *\n * Returns:\n * A Microsoft XML DOM Object\n * See Also:\n * http://msdn.microsoft.com/en-us/library/ms757837%28VS.85%29.aspx\n */\n _getIEXmlDom: function _getIEXmlDom() {\n var doc = null;\n var docStrings = [\"Msxml2.DOMDocument.6.0\", \"Msxml2.DOMDocument.5.0\", \"Msxml2.DOMDocument.4.0\", \"MSXML2.DOMDocument.3.0\", \"MSXML2.DOMDocument\", \"MSXML.DOMDocument\", \"Microsoft.XMLDOM\"];\n\n for (var d = 0; d < docStrings.length; d++) {\n if (doc === null) {\n try {\n doc = new ActiveXObject(docStrings[d]);\n } catch (e) {\n doc = null;\n }\n } else {\n break;\n }\n }\n\n return doc;\n },\n\n /** Function: xmlElement\n * Create an XML DOM element.\n *\n * This function creates an XML DOM element correctly across all\n * implementations. Note that these are not HTML DOM elements, which\n * aren't appropriate for XMPP stanzas.\n *\n * Parameters:\n * (String) name - The name for the element.\n * (Array|Object) attrs - An optional array or object containing\n * key/value pairs to use as element attributes. The object should\n * be in the format {'key': 'value'} or {key: 'value'}. The array\n * should have the format [['key1', 'value1'], ['key2', 'value2']].\n * (String) text - The text child data for the element.\n *\n * Returns:\n * A new XML DOM element.\n */\n xmlElement: function xmlElement(name) {\n if (!name) {\n return null;\n }\n\n var node = Strophe.xmlGenerator().createElement(name); // FIXME: this should throw errors if args are the wrong type or\n // there are more than two optional args\n\n for (var a = 1; a < arguments.length; a++) {\n var arg = arguments[a];\n\n if (!arg) {\n continue;\n }\n\n if (typeof arg === \"string\" || typeof arg === \"number\") {\n node.appendChild(Strophe.xmlTextNode(arg));\n } else if (_typeof(arg) === \"object\" && typeof arg.sort === \"function\") {\n for (var i = 0; i < arg.length; i++) {\n var attr = arg[i];\n\n if (_typeof(attr) === \"object\" && typeof attr.sort === \"function\" && attr[1] !== undefined && attr[1] !== null) {\n node.setAttribute(attr[0], attr[1]);\n }\n }\n } else if (_typeof(arg) === \"object\") {\n for (var k in arg) {\n if (Object.prototype.hasOwnProperty.call(arg, k) && arg[k] !== undefined && arg[k] !== null) {\n node.setAttribute(k, arg[k]);\n }\n }\n }\n }\n\n return node;\n },\n\n /* Function: xmlescape\n * Excapes invalid xml characters.\n *\n * Parameters:\n * (String) text - text to escape.\n *\n * Returns:\n * Escaped text.\n */\n xmlescape: function xmlescape(text) {\n text = text.replace(/\\&/g, \"&\");\n text = text.replace(//g, \">\");\n text = text.replace(/'/g, \"'\");\n text = text.replace(/\"/g, \""\");\n return text;\n },\n\n /* Function: xmlunescape\n * Unexcapes invalid xml characters.\n *\n * Parameters:\n * (String) text - text to unescape.\n *\n * Returns:\n * Unescaped text.\n */\n xmlunescape: function xmlunescape(text) {\n text = text.replace(/\\&/g, \"&\");\n text = text.replace(/</g, \"<\");\n text = text.replace(/>/g, \">\");\n text = text.replace(/'/g, \"'\");\n text = text.replace(/"/g, \"\\\"\");\n return text;\n },\n\n /** Function: xmlTextNode\n * Creates an XML DOM text node.\n *\n * Provides a cross implementation version of document.createTextNode.\n *\n * Parameters:\n * (String) text - The content of the text node.\n *\n * Returns:\n * A new XML DOM text node.\n */\n xmlTextNode: function xmlTextNode(text) {\n return Strophe.xmlGenerator().createTextNode(text);\n },\n\n /** Function: xmlHtmlNode\n * Creates an XML DOM html node.\n *\n * Parameters:\n * (String) html - The content of the html node.\n *\n * Returns:\n * A new XML DOM text node.\n */\n xmlHtmlNode: function xmlHtmlNode(html) {\n var node; //ensure text is escaped\n\n if (DOMParser) {\n var parser = new DOMParser();\n node = parser.parseFromString(html, \"text/xml\");\n } else {\n node = new ActiveXObject(\"Microsoft.XMLDOM\");\n node.async = \"false\";\n node.loadXML(html);\n }\n\n return node;\n },\n\n /** Function: getText\n * Get the concatenation of all text children of an element.\n *\n * Parameters:\n * (XMLElement) elem - A DOM element.\n *\n * Returns:\n * A String with the concatenated text of all text element children.\n */\n getText: function getText(elem) {\n if (!elem) {\n return null;\n }\n\n var str = \"\";\n\n if (elem.childNodes.length === 0 && elem.nodeType === Strophe.ElementType.TEXT) {\n str += elem.nodeValue;\n }\n\n for (var i = 0; i < elem.childNodes.length; i++) {\n if (elem.childNodes[i].nodeType === Strophe.ElementType.TEXT) {\n str += elem.childNodes[i].nodeValue;\n }\n }\n\n return Strophe.xmlescape(str);\n },\n\n /** Function: copyElement\n * Copy an XML DOM element.\n *\n * This function copies a DOM element and all its descendants and returns\n * the new copy.\n *\n * Parameters:\n * (XMLElement) elem - A DOM element.\n *\n * Returns:\n * A new, copied DOM element tree.\n */\n copyElement: function copyElement(elem) {\n var el;\n\n if (elem.nodeType === Strophe.ElementType.NORMAL) {\n el = Strophe.xmlElement(elem.tagName);\n\n for (var i = 0; i < elem.attributes.length; i++) {\n el.setAttribute(elem.attributes[i].nodeName, elem.attributes[i].value);\n }\n\n for (var _i = 0; _i < elem.childNodes.length; _i++) {\n el.appendChild(Strophe.copyElement(elem.childNodes[_i]));\n }\n } else if (elem.nodeType === Strophe.ElementType.TEXT) {\n el = Strophe.xmlGenerator().createTextNode(elem.nodeValue);\n }\n\n return el;\n },\n\n /** Function: createHtml\n * Copy an HTML DOM element into an XML DOM.\n *\n * This function copies a DOM element and all its descendants and returns\n * the new copy.\n *\n * Parameters:\n * (HTMLElement) elem - A DOM element.\n *\n * Returns:\n * A new, copied DOM element tree.\n */\n createHtml: function createHtml(elem) {\n var el;\n\n if (elem.nodeType === Strophe.ElementType.NORMAL) {\n var tag = elem.nodeName.toLowerCase(); // XHTML tags must be lower case.\n\n if (Strophe.XHTML.validTag(tag)) {\n try {\n el = Strophe.xmlElement(tag);\n\n for (var i = 0; i < Strophe.XHTML.attributes[tag].length; i++) {\n var attribute = Strophe.XHTML.attributes[tag][i];\n var value = elem.getAttribute(attribute);\n\n if (typeof value === 'undefined' || value === null || value === '' || value === false || value === 0) {\n continue;\n }\n\n if (attribute === 'style' && _typeof(value) === 'object' && typeof value.cssText !== 'undefined') {\n value = value.cssText; // we're dealing with IE, need to get CSS out\n } // filter out invalid css styles\n\n\n if (attribute === 'style') {\n var css = [];\n var cssAttrs = value.split(';');\n\n for (var j = 0; j < cssAttrs.length; j++) {\n var attr = cssAttrs[j].split(':');\n var cssName = attr[0].replace(/^\\s*/, \"\").replace(/\\s*$/, \"\").toLowerCase();\n\n if (Strophe.XHTML.validCSS(cssName)) {\n var cssValue = attr[1].replace(/^\\s*/, \"\").replace(/\\s*$/, \"\");\n css.push(cssName + ': ' + cssValue);\n }\n }\n\n if (css.length > 0) {\n value = css.join('; ');\n el.setAttribute(attribute, value);\n }\n } else {\n el.setAttribute(attribute, value);\n }\n }\n\n for (var _i2 = 0; _i2 < elem.childNodes.length; _i2++) {\n el.appendChild(Strophe.createHtml(elem.childNodes[_i2]));\n }\n } catch (e) {\n // invalid elements\n el = Strophe.xmlTextNode('');\n }\n } else {\n el = Strophe.xmlGenerator().createDocumentFragment();\n\n for (var _i3 = 0; _i3 < elem.childNodes.length; _i3++) {\n el.appendChild(Strophe.createHtml(elem.childNodes[_i3]));\n }\n }\n } else if (elem.nodeType === Strophe.ElementType.FRAGMENT) {\n el = Strophe.xmlGenerator().createDocumentFragment();\n\n for (var _i4 = 0; _i4 < elem.childNodes.length; _i4++) {\n el.appendChild(Strophe.createHtml(elem.childNodes[_i4]));\n }\n } else if (elem.nodeType === Strophe.ElementType.TEXT) {\n el = Strophe.xmlTextNode(elem.nodeValue);\n }\n\n return el;\n },\n\n /** Function: escapeNode\n * Escape the node part (also called local part) of a JID.\n *\n * Parameters:\n * (String) node - A node (or local part).\n *\n * Returns:\n * An escaped node (or local part).\n */\n escapeNode: function escapeNode(node) {\n if (typeof node !== \"string\") {\n return node;\n }\n\n return node.replace(/^\\s+|\\s+$/g, '').replace(/\\\\/g, \"\\\\5c\").replace(/ /g, \"\\\\20\").replace(/\\\"/g, \"\\\\22\").replace(/\\&/g, \"\\\\26\").replace(/\\'/g, \"\\\\27\").replace(/\\//g, \"\\\\2f\").replace(/:/g, \"\\\\3a\").replace(//g, \"\\\\3e\").replace(/@/g, \"\\\\40\");\n },\n\n /** Function: unescapeNode\n * Unescape a node part (also called local part) of a JID.\n *\n * Parameters:\n * (String) node - A node (or local part).\n *\n * Returns:\n * An unescaped node (or local part).\n */\n unescapeNode: function unescapeNode(node) {\n if (typeof node !== \"string\") {\n return node;\n }\n\n return node.replace(/\\\\20/g, \" \").replace(/\\\\22/g, '\"').replace(/\\\\26/g, \"&\").replace(/\\\\27/g, \"'\").replace(/\\\\2f/g, \"/\").replace(/\\\\3a/g, \":\").replace(/\\\\3c/g, \"<\").replace(/\\\\3e/g, \">\").replace(/\\\\40/g, \"@\").replace(/\\\\5c/g, \"\\\\\");\n },\n\n /** Function: getNodeFromJid\n * Get the node portion of a JID String.\n *\n * Parameters:\n * (String) jid - A JID.\n *\n * Returns:\n * A String containing the node.\n */\n getNodeFromJid: function getNodeFromJid(jid) {\n if (jid.indexOf(\"@\") < 0) {\n return null;\n }\n\n return jid.split(\"@\")[0];\n },\n\n /** Function: getDomainFromJid\n * Get the domain portion of a JID String.\n *\n * Parameters:\n * (String) jid - A JID.\n *\n * Returns:\n * A String containing the domain.\n */\n getDomainFromJid: function getDomainFromJid(jid) {\n var bare = Strophe.getBareJidFromJid(jid);\n\n if (bare.indexOf(\"@\") < 0) {\n return bare;\n } else {\n var parts = bare.split(\"@\");\n parts.splice(0, 1);\n return parts.join('@');\n }\n },\n\n /** Function: getResourceFromJid\n * Get the resource portion of a JID String.\n *\n * Parameters:\n * (String) jid - A JID.\n *\n * Returns:\n * A String containing the resource.\n */\n getResourceFromJid: function getResourceFromJid(jid) {\n if (!jid) {\n return null;\n }\n\n var s = jid.split(\"/\");\n\n if (s.length < 2) {\n return null;\n }\n\n s.splice(0, 1);\n return s.join('/');\n },\n\n /** Function: getBareJidFromJid\n * Get the bare JID from a JID String.\n *\n * Parameters:\n * (String) jid - A JID.\n *\n * Returns:\n * A String containing the bare JID.\n */\n getBareJidFromJid: function getBareJidFromJid(jid) {\n return jid ? jid.split(\"/\")[0] : null;\n },\n\n /** PrivateFunction: _handleError\n * _Private_ function that properly logs an error to the console\n */\n _handleError: function _handleError(e) {\n if (typeof e.stack !== \"undefined\") {\n Strophe.fatal(e.stack);\n }\n\n if (e.sourceURL) {\n Strophe.fatal(\"error: \" + this.handler + \" \" + e.sourceURL + \":\" + e.line + \" - \" + e.name + \": \" + e.message);\n } else if (e.fileName) {\n Strophe.fatal(\"error: \" + this.handler + \" \" + e.fileName + \":\" + e.lineNumber + \" - \" + e.name + \": \" + e.message);\n } else {\n Strophe.fatal(\"error: \" + e.message);\n }\n },\n\n /** Function: log\n * User overrideable logging function.\n *\n * This function is called whenever the Strophe library calls any\n * of the logging functions. The default implementation of this\n * function logs only fatal errors. If client code wishes to handle the logging\n * messages, it should override this with\n * > Strophe.log = function (level, msg) {\n * > (user code here)\n * > };\n *\n * Please note that data sent and received over the wire is logged\n * via Strophe.Connection.rawInput() and Strophe.Connection.rawOutput().\n *\n * The different levels and their meanings are\n *\n * DEBUG - Messages useful for debugging purposes.\n * INFO - Informational messages. This is mostly information like\n * 'disconnect was called' or 'SASL auth succeeded'.\n * WARN - Warnings about potential problems. This is mostly used\n * to report transient connection errors like request timeouts.\n * ERROR - Some error occurred.\n * FATAL - A non-recoverable fatal error occurred.\n *\n * Parameters:\n * (Integer) level - The log level of the log message. This will\n * be one of the values in Strophe.LogLevel.\n * (String) msg - The log message.\n */\n log: function log(level, msg) {\n if (level === this.LogLevel.FATAL && _typeof(window.console) === 'object' && typeof window.console.error === 'function') {\n window.console.error(msg);\n }\n },\n\n /** Function: debug\n * Log a message at the Strophe.LogLevel.DEBUG level.\n *\n * Parameters:\n * (String) msg - The log message.\n */\n debug: function debug(msg) {\n this.log(this.LogLevel.DEBUG, msg);\n },\n\n /** Function: info\n * Log a message at the Strophe.LogLevel.INFO level.\n *\n * Parameters:\n * (String) msg - The log message.\n */\n info: function info(msg) {\n this.log(this.LogLevel.INFO, msg);\n },\n\n /** Function: warn\n * Log a message at the Strophe.LogLevel.WARN level.\n *\n * Parameters:\n * (String) msg - The log message.\n */\n warn: function warn(msg) {\n this.log(this.LogLevel.WARN, msg);\n },\n\n /** Function: error\n * Log a message at the Strophe.LogLevel.ERROR level.\n *\n * Parameters:\n * (String) msg - The log message.\n */\n error: function error(msg) {\n this.log(this.LogLevel.ERROR, msg);\n },\n\n /** Function: fatal\n * Log a message at the Strophe.LogLevel.FATAL level.\n *\n * Parameters:\n * (String) msg - The log message.\n */\n fatal: function fatal(msg) {\n this.log(this.LogLevel.FATAL, msg);\n },\n\n /** Function: serialize\n * Render a DOM element and all descendants to a String.\n *\n * Parameters:\n * (XMLElement) elem - A DOM element.\n *\n * Returns:\n * The serialized element tree as a String.\n */\n serialize: function serialize(elem) {\n if (!elem) {\n return null;\n }\n\n if (typeof elem.tree === \"function\") {\n elem = elem.tree();\n }\n\n var names = _toConsumableArray(Array(elem.attributes.length).keys()).map(function (i) {\n return elem.attributes[i].nodeName;\n });\n\n names.sort();\n var result = names.reduce(function (a, n) {\n return \"\".concat(a, \" \").concat(n, \"=\\\"\").concat(Strophe.xmlescape(elem.attributes.getNamedItem(n).value), \"\\\"\");\n }, \"<\".concat(elem.nodeName));\n\n if (elem.childNodes.length > 0) {\n result += \">\";\n\n for (var i = 0; i < elem.childNodes.length; i++) {\n var child = elem.childNodes[i];\n\n switch (child.nodeType) {\n case Strophe.ElementType.NORMAL:\n // normal element, so recurse\n result += Strophe.serialize(child);\n break;\n\n case Strophe.ElementType.TEXT:\n // text element to escape values\n result += Strophe.xmlescape(child.nodeValue);\n break;\n\n case Strophe.ElementType.CDATA:\n // cdata section so don't escape values\n result += \"\";\n }\n }\n\n result += \"\";\n } else {\n result += \"/>\";\n }\n\n return result;\n },\n\n /** PrivateVariable: _requestId\n * _Private_ variable that keeps track of the request ids for\n * connections.\n */\n _requestId: 0,\n\n /** PrivateVariable: Strophe.connectionPlugins\n * _Private_ variable Used to store plugin names that need\n * initialization on Strophe.Connection construction.\n */\n _connectionPlugins: {},\n\n /** Function: addConnectionPlugin\n * Extends the Strophe.Connection object with the given plugin.\n *\n * Parameters:\n * (String) name - The name of the extension.\n * (Object) ptype - The plugin's prototype.\n */\n addConnectionPlugin: function addConnectionPlugin(name, ptype) {\n Strophe._connectionPlugins[name] = ptype;\n }\n };\n /** Class: Strophe.Builder\n * XML DOM builder.\n *\n * This object provides an interface similar to JQuery but for building\n * DOM elements easily and rapidly. All the functions except for toString()\n * and tree() return the object, so calls can be chained. Here's an\n * example using the $iq() builder helper.\n * > $iq({to: 'you', from: 'me', type: 'get', id: '1'})\n * > .c('query', {xmlns: 'strophe:example'})\n * > .c('example')\n * > .toString()\n *\n * The above generates this XML fragment\n * > \n * > \n * > \n * > \n * > \n * The corresponding DOM manipulations to get a similar fragment would be\n * a lot more tedious and probably involve several helper variables.\n *\n * Since adding children makes new operations operate on the child, up()\n * is provided to traverse up the tree. To add two children, do\n * > builder.c('child1', ...).up().c('child2', ...)\n * The next operation on the Builder will be relative to the second child.\n */\n\n /** Constructor: Strophe.Builder\n * Create a Strophe.Builder object.\n *\n * The attributes should be passed in object notation. For example\n * > let b = new Builder('message', {to: 'you', from: 'me'});\n * or\n * > let b = new Builder('messsage', {'xml:lang': 'en'});\n *\n * Parameters:\n * (String) name - The name of the root element.\n * (Object) attrs - The attributes for the root element in object notation.\n *\n * Returns:\n * A new Strophe.Builder.\n */\n\n Strophe.Builder = function (name, attrs) {\n // Set correct namespace for jabber:client elements\n if (name === \"presence\" || name === \"message\" || name === \"iq\") {\n if (attrs && !attrs.xmlns) {\n attrs.xmlns = Strophe.NS.CLIENT;\n } else if (!attrs) {\n attrs = {\n xmlns: Strophe.NS.CLIENT\n };\n }\n } // Holds the tree being built.\n\n\n this.nodeTree = Strophe.xmlElement(name, attrs); // Points to the current operation node.\n\n this.node = this.nodeTree;\n };\n\n Strophe.Builder.prototype = {\n /** Function: tree\n * Return the DOM tree.\n *\n * This function returns the current DOM tree as an element object. This\n * is suitable for passing to functions like Strophe.Connection.send().\n *\n * Returns:\n * The DOM tree as a element object.\n */\n tree: function tree() {\n return this.nodeTree;\n },\n\n /** Function: toString\n * Serialize the DOM tree to a String.\n *\n * This function returns a string serialization of the current DOM\n * tree. It is often used internally to pass data to a\n * Strophe.Request object.\n *\n * Returns:\n * The serialized DOM tree in a String.\n */\n toString: function toString() {\n return Strophe.serialize(this.nodeTree);\n },\n\n /** Function: up\n * Make the current parent element the new current element.\n *\n * This function is often used after c() to traverse back up the tree.\n * For example, to add two children to the same element\n * > builder.c('child1', {}).up().c('child2', {});\n *\n * Returns:\n * The Stophe.Builder object.\n */\n up: function up() {\n this.node = this.node.parentNode;\n return this;\n },\n\n /** Function: root\n * Make the root element the new current element.\n *\n * When at a deeply nested element in the tree, this function can be used\n * to jump back to the root of the tree, instead of having to repeatedly\n * call up().\n *\n * Returns:\n * The Stophe.Builder object.\n */\n root: function root() {\n this.node = this.nodeTree;\n return this;\n },\n\n /** Function: attrs\n * Add or modify attributes of the current element.\n *\n * The attributes should be passed in object notation. This function\n * does not move the current element pointer.\n *\n * Parameters:\n * (Object) moreattrs - The attributes to add/modify in object notation.\n *\n * Returns:\n * The Strophe.Builder object.\n */\n attrs: function attrs(moreattrs) {\n for (var k in moreattrs) {\n if (Object.prototype.hasOwnProperty.call(moreattrs, k)) {\n if (moreattrs[k] === undefined) {\n this.node.removeAttribute(k);\n } else {\n this.node.setAttribute(k, moreattrs[k]);\n }\n }\n }\n\n return this;\n },\n\n /** Function: c\n * Add a child to the current element and make it the new current\n * element.\n *\n * This function moves the current element pointer to the child,\n * unless text is provided. If you need to add another child, it\n * is necessary to use up() to go back to the parent in the tree.\n *\n * Parameters:\n * (String) name - The name of the child.\n * (Object) attrs - The attributes of the child in object notation.\n * (String) text - The text to add to the child.\n *\n * Returns:\n * The Strophe.Builder object.\n */\n c: function c(name, attrs, text) {\n var child = Strophe.xmlElement(name, attrs, text);\n this.node.appendChild(child);\n\n if (typeof text !== \"string\" && typeof text !== \"number\") {\n this.node = child;\n }\n\n return this;\n },\n\n /** Function: cnode\n * Add a child to the current element and make it the new current\n * element.\n *\n * This function is the same as c() except that instead of using a\n * name and an attributes object to create the child it uses an\n * existing DOM element object.\n *\n * Parameters:\n * (XMLElement) elem - A DOM element.\n *\n * Returns:\n * The Strophe.Builder object.\n */\n cnode: function cnode(elem) {\n var impNode;\n var xmlGen = Strophe.xmlGenerator();\n\n try {\n impNode = xmlGen.importNode !== undefined;\n } catch (e) {\n impNode = false;\n }\n\n var newElem = impNode ? xmlGen.importNode(elem, true) : Strophe.copyElement(elem);\n this.node.appendChild(newElem);\n this.node = newElem;\n return this;\n },\n\n /** Function: t\n * Add a child text element.\n *\n * This *does not* make the child the new current element since there\n * are no children of text elements.\n *\n * Parameters:\n * (String) text - The text data to append to the current element.\n *\n * Returns:\n * The Strophe.Builder object.\n */\n t: function t(text) {\n var child = Strophe.xmlTextNode(text);\n this.node.appendChild(child);\n return this;\n },\n\n /** Function: h\n * Replace current element contents with the HTML passed in.\n *\n * This *does not* make the child the new current element\n *\n * Parameters:\n * (String) html - The html to insert as contents of current element.\n *\n * Returns:\n * The Strophe.Builder object.\n */\n h: function h(html) {\n var fragment = document.createElement('body'); // force the browser to try and fix any invalid HTML tags\n\n fragment.innerHTML = html; // copy cleaned html into an xml dom\n\n var xhtml = Strophe.createHtml(fragment);\n\n while (xhtml.childNodes.length > 0) {\n this.node.appendChild(xhtml.childNodes[0]);\n }\n\n return this;\n }\n };\n /** PrivateClass: Strophe.Handler\n * _Private_ helper class for managing stanza handlers.\n *\n * A Strophe.Handler encapsulates a user provided callback function to be\n * executed when matching stanzas are received by the connection.\n * Handlers can be either one-off or persistant depending on their\n * return value. Returning true will cause a Handler to remain active, and\n * returning false will remove the Handler.\n *\n * Users will not use Strophe.Handler objects directly, but instead they\n * will use Strophe.Connection.addHandler() and\n * Strophe.Connection.deleteHandler().\n */\n\n /** PrivateConstructor: Strophe.Handler\n * Create and initialize a new Strophe.Handler.\n *\n * Parameters:\n * (Function) handler - A function to be executed when the handler is run.\n * (String) ns - The namespace to match.\n * (String) name - The element name to match.\n * (String) type - The element type to match.\n * (String) id - The element id attribute to match.\n * (String) from - The element from attribute to match.\n * (Object) options - Handler options\n *\n * Returns:\n * A new Strophe.Handler object.\n */\n\n Strophe.Handler = function (handler, ns, name, type, id, from, options) {\n this.handler = handler;\n this.ns = ns;\n this.name = name;\n this.type = type;\n this.id = id;\n this.options = options || {\n 'matchBareFromJid': false,\n 'ignoreNamespaceFragment': false\n }; // BBB: Maintain backward compatibility with old `matchBare` option\n\n if (this.options.matchBare) {\n Strophe.warn('The \"matchBare\" option is deprecated, use \"matchBareFromJid\" instead.');\n this.options.matchBareFromJid = this.options.matchBare;\n delete this.options.matchBare;\n }\n\n if (this.options.matchBareFromJid) {\n this.from = from ? Strophe.getBareJidFromJid(from) : null;\n } else {\n this.from = from;\n } // whether the handler is a user handler or a system handler\n\n\n this.user = true;\n };\n\n Strophe.Handler.prototype = {\n /** PrivateFunction: getNamespace\n * Returns the XML namespace attribute on an element.\n * If `ignoreNamespaceFragment` was passed in for this handler, then the\n * URL fragment will be stripped.\n *\n * Parameters:\n * (XMLElement) elem - The XML element with the namespace.\n *\n * Returns:\n * The namespace, with optionally the fragment stripped.\n */\n getNamespace: function getNamespace(elem) {\n var elNamespace = elem.getAttribute(\"xmlns\");\n\n if (elNamespace && this.options.ignoreNamespaceFragment) {\n elNamespace = elNamespace.split('#')[0];\n }\n\n return elNamespace;\n },\n\n /** PrivateFunction: namespaceMatch\n * Tests if a stanza matches the namespace set for this Strophe.Handler.\n *\n * Parameters:\n * (XMLElement) elem - The XML element to test.\n *\n * Returns:\n * true if the stanza matches and false otherwise.\n */\n namespaceMatch: function namespaceMatch(elem) {\n var _this = this;\n\n var nsMatch = false;\n\n if (!this.ns) {\n return true;\n } else {\n Strophe.forEachChild(elem, null, function (elem) {\n if (_this.getNamespace(elem) === _this.ns) {\n nsMatch = true;\n }\n });\n return nsMatch || this.getNamespace(elem) === this.ns;\n }\n },\n\n /** PrivateFunction: isMatch\n * Tests if a stanza matches the Strophe.Handler.\n *\n * Parameters:\n * (XMLElement) elem - The XML element to test.\n *\n * Returns:\n * true if the stanza matches and false otherwise.\n */\n isMatch: function isMatch(elem) {\n var from = elem.getAttribute('from');\n\n if (this.options.matchBareFromJid) {\n from = Strophe.getBareJidFromJid(from);\n }\n\n var elem_type = elem.getAttribute(\"type\");\n\n if (this.namespaceMatch(elem) && (!this.name || Strophe.isTagEqual(elem, this.name)) && (!this.type || (Array.isArray(this.type) ? this.type.indexOf(elem_type) !== -1 : elem_type === this.type)) && (!this.id || elem.getAttribute(\"id\") === this.id) && (!this.from || from === this.from)) {\n return true;\n }\n\n return false;\n },\n\n /** PrivateFunction: run\n * Run the callback on a matching stanza.\n *\n * Parameters:\n * (XMLElement) elem - The DOM element that triggered the\n * Strophe.Handler.\n *\n * Returns:\n * A boolean indicating if the handler should remain active.\n */\n run: function run(elem) {\n var result = null;\n\n try {\n result = this.handler(elem);\n } catch (e) {\n Strophe._handleError(e);\n\n throw e;\n }\n\n return result;\n },\n\n /** PrivateFunction: toString\n * Get a String representation of the Strophe.Handler object.\n *\n * Returns:\n * A String.\n */\n toString: function toString() {\n return \"{Handler: \" + this.handler + \"(\" + this.name + \",\" + this.id + \",\" + this.ns + \")}\";\n }\n };\n /** PrivateClass: Strophe.TimedHandler\n * _Private_ helper class for managing timed handlers.\n *\n * A Strophe.TimedHandler encapsulates a user provided callback that\n * should be called after a certain period of time or at regular\n * intervals. The return value of the callback determines whether the\n * Strophe.TimedHandler will continue to fire.\n *\n * Users will not use Strophe.TimedHandler objects directly, but instead\n * they will use Strophe.Connection.addTimedHandler() and\n * Strophe.Connection.deleteTimedHandler().\n */\n\n /** PrivateConstructor: Strophe.TimedHandler\n * Create and initialize a new Strophe.TimedHandler object.\n *\n * Parameters:\n * (Integer) period - The number of milliseconds to wait before the\n * handler is called.\n * (Function) handler - The callback to run when the handler fires. This\n * function should take no arguments.\n *\n * Returns:\n * A new Strophe.TimedHandler object.\n */\n\n Strophe.TimedHandler = function (period, handler) {\n this.period = period;\n this.handler = handler;\n this.lastCalled = new Date().getTime();\n this.user = true;\n };\n\n Strophe.TimedHandler.prototype = {\n /** PrivateFunction: run\n * Run the callback for the Strophe.TimedHandler.\n *\n * Returns:\n * true if the Strophe.TimedHandler should be called again, and false\n * otherwise.\n */\n run: function run() {\n this.lastCalled = new Date().getTime();\n return this.handler();\n },\n\n /** PrivateFunction: reset\n * Reset the last called time for the Strophe.TimedHandler.\n */\n reset: function reset() {\n this.lastCalled = new Date().getTime();\n },\n\n /** PrivateFunction: toString\n * Get a string representation of the Strophe.TimedHandler object.\n *\n * Returns:\n * The string representation.\n */\n toString: function toString() {\n return \"{TimedHandler: \" + this.handler + \"(\" + this.period + \")}\";\n }\n };\n /** Class: Strophe.Connection\n * XMPP Connection manager.\n *\n * This class is the main part of Strophe. It manages a BOSH or websocket\n * connection to an XMPP server and dispatches events to the user callbacks\n * as data arrives. It supports SASL PLAIN, SASL DIGEST-MD5, SASL SCRAM-SHA1\n * and legacy authentication.\n *\n * After creating a Strophe.Connection object, the user will typically\n * call connect() with a user supplied callback to handle connection level\n * events like authentication failure, disconnection, or connection\n * complete.\n *\n * The user will also have several event handlers defined by using\n * addHandler() and addTimedHandler(). These will allow the user code to\n * respond to interesting stanzas or do something periodically with the\n * connection. These handlers will be active once authentication is\n * finished.\n *\n * To send data to the connection, use send().\n */\n\n /** Constructor: Strophe.Connection\n * Create and initialize a Strophe.Connection object.\n *\n * The transport-protocol for this connection will be chosen automatically\n * based on the given service parameter. URLs starting with \"ws://\" or\n * \"wss://\" will use WebSockets, URLs starting with \"http://\", \"https://\"\n * or without a protocol will use BOSH.\n *\n * To make Strophe connect to the current host you can leave out the protocol\n * and host part and just pass the path, e.g.\n *\n * > let conn = new Strophe.Connection(\"/http-bind/\");\n *\n * Options common to both Websocket and BOSH:\n * ------------------------------------------\n *\n * cookies:\n *\n * The *cookies* option allows you to pass in cookies to be added to the\n * document. These cookies will then be included in the BOSH XMLHttpRequest\n * or in the websocket connection.\n *\n * The passed in value must be a map of cookie names and string values.\n *\n * > { \"myCookie\": {\n * > \"value\": \"1234\",\n * > \"domain\": \".example.org\",\n * > \"path\": \"/\",\n * > \"expires\": expirationDate\n * > }\n * > }\n *\n * Note that cookies can't be set in this way for other domains (i.e. cross-domain).\n * Those cookies need to be set under those domains, for example they can be\n * set server-side by making a XHR call to that domain to ask it to set any\n * necessary cookies.\n *\n * mechanisms:\n *\n * The *mechanisms* option allows you to specify the SASL mechanisms that this\n * instance of Strophe.Connection (and therefore your XMPP client) will\n * support.\n *\n * The value must be an array of objects with Strophe.SASLMechanism\n * prototypes.\n *\n * If nothing is specified, then the following mechanisms (and their\n * priorities) are registered:\n *\n * SCRAM-SHA1 - 70\n * DIGEST-MD5 - 60\n * PLAIN - 50\n * OAUTH-BEARER - 40\n * OAUTH-2 - 30\n * ANONYMOUS - 20\n * EXTERNAL - 10\n *\n * explicitResourceBinding:\n *\n * If `explicitResourceBinding` is set to a truthy value, then the XMPP client\n * needs to explicitly call `Strophe.Connection.prototype.bind` once the XMPP\n * server has advertised the \"urn:ietf:params:xml:ns:xmpp-bind\" feature.\n *\n * Making this step explicit allows client authors to first finish other\n * stream related tasks, such as setting up an XEP-0198 Stream Management\n * session, before binding the JID resource for this session.\n *\n * WebSocket options:\n * ------------------\n *\n * If you want to connect to the current host with a WebSocket connection you\n * can tell Strophe to use WebSockets through a \"protocol\" attribute in the\n * optional options parameter. Valid values are \"ws\" for WebSocket and \"wss\"\n * for Secure WebSocket.\n * So to connect to \"wss://CURRENT_HOSTNAME/xmpp-websocket\" you would call\n *\n * > let conn = new Strophe.Connection(\"/xmpp-websocket/\", {protocol: \"wss\"});\n *\n * Note that relative URLs _NOT_ starting with a \"/\" will also include the path\n * of the current site.\n *\n * Also because downgrading security is not permitted by browsers, when using\n * relative URLs both BOSH and WebSocket connections will use their secure\n * variants if the current connection to the site is also secure (https).\n *\n * BOSH options:\n * -------------\n *\n * By adding \"sync\" to the options, you can control if requests will\n * be made synchronously or not. The default behaviour is asynchronous.\n * If you want to make requests synchronous, make \"sync\" evaluate to true.\n * > let conn = new Strophe.Connection(\"/http-bind/\", {sync: true});\n *\n * You can also toggle this on an already established connection.\n * > conn.options.sync = true;\n *\n * The *customHeaders* option can be used to provide custom HTTP headers to be\n * included in the XMLHttpRequests made.\n *\n * The *keepalive* option can be used to instruct Strophe to maintain the\n * current BOSH session across interruptions such as webpage reloads.\n *\n * It will do this by caching the sessions tokens in sessionStorage, and when\n * \"restore\" is called it will check whether there are cached tokens with\n * which it can resume an existing session.\n *\n * The *withCredentials* option should receive a Boolean value and is used to\n * indicate wether cookies should be included in ajax requests (by default\n * they're not).\n * Set this value to true if you are connecting to a BOSH service\n * and for some reason need to send cookies to it.\n * In order for this to work cross-domain, the server must also enable\n * credentials by setting the Access-Control-Allow-Credentials response header\n * to \"true\". For most usecases however this setting should be false (which\n * is the default).\n * Additionally, when using Access-Control-Allow-Credentials, the\n * Access-Control-Allow-Origin header can't be set to the wildcard \"*\", but\n * instead must be restricted to actual domains.\n *\n * The *contentType* option can be set to change the default Content-Type\n * of \"text/xml; charset=utf-8\", which can be useful to reduce the amount of\n * CORS preflight requests that are sent to the server.\n *\n * Parameters:\n * (String) service - The BOSH or WebSocket service URL.\n * (Object) options - A hash of configuration options\n *\n * Returns:\n * A new Strophe.Connection object.\n */\n\n Strophe.Connection = function (service, options) {\n var _this2 = this;\n\n // The service URL\n this.service = service; // Configuration options\n\n this.options = options || {};\n var proto = this.options.protocol || \"\"; // Select protocal based on service or options\n\n if (service.indexOf(\"ws:\") === 0 || service.indexOf(\"wss:\") === 0 || proto.indexOf(\"ws\") === 0) {\n this._proto = new Strophe.Websocket(this);\n } else {\n this._proto = new Strophe.Bosh(this);\n }\n /* The connected JID. */\n\n\n this.jid = \"\";\n /* the JIDs domain */\n\n this.domain = null;\n /* stream:features */\n\n this.features = null; // SASL\n\n this._sasl_data = {};\n this.do_session = false;\n this.do_bind = false; // handler lists\n\n this.timedHandlers = [];\n this.handlers = [];\n this.removeTimeds = [];\n this.removeHandlers = [];\n this.addTimeds = [];\n this.addHandlers = [];\n this.protocolErrorHandlers = {\n 'HTTP': {},\n 'websocket': {}\n };\n this._idleTimeout = null;\n this._disconnectTimeout = null;\n this.authenticated = false;\n this.connected = false;\n this.disconnecting = false;\n this.do_authentication = true;\n this.paused = false;\n this.restored = false;\n this._data = [];\n this._uniqueId = 0;\n this._sasl_success_handler = null;\n this._sasl_failure_handler = null;\n this._sasl_challenge_handler = null; // Max retries before disconnecting\n\n this.maxRetries = 5; // Call onIdle callback every 1/10th of a second\n\n this._idleTimeout = setTimeout(function () {\n return _this2._onIdle();\n }, 100);\n utils.addCookies(this.options.cookies);\n this.registerSASLMechanisms(this.options.mechanisms); // initialize plugins\n\n for (var k in Strophe._connectionPlugins) {\n if (Object.prototype.hasOwnProperty.call(Strophe._connectionPlugins, k)) {\n var F = function F() {};\n\n F.prototype = Strophe._connectionPlugins[k];\n this[k] = new F();\n this[k].init(this);\n }\n }\n };\n\n Strophe.Connection.prototype = {\n /** Function: reset\n * Reset the connection.\n *\n * This function should be called after a connection is disconnected\n * before that connection is reused.\n */\n reset: function reset() {\n this._proto._reset(); // SASL\n\n\n this.do_session = false;\n this.do_bind = false; // handler lists\n\n this.timedHandlers = [];\n this.handlers = [];\n this.removeTimeds = [];\n this.removeHandlers = [];\n this.addTimeds = [];\n this.addHandlers = [];\n this.authenticated = false;\n this.connected = false;\n this.disconnecting = false;\n this.restored = false;\n this._data = [];\n this._requests = [];\n this._uniqueId = 0;\n },\n\n /** Function: pause\n * Pause the request manager.\n *\n * This will prevent Strophe from sending any more requests to the\n * server. This is very useful for temporarily pausing\n * BOSH-Connections while a lot of send() calls are happening quickly.\n * This causes Strophe to send the data in a single request, saving\n * many request trips.\n */\n pause: function pause() {\n this.paused = true;\n },\n\n /** Function: resume\n * Resume the request manager.\n *\n * This resumes after pause() has been called.\n */\n resume: function resume() {\n this.paused = false;\n },\n\n /** Function: getUniqueId\n * Generate a unique ID for use in elements.\n *\n * All stanzas are required to have unique id attributes. This\n * function makes creating these easy. Each connection instance has\n * a counter which starts from zero, and the value of this counter\n * plus a colon followed by the suffix becomes the unique id. If no\n * suffix is supplied, the counter is used as the unique id.\n *\n * Suffixes are used to make debugging easier when reading the stream\n * data, and their use is recommended. The counter resets to 0 for\n * every new connection for the same reason. For connections to the\n * same server that authenticate the same way, all the ids should be\n * the same, which makes it easy to see changes. This is useful for\n * automated testing as well.\n *\n * Parameters:\n * (String) suffix - A optional suffix to append to the id.\n *\n * Returns:\n * A unique string to be used for the id attribute.\n */\n getUniqueId: function getUniqueId(suffix) {\n var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {\n var r = Math.random() * 16 | 0,\n v = c === 'x' ? r : r & 0x3 | 0x8;\n return v.toString(16);\n });\n\n if (typeof suffix === \"string\" || typeof suffix === \"number\") {\n return uuid + \":\" + suffix;\n } else {\n return uuid + \"\";\n }\n },\n\n /** Function: addProtocolErrorHandler\n * Register a handler function for when a protocol (websocker or HTTP)\n * error occurs.\n *\n * NOTE: Currently only HTTP errors for BOSH requests are handled.\n * Patches that handle websocket errors would be very welcome.\n *\n * Parameters:\n * (String) protocol - 'HTTP' or 'websocket'\n * (Integer) status_code - Error status code (e.g 500, 400 or 404)\n * (Function) callback - Function that will fire on Http error\n *\n * Example:\n * function onError(err_code){\n * //do stuff\n * }\n *\n * let conn = Strophe.connect('http://example.com/http-bind');\n * conn.addProtocolErrorHandler('HTTP', 500, onError);\n * // Triggers HTTP 500 error and onError handler will be called\n * conn.connect('user_jid@incorrect_jabber_host', 'secret', onConnect);\n */\n addProtocolErrorHandler: function addProtocolErrorHandler(protocol, status_code, callback) {\n this.protocolErrorHandlers[protocol][status_code] = callback;\n },\n\n /** Function: connect\n * Starts the connection process.\n *\n * As the connection process proceeds, the user supplied callback will\n * be triggered multiple times with status updates. The callback\n * should take two arguments - the status code and the error condition.\n *\n * The status code will be one of the values in the Strophe.Status\n * constants. The error condition will be one of the conditions\n * defined in RFC 3920 or the condition 'strophe-parsererror'.\n *\n * The Parameters _wait_, _hold_ and _route_ are optional and only relevant\n * for BOSH connections. Please see XEP 124 for a more detailed explanation\n * of the optional parameters.\n *\n * Parameters:\n * (String) jid - The user's JID. This may be a bare JID,\n * or a full JID. If a node is not supplied, SASL OAUTHBEARER or\n * SASL ANONYMOUS authentication will be attempted (OAUTHBEARER will\n * process the provided password value as an access token).\n * (String) pass - The user's password.\n * (Function) callback - The connect callback function.\n * (Integer) wait - The optional HTTPBIND wait value. This is the\n * time the server will wait before returning an empty result for\n * a request. The default setting of 60 seconds is recommended.\n * (Integer) hold - The optional HTTPBIND hold value. This is the\n * number of connections the server will hold at one time. This\n * should almost always be set to 1 (the default).\n * (String) route - The optional route value.\n * (String) authcid - The optional alternative authentication identity\n * (username) if intending to impersonate another user.\n * When using the SASL-EXTERNAL authentication mechanism, for example\n * with client certificates, then the authcid value is used to\n * determine whether an authorization JID (authzid) should be sent to\n * the server. The authzid should not be sent to the server if the\n * authzid and authcid are the same. So to prevent it from being sent\n * (for example when the JID is already contained in the client\n * certificate), set authcid to that same JID. See XEP-178 for more\n * details.\n */\n connect: function connect(jid, pass, callback, wait, hold, route, authcid) {\n this.jid = jid;\n /** Variable: authzid\n * Authorization identity.\n */\n\n this.authzid = Strophe.getBareJidFromJid(this.jid);\n /** Variable: authcid\n * Authentication identity (User name).\n */\n\n this.authcid = authcid || Strophe.getNodeFromJid(this.jid);\n /** Variable: pass\n * Authentication identity (User password).\n */\n\n this.pass = pass;\n /** Variable: servtype\n * Digest MD5 compatibility.\n */\n\n this.servtype = \"xmpp\";\n this.connect_callback = callback;\n this.disconnecting = false;\n this.connected = false;\n this.authenticated = false;\n this.restored = false; // parse jid for domain\n\n this.domain = Strophe.getDomainFromJid(this.jid);\n\n this._changeConnectStatus(Strophe.Status.CONNECTING, null);\n\n this._proto._connect(wait, hold, route);\n },\n\n /** Function: attach\n * Attach to an already created and authenticated BOSH session.\n *\n * This function is provided to allow Strophe to attach to BOSH\n * sessions which have been created externally, perhaps by a Web\n * application. This is often used to support auto-login type features\n * without putting user credentials into the page.\n *\n * Parameters:\n * (String) jid - The full JID that is bound by the session.\n * (String) sid - The SID of the BOSH session.\n * (String) rid - The current RID of the BOSH session. This RID\n * will be used by the next request.\n * (Function) callback The connect callback function.\n * (Integer) wait - The optional HTTPBIND wait value. This is the\n * time the server will wait before returning an empty result for\n * a request. The default setting of 60 seconds is recommended.\n * Other settings will require tweaks to the Strophe.TIMEOUT value.\n * (Integer) hold - The optional HTTPBIND hold value. This is the\n * number of connections the server will hold at one time. This\n * should almost always be set to 1 (the default).\n * (Integer) wind - The optional HTTBIND window value. This is the\n * allowed range of request ids that are valid. The default is 5.\n */\n attach: function attach(jid, sid, rid, callback, wait, hold, wind) {\n if (this._proto instanceof Strophe.Bosh) {\n this._proto._attach(jid, sid, rid, callback, wait, hold, wind);\n } else {\n var error = new Error('The \"attach\" method can only be used with a BOSH connection.');\n error.name = 'StropheSessionError';\n throw error;\n }\n },\n\n /** Function: restore\n * Attempt to restore a cached BOSH session.\n *\n * This function is only useful in conjunction with providing the\n * \"keepalive\":true option when instantiating a new Strophe.Connection.\n *\n * When \"keepalive\" is set to true, Strophe will cache the BOSH tokens\n * RID (Request ID) and SID (Session ID) and then when this function is\n * called, it will attempt to restore the session from those cached\n * tokens.\n *\n * This function must therefore be called instead of connect or attach.\n *\n * For an example on how to use it, please see examples/restore.js\n *\n * Parameters:\n * (String) jid - The user's JID. This may be a bare JID or a full JID.\n * (Function) callback - The connect callback function.\n * (Integer) wait - The optional HTTPBIND wait value. This is the\n * time the server will wait before returning an empty result for\n * a request. The default setting of 60 seconds is recommended.\n * (Integer) hold - The optional HTTPBIND hold value. This is the\n * number of connections the server will hold at one time. This\n * should almost always be set to 1 (the default).\n * (Integer) wind - The optional HTTBIND window value. This is the\n * allowed range of request ids that are valid. The default is 5.\n */\n restore: function restore(jid, callback, wait, hold, wind) {\n if (this._sessionCachingSupported()) {\n this._proto._restore(jid, callback, wait, hold, wind);\n } else {\n var error = new Error('The \"restore\" method can only be used with a BOSH connection.');\n error.name = 'StropheSessionError';\n throw error;\n }\n },\n\n /** PrivateFunction: _sessionCachingSupported\n * Checks whether sessionStorage and JSON are supported and whether we're\n * using BOSH.\n */\n _sessionCachingSupported: function _sessionCachingSupported() {\n if (this._proto instanceof Strophe.Bosh) {\n if (!JSON) {\n return false;\n }\n\n try {\n sessionStorage.setItem('_strophe_', '_strophe_');\n sessionStorage.removeItem('_strophe_');\n } catch (e) {\n return false;\n }\n\n return true;\n }\n\n return false;\n },\n\n /** Function: xmlInput\n * User overrideable function that receives XML data coming into the\n * connection.\n *\n * The default function does nothing. User code can override this with\n * > Strophe.Connection.xmlInput = function (elem) {\n * > (user code)\n * > };\n *\n * Due to limitations of current Browsers' XML-Parsers the opening and closing\n * tag for WebSocket-Connoctions will be passed as selfclosing here.\n *\n * BOSH-Connections will have all stanzas wrapped in a tag. See\n * if you want to strip this tag.\n *\n * Parameters:\n * (XMLElement) elem - The XML data received by the connection.\n */\n xmlInput: function xmlInput(elem) {\n return;\n },\n\n /** Function: xmlOutput\n * User overrideable function that receives XML data sent to the\n * connection.\n *\n * The default function does nothing. User code can override this with\n * > Strophe.Connection.xmlOutput = function (elem) {\n * > (user code)\n * > };\n *\n * Due to limitations of current Browsers' XML-Parsers the opening and closing\n * tag for WebSocket-Connoctions will be passed as selfclosing here.\n *\n * BOSH-Connections will have all stanzas wrapped in a tag. See\n * if you want to strip this tag.\n *\n * Parameters:\n * (XMLElement) elem - The XMLdata sent by the connection.\n */\n xmlOutput: function xmlOutput(elem) {\n return;\n },\n\n /** Function: rawInput\n * User overrideable function that receives raw data coming into the\n * connection.\n *\n * The default function does nothing. User code can override this with\n * > Strophe.Connection.rawInput = function (data) {\n * > (user code)\n * > };\n *\n * Parameters:\n * (String) data - The data received by the connection.\n */\n rawInput: function rawInput(data) {\n return;\n },\n\n /** Function: rawOutput\n * User overrideable function that receives raw data sent to the\n * connection.\n *\n * The default function does nothing. User code can override this with\n * > Strophe.Connection.rawOutput = function (data) {\n * > (user code)\n * > };\n *\n * Parameters:\n * (String) data - The data sent by the connection.\n */\n rawOutput: function rawOutput(data) {\n return;\n },\n\n /** Function: nextValidRid\n * User overrideable function that receives the new valid rid.\n *\n * The default function does nothing. User code can override this with\n * > Strophe.Connection.nextValidRid = function (rid) {\n * > (user code)\n * > };\n *\n * Parameters:\n * (Number) rid - The next valid rid\n */\n nextValidRid: function nextValidRid(rid) {\n return;\n },\n\n /** Function: send\n * Send a stanza.\n *\n * This function is called to push data onto the send queue to\n * go out over the wire. Whenever a request is sent to the BOSH\n * server, all pending data is sent and the queue is flushed.\n *\n * Parameters:\n * (XMLElement |\n * [XMLElement] |\n * Strophe.Builder) elem - The stanza to send.\n */\n send: function send(elem) {\n if (elem === null) {\n return;\n }\n\n if (typeof elem.sort === \"function\") {\n for (var i = 0; i < elem.length; i++) {\n this._queueData(elem[i]);\n }\n } else if (typeof elem.tree === \"function\") {\n this._queueData(elem.tree());\n } else {\n this._queueData(elem);\n }\n\n this._proto._send();\n },\n\n /** Function: flush\n * Immediately send any pending outgoing data.\n *\n * Normally send() queues outgoing data until the next idle period\n * (100ms), which optimizes network use in the common cases when\n * several send()s are called in succession. flush() can be used to\n * immediately send all pending data.\n */\n flush: function flush() {\n // cancel the pending idle period and run the idle function\n // immediately\n clearTimeout(this._idleTimeout);\n\n this._onIdle();\n },\n\n /** Function: sendPresence\n * Helper function to send presence stanzas. The main benefit is for\n * sending presence stanzas for which you expect a responding presence\n * stanza with the same id (for example when leaving a chat room).\n *\n * Parameters:\n * (XMLElement) elem - The stanza to send.\n * (Function) callback - The callback function for a successful request.\n * (Function) errback - The callback function for a failed or timed\n * out request. On timeout, the stanza will be null.\n * (Integer) timeout - The time specified in milliseconds for a\n * timeout to occur.\n *\n * Returns:\n * The id used to send the presence.\n */\n sendPresence: function sendPresence(elem, callback, errback, timeout) {\n var _this3 = this;\n\n var timeoutHandler = null;\n\n if (typeof elem.tree === \"function\") {\n elem = elem.tree();\n }\n\n var id = elem.getAttribute('id');\n\n if (!id) {\n // inject id if not found\n id = this.getUniqueId(\"sendPresence\");\n elem.setAttribute(\"id\", id);\n }\n\n if (typeof callback === \"function\" || typeof errback === \"function\") {\n var handler = this.addHandler(function (stanza) {\n // remove timeout handler if there is one\n if (timeoutHandler) {\n _this3.deleteTimedHandler(timeoutHandler);\n }\n\n if (stanza.getAttribute('type') === 'error') {\n if (errback) {\n errback(stanza);\n }\n } else if (callback) {\n callback(stanza);\n }\n }, null, 'presence', null, id); // if timeout specified, set up a timeout handler.\n\n if (timeout) {\n timeoutHandler = this.addTimedHandler(timeout, function () {\n // get rid of normal handler\n _this3.deleteHandler(handler); // call errback on timeout with null stanza\n\n\n if (errback) {\n errback(null);\n }\n\n return false;\n });\n }\n }\n\n this.send(elem);\n return id;\n },\n\n /** Function: sendIQ\n * Helper function to send IQ stanzas.\n *\n * Parameters:\n * (XMLElement) elem - The stanza to send.\n * (Function) callback - The callback function for a successful request.\n * (Function) errback - The callback function for a failed or timed\n * out request. On timeout, the stanza will be null.\n * (Integer) timeout - The time specified in milliseconds for a\n * timeout to occur.\n *\n * Returns:\n * The id used to send the IQ.\n */\n sendIQ: function sendIQ(elem, callback, errback, timeout) {\n var _this4 = this;\n\n var timeoutHandler = null;\n\n if (typeof elem.tree === \"function\") {\n elem = elem.tree();\n }\n\n var id = elem.getAttribute('id');\n\n if (!id) {\n // inject id if not found\n id = this.getUniqueId(\"sendIQ\");\n elem.setAttribute(\"id\", id);\n }\n\n if (typeof callback === \"function\" || typeof errback === \"function\") {\n var handler = this.addHandler(function (stanza) {\n // remove timeout handler if there is one\n if (timeoutHandler) {\n _this4.deleteTimedHandler(timeoutHandler);\n }\n\n var iqtype = stanza.getAttribute('type');\n\n if (iqtype === 'result') {\n if (callback) {\n callback(stanza);\n }\n } else if (iqtype === 'error') {\n if (errback) {\n errback(stanza);\n }\n } else {\n var error = new Error(\"Got bad IQ type of \".concat(iqtype));\n error.name = \"StropheError\";\n throw error;\n }\n }, null, 'iq', ['error', 'result'], id); // if timeout specified, set up a timeout handler.\n\n if (timeout) {\n timeoutHandler = this.addTimedHandler(timeout, function () {\n // get rid of normal handler\n _this4.deleteHandler(handler); // call errback on timeout with null stanza\n\n\n if (errback) {\n errback(null);\n }\n\n return false;\n });\n }\n }\n\n this.send(elem);\n return id;\n },\n\n /** PrivateFunction: _queueData\n * Queue outgoing data for later sending. Also ensures that the data\n * is a DOMElement.\n */\n _queueData: function _queueData(element) {\n if (element === null || !element.tagName || !element.childNodes) {\n var error = new Error(\"Cannot queue non-DOMElement.\");\n error.name = \"StropheError\";\n throw error;\n }\n\n this._data.push(element);\n },\n\n /** PrivateFunction: _sendRestart\n * Send an xmpp:restart stanza.\n */\n _sendRestart: function _sendRestart() {\n var _this5 = this;\n\n this._data.push(\"restart\");\n\n this._proto._sendRestart();\n\n this._idleTimeout = setTimeout(function () {\n return _this5._onIdle();\n }, 100);\n },\n\n /** Function: addTimedHandler\n * Add a timed handler to the connection.\n *\n * This function adds a timed handler. The provided handler will\n * be called every period milliseconds until it returns false,\n * the connection is terminated, or the handler is removed. Handlers\n * that wish to continue being invoked should return true.\n *\n * Because of method binding it is necessary to save the result of\n * this function if you wish to remove a handler with\n * deleteTimedHandler().\n *\n * Note that user handlers are not active until authentication is\n * successful.\n *\n * Parameters:\n * (Integer) period - The period of the handler.\n * (Function) handler - The callback function.\n *\n * Returns:\n * A reference to the handler that can be used to remove it.\n */\n addTimedHandler: function addTimedHandler(period, handler) {\n var thand = new Strophe.TimedHandler(period, handler);\n this.addTimeds.push(thand);\n return thand;\n },\n\n /** Function: deleteTimedHandler\n * Delete a timed handler for a connection.\n *\n * This function removes a timed handler from the connection. The\n * handRef parameter is *not* the function passed to addTimedHandler(),\n * but is the reference returned from addTimedHandler().\n *\n * Parameters:\n * (Strophe.TimedHandler) handRef - The handler reference.\n */\n deleteTimedHandler: function deleteTimedHandler(handRef) {\n // this must be done in the Idle loop so that we don't change\n // the handlers during iteration\n this.removeTimeds.push(handRef);\n },\n\n /** Function: addHandler\n * Add a stanza handler for the connection.\n *\n * This function adds a stanza handler to the connection. The\n * handler callback will be called for any stanza that matches\n * the parameters. Note that if multiple parameters are supplied,\n * they must all match for the handler to be invoked.\n *\n * The handler will receive the stanza that triggered it as its argument.\n * *The handler should return true if it is to be invoked again;\n * returning false will remove the handler after it returns.*\n *\n * As a convenience, the ns parameters applies to the top level element\n * and also any of its immediate children. This is primarily to make\n * matching /iq/query elements easy.\n *\n * Options\n * ~~~~~~~\n * With the options argument, you can specify boolean flags that affect how\n * matches are being done.\n *\n * Currently two flags exist:\n *\n * - matchBareFromJid:\n * When set to true, the from parameter and the\n * from attribute on the stanza will be matched as bare JIDs instead\n * of full JIDs. To use this, pass {matchBareFromJid: true} as the\n * value of options. The default value for matchBareFromJid is false.\n *\n * - ignoreNamespaceFragment:\n * When set to true, a fragment specified on the stanza's namespace\n * URL will be ignored when it's matched with the one configured for\n * the handler.\n *\n * This means that if you register like this:\n * > connection.addHandler(\n * > handler,\n * > 'http://jabber.org/protocol/muc',\n * > null, null, null, null,\n * > {'ignoreNamespaceFragment': true}\n * > );\n *\n * Then a stanza with XML namespace of\n * 'http://jabber.org/protocol/muc#user' will also be matched. If\n * 'ignoreNamespaceFragment' is false, then only stanzas with\n * 'http://jabber.org/protocol/muc' will be matched.\n *\n * Deleting the handler\n * ~~~~~~~~~~~~~~~~~~~~\n * The return value should be saved if you wish to remove the handler\n * with deleteHandler().\n *\n * Parameters:\n * (Function) handler - The user callback.\n * (String) ns - The namespace to match.\n * (String) name - The stanza name to match.\n * (String|Array) type - The stanza type (or types if an array) to match.\n * (String) id - The stanza id attribute to match.\n * (String) from - The stanza from attribute to match.\n * (String) options - The handler options\n *\n * Returns:\n * A reference to the handler that can be used to remove it.\n */\n addHandler: function addHandler(handler, ns, name, type, id, from, options) {\n var hand = new Strophe.Handler(handler, ns, name, type, id, from, options);\n this.addHandlers.push(hand);\n return hand;\n },\n\n /** Function: deleteHandler\n * Delete a stanza handler for a connection.\n *\n * This function removes a stanza handler from the connection. The\n * handRef parameter is *not* the function passed to addHandler(),\n * but is the reference returned from addHandler().\n *\n * Parameters:\n * (Strophe.Handler) handRef - The handler reference.\n */\n deleteHandler: function deleteHandler(handRef) {\n // this must be done in the Idle loop so that we don't change\n // the handlers during iteration\n this.removeHandlers.push(handRef); // If a handler is being deleted while it is being added,\n // prevent it from getting added\n\n var i = this.addHandlers.indexOf(handRef);\n\n if (i >= 0) {\n this.addHandlers.splice(i, 1);\n }\n },\n\n /** Function: registerSASLMechanisms\n *\n * Register the SASL mechanisms which will be supported by this instance of\n * Strophe.Connection (i.e. which this XMPP client will support).\n *\n * Parameters:\n * (Array) mechanisms - Array of objects with Strophe.SASLMechanism prototypes\n *\n */\n registerSASLMechanisms: function registerSASLMechanisms(mechanisms) {\n this.mechanisms = {};\n mechanisms = mechanisms || [Strophe.SASLAnonymous, Strophe.SASLExternal, Strophe.SASLMD5, Strophe.SASLOAuthBearer, Strophe.SASLXOAuth2, Strophe.SASLPlain, Strophe.SASLSHA1];\n mechanisms.forEach(this.registerSASLMechanism.bind(this));\n },\n\n /** Function: registerSASLMechanism\n *\n * Register a single SASL mechanism, to be supported by this client.\n *\n * Parameters:\n * (Object) mechanism - Object with a Strophe.SASLMechanism prototype\n *\n */\n registerSASLMechanism: function registerSASLMechanism(mechanism) {\n this.mechanisms[mechanism.prototype.name] = mechanism;\n },\n\n /** Function: disconnect\n * Start the graceful disconnection process.\n *\n * This function starts the disconnection process. This process starts\n * by sending unavailable presence and sending BOSH body of type\n * terminate. A timeout handler makes sure that disconnection happens\n * even if the BOSH server does not respond.\n * If the Connection object isn't connected, at least tries to abort all pending requests\n * so the connection object won't generate successful requests (which were already opened).\n *\n * The user supplied connection callback will be notified of the\n * progress as this process happens.\n *\n * Parameters:\n * (String) reason - The reason the disconnect is occuring.\n */\n disconnect: function disconnect(reason) {\n this._changeConnectStatus(Strophe.Status.DISCONNECTING, reason);\n\n Strophe.warn(\"Disconnect was called because: \" + reason);\n\n if (this.connected) {\n var pres = false;\n this.disconnecting = true;\n\n if (this.authenticated) {\n pres = $pres({\n 'xmlns': Strophe.NS.CLIENT,\n 'type': 'unavailable'\n });\n } // setup timeout handler\n\n\n this._disconnectTimeout = this._addSysTimedHandler(3000, this._onDisconnectTimeout.bind(this));\n\n this._proto._disconnect(pres);\n } else {\n Strophe.warn(\"Disconnect was called before Strophe connected to the server\");\n\n this._proto._abortAllRequests();\n\n this._doDisconnect();\n }\n },\n\n /** PrivateFunction: _changeConnectStatus\n * _Private_ helper function that makes sure plugins and the user's\n * callback are notified of connection status changes.\n *\n * Parameters:\n * (Integer) status - the new connection status, one of the values\n * in Strophe.Status\n * (String) condition - the error condition or null\n * (XMLElement) elem - The triggering stanza.\n */\n _changeConnectStatus: function _changeConnectStatus(status, condition, elem) {\n // notify all plugins listening for status changes\n for (var k in Strophe._connectionPlugins) {\n if (Object.prototype.hasOwnProperty.call(Strophe._connectionPlugins, k)) {\n var plugin = this[k];\n\n if (plugin.statusChanged) {\n try {\n plugin.statusChanged(status, condition);\n } catch (err) {\n Strophe.error(\"\".concat(k, \" plugin caused an exception changing status: \").concat(err));\n }\n }\n }\n } // notify the user's callback\n\n\n if (this.connect_callback) {\n try {\n this.connect_callback(status, condition, elem);\n } catch (e) {\n Strophe._handleError(e);\n\n Strophe.error(\"User connection callback caused an exception: \".concat(e));\n }\n }\n },\n\n /** PrivateFunction: _doDisconnect\n * _Private_ function to disconnect.\n *\n * This is the last piece of the disconnection logic. This resets the\n * connection and alerts the user's connection callback.\n */\n _doDisconnect: function _doDisconnect(condition) {\n if (typeof this._idleTimeout === \"number\") {\n clearTimeout(this._idleTimeout);\n } // Cancel Disconnect Timeout\n\n\n if (this._disconnectTimeout !== null) {\n this.deleteTimedHandler(this._disconnectTimeout);\n this._disconnectTimeout = null;\n }\n\n Strophe.debug(\"_doDisconnect was called\");\n\n this._proto._doDisconnect();\n\n this.authenticated = false;\n this.disconnecting = false;\n this.restored = false; // delete handlers\n\n this.handlers = [];\n this.timedHandlers = [];\n this.removeTimeds = [];\n this.removeHandlers = [];\n this.addTimeds = [];\n this.addHandlers = []; // tell the parent we disconnected\n\n this._changeConnectStatus(Strophe.Status.DISCONNECTED, condition);\n\n this.connected = false;\n },\n\n /** PrivateFunction: _dataRecv\n * _Private_ handler to processes incoming data from the the connection.\n *\n * Except for _connect_cb handling the initial connection request,\n * this function handles the incoming data for all requests. This\n * function also fires stanza handlers that match each incoming\n * stanza.\n *\n * Parameters:\n * (Strophe.Request) req - The request that has data ready.\n * (string) req - The stanza a raw string (optiona).\n */\n _dataRecv: function _dataRecv(req, raw) {\n var _this6 = this;\n\n Strophe.debug(\"_dataRecv called\");\n\n var elem = this._proto._reqToData(req);\n\n if (elem === null) {\n return;\n }\n\n if (this.xmlInput !== Strophe.Connection.prototype.xmlInput) {\n if (elem.nodeName === this._proto.strip && elem.childNodes.length) {\n this.xmlInput(elem.childNodes[0]);\n } else {\n this.xmlInput(elem);\n }\n }\n\n if (this.rawInput !== Strophe.Connection.prototype.rawInput) {\n if (raw) {\n this.rawInput(raw);\n } else {\n this.rawInput(Strophe.serialize(elem));\n }\n } // remove handlers scheduled for deletion\n\n\n while (this.removeHandlers.length > 0) {\n var hand = this.removeHandlers.pop();\n var i = this.handlers.indexOf(hand);\n\n if (i >= 0) {\n this.handlers.splice(i, 1);\n }\n } // add handlers scheduled for addition\n\n\n while (this.addHandlers.length > 0) {\n this.handlers.push(this.addHandlers.pop());\n } // handle graceful disconnect\n\n\n if (this.disconnecting && this._proto._emptyQueue()) {\n this._doDisconnect();\n\n return;\n }\n\n var type = elem.getAttribute(\"type\");\n\n if (type !== null && type === \"terminate\") {\n // Don't process stanzas that come in after disconnect\n if (this.disconnecting) {\n return;\n } // an error occurred\n\n\n var cond = elem.getAttribute(\"condition\");\n var conflict = elem.getElementsByTagName(\"conflict\");\n\n if (cond !== null) {\n if (cond === \"remote-stream-error\" && conflict.length > 0) {\n cond = \"conflict\";\n }\n\n this._changeConnectStatus(Strophe.Status.CONNFAIL, cond);\n } else {\n this._changeConnectStatus(Strophe.Status.CONNFAIL, Strophe.ErrorCondition.UNKOWN_REASON);\n }\n\n this._doDisconnect(cond);\n\n return;\n } // send each incoming stanza through the handler chain\n\n\n Strophe.forEachChild(elem, null, function (child) {\n // process handlers\n var newList = _this6.handlers;\n _this6.handlers = [];\n\n for (var _i5 = 0; _i5 < newList.length; _i5++) {\n var _hand = newList[_i5]; // encapsulate 'handler.run' not to lose the whole handler list if\n // one of the handlers throws an exception\n\n try {\n if (_hand.isMatch(child) && (_this6.authenticated || !_hand.user)) {\n if (_hand.run(child)) {\n _this6.handlers.push(_hand);\n }\n } else {\n _this6.handlers.push(_hand);\n }\n } catch (e) {\n // if the handler throws an exception, we consider it as false\n Strophe.warn('Removing Strophe handlers due to uncaught exception: ' + e.message);\n }\n }\n });\n },\n\n /** Attribute: mechanisms\n * SASL Mechanisms available for Connection.\n */\n mechanisms: {},\n\n /** PrivateFunction: _connect_cb\n * _Private_ handler for initial connection request.\n *\n * This handler is used to process the initial connection request\n * response from the BOSH server. It is used to set up authentication\n * handlers and start the authentication process.\n *\n * SASL authentication will be attempted if available, otherwise\n * the code will fall back to legacy authentication.\n *\n * Parameters:\n * (Strophe.Request) req - The current request.\n * (Function) _callback - low level (xmpp) connect callback function.\n * Useful for plugins with their own xmpp connect callback (when they\n * want to do something special).\n */\n _connect_cb: function _connect_cb(req, _callback, raw) {\n Strophe.debug(\"_connect_cb was called\");\n this.connected = true;\n var bodyWrap;\n\n try {\n bodyWrap = this._proto._reqToData(req);\n } catch (e) {\n if (e.name !== Strophe.ErrorCondition.BAD_FORMAT) {\n throw e;\n }\n\n this._changeConnectStatus(Strophe.Status.CONNFAIL, Strophe.ErrorCondition.BAD_FORMAT);\n\n this._doDisconnect(Strophe.ErrorCondition.BAD_FORMAT);\n }\n\n if (!bodyWrap) {\n return;\n }\n\n if (this.xmlInput !== Strophe.Connection.prototype.xmlInput) {\n if (bodyWrap.nodeName === this._proto.strip && bodyWrap.childNodes.length) {\n this.xmlInput(bodyWrap.childNodes[0]);\n } else {\n this.xmlInput(bodyWrap);\n }\n }\n\n if (this.rawInput !== Strophe.Connection.prototype.rawInput) {\n if (raw) {\n this.rawInput(raw);\n } else {\n this.rawInput(Strophe.serialize(bodyWrap));\n }\n }\n\n var conncheck = this._proto._connect_cb(bodyWrap);\n\n if (conncheck === Strophe.Status.CONNFAIL) {\n return;\n } // Check for the stream:features tag\n\n\n var hasFeatures;\n\n if (bodyWrap.getElementsByTagNameNS) {\n hasFeatures = bodyWrap.getElementsByTagNameNS(Strophe.NS.STREAM, \"features\").length > 0;\n } else {\n hasFeatures = bodyWrap.getElementsByTagName(\"stream:features\").length > 0 || bodyWrap.getElementsByTagName(\"features\").length > 0;\n }\n\n if (!hasFeatures) {\n this._proto._no_auth_received(_callback);\n\n return;\n }\n\n var matched = [];\n var mechanisms = bodyWrap.getElementsByTagName(\"mechanism\");\n\n if (mechanisms.length > 0) {\n for (var i = 0; i < mechanisms.length; i++) {\n var mech = Strophe.getText(mechanisms[i]);\n if (this.mechanisms[mech]) matched.push(this.mechanisms[mech]);\n }\n }\n\n if (matched.length === 0) {\n if (bodyWrap.getElementsByTagName(\"auth\").length === 0) {\n // There are no matching SASL mechanisms and also no legacy\n // auth available.\n this._proto._no_auth_received(_callback);\n\n return;\n }\n }\n\n if (this.do_authentication !== false) {\n this.authenticate(matched);\n }\n },\n\n /** Function: sortMechanismsByPriority\n *\n * Sorts an array of objects with prototype SASLMechanism according to\n * their priorities.\n *\n * Parameters:\n * (Array) mechanisms - Array of SASL mechanisms.\n *\n */\n sortMechanismsByPriority: function sortMechanismsByPriority(mechanisms) {\n // Sorting mechanisms according to priority.\n for (var i = 0; i < mechanisms.length - 1; ++i) {\n var higher = i;\n\n for (var j = i + 1; j < mechanisms.length; ++j) {\n if (mechanisms[j].prototype.priority > mechanisms[higher].prototype.priority) {\n higher = j;\n }\n }\n\n if (higher !== i) {\n var swap = mechanisms[i];\n mechanisms[i] = mechanisms[higher];\n mechanisms[higher] = swap;\n }\n }\n\n return mechanisms;\n },\n\n /** Function: authenticate\n * Set up authentication\n *\n * Continues the initial connection request by setting up authentication\n * handlers and starting the authentication process.\n *\n * SASL authentication will be attempted if available, otherwise\n * the code will fall back to legacy authentication.\n *\n * Parameters:\n * (Array) matched - Array of SASL mechanisms supported.\n *\n */\n authenticate: function authenticate(matched) {\n if (!this._attemptSASLAuth(matched)) {\n this._attemptLegacyAuth();\n }\n },\n\n /** PrivateFunction: _attemptSASLAuth\n *\n * Iterate through an array of SASL mechanisms and attempt authentication\n * with the highest priority (enabled) mechanism.\n *\n * Parameters:\n * (Array) mechanisms - Array of SASL mechanisms.\n *\n * Returns:\n * (Boolean) mechanism_found - true or false, depending on whether a\n * valid SASL mechanism was found with which authentication could be\n * started.\n */\n _attemptSASLAuth: function _attemptSASLAuth(mechanisms) {\n mechanisms = this.sortMechanismsByPriority(mechanisms || []);\n var mechanism_found = false;\n\n for (var i = 0; i < mechanisms.length; ++i) {\n if (!mechanisms[i].prototype.test(this)) {\n continue;\n }\n\n this._sasl_success_handler = this._addSysHandler(this._sasl_success_cb.bind(this), null, \"success\", null, null);\n this._sasl_failure_handler = this._addSysHandler(this._sasl_failure_cb.bind(this), null, \"failure\", null, null);\n this._sasl_challenge_handler = this._addSysHandler(this._sasl_challenge_cb.bind(this), null, \"challenge\", null, null);\n this._sasl_mechanism = new mechanisms[i]();\n\n this._sasl_mechanism.onStart(this);\n\n var request_auth_exchange = $build(\"auth\", {\n 'xmlns': Strophe.NS.SASL,\n 'mechanism': this._sasl_mechanism.name\n });\n\n if (this._sasl_mechanism.isClientFirst) {\n var response = this._sasl_mechanism.onChallenge(this, null);\n\n request_auth_exchange.t(btoa(response));\n }\n\n this.send(request_auth_exchange.tree());\n mechanism_found = true;\n break;\n }\n\n return mechanism_found;\n },\n\n /** PrivateFunction: _sasl_challenge_cb\n * _Private_ handler for the SASL challenge\n *\n */\n _sasl_challenge_cb: function _sasl_challenge_cb(elem) {\n var challenge = atob(Strophe.getText(elem));\n\n var response = this._sasl_mechanism.onChallenge(this, challenge);\n\n var stanza = $build('response', {\n 'xmlns': Strophe.NS.SASL\n });\n\n if (response !== \"\") {\n stanza.t(btoa(response));\n }\n\n this.send(stanza.tree());\n return true;\n },\n\n /** PrivateFunction: _attemptLegacyAuth\n *\n * Attempt legacy (i.e. non-SASL) authentication.\n */\n _attemptLegacyAuth: function _attemptLegacyAuth() {\n if (Strophe.getNodeFromJid(this.jid) === null) {\n // we don't have a node, which is required for non-anonymous\n // client connections\n this._changeConnectStatus(Strophe.Status.CONNFAIL, Strophe.ErrorCondition.MISSING_JID_NODE);\n\n this.disconnect(Strophe.ErrorCondition.MISSING_JID_NODE);\n } else {\n // Fall back to legacy authentication\n this._changeConnectStatus(Strophe.Status.AUTHENTICATING, null);\n\n this._addSysHandler(this._onLegacyAuthIQResult.bind(this), null, null, null, \"_auth_1\");\n\n this.send($iq({\n 'type': \"get\",\n 'to': this.domain,\n 'id': \"_auth_1\"\n }).c(\"query\", {\n xmlns: Strophe.NS.AUTH\n }).c(\"username\", {}).t(Strophe.getNodeFromJid(this.jid)).tree());\n }\n },\n\n /** PrivateFunction: _onLegacyAuthIQResult\n * _Private_ handler for legacy authentication.\n *\n * This handler is called in response to the initial \n * for legacy authentication. It builds an authentication and\n * sends it, creating a handler (calling back to _auth2_cb()) to\n * handle the result\n *\n * Parameters:\n * (XMLElement) elem - The stanza that triggered the callback.\n *\n * Returns:\n * false to remove the handler.\n */\n _onLegacyAuthIQResult: function _onLegacyAuthIQResult(elem) {\n // build plaintext auth iq\n var iq = $iq({\n type: \"set\",\n id: \"_auth_2\"\n }).c('query', {\n xmlns: Strophe.NS.AUTH\n }).c('username', {}).t(Strophe.getNodeFromJid(this.jid)).up().c('password').t(this.pass);\n\n if (!Strophe.getResourceFromJid(this.jid)) {\n // since the user has not supplied a resource, we pick\n // a default one here. unlike other auth methods, the server\n // cannot do this for us.\n this.jid = Strophe.getBareJidFromJid(this.jid) + '/strophe';\n }\n\n iq.up().c('resource', {}).t(Strophe.getResourceFromJid(this.jid));\n\n this._addSysHandler(this._auth2_cb.bind(this), null, null, null, \"_auth_2\");\n\n this.send(iq.tree());\n return false;\n },\n\n /** PrivateFunction: _sasl_success_cb\n * _Private_ handler for succesful SASL authentication.\n *\n * Parameters:\n * (XMLElement) elem - The matching stanza.\n *\n * Returns:\n * false to remove the handler.\n */\n _sasl_success_cb: function _sasl_success_cb(elem) {\n var _this7 = this;\n\n if (this._sasl_data[\"server-signature\"]) {\n var serverSignature;\n var success = atob(Strophe.getText(elem));\n var attribMatch = /([a-z]+)=([^,]+)(,|$)/;\n var matches = success.match(attribMatch);\n\n if (matches[1] === \"v\") {\n serverSignature = matches[2];\n }\n\n if (serverSignature !== this._sasl_data[\"server-signature\"]) {\n // remove old handlers\n this.deleteHandler(this._sasl_failure_handler);\n this._sasl_failure_handler = null;\n\n if (this._sasl_challenge_handler) {\n this.deleteHandler(this._sasl_challenge_handler);\n this._sasl_challenge_handler = null;\n }\n\n this._sasl_data = {};\n return this._sasl_failure_cb(null);\n }\n }\n\n Strophe.info(\"SASL authentication succeeded.\");\n\n if (this._sasl_mechanism) {\n this._sasl_mechanism.onSuccess();\n } // remove old handlers\n\n\n this.deleteHandler(this._sasl_failure_handler);\n this._sasl_failure_handler = null;\n\n if (this._sasl_challenge_handler) {\n this.deleteHandler(this._sasl_challenge_handler);\n this._sasl_challenge_handler = null;\n }\n\n var streamfeature_handlers = [];\n\n var wrapper = function wrapper(handlers, elem) {\n while (handlers.length) {\n _this7.deleteHandler(handlers.pop());\n }\n\n _this7._onStreamFeaturesAfterSASL(elem);\n\n return false;\n };\n\n streamfeature_handlers.push(this._addSysHandler(function (elem) {\n return wrapper(streamfeature_handlers, elem);\n }, null, \"stream:features\", null, null));\n streamfeature_handlers.push(this._addSysHandler(function (elem) {\n return wrapper(streamfeature_handlers, elem);\n }, Strophe.NS.STREAM, \"features\", null, null)); // we must send an xmpp:restart now\n\n this._sendRestart();\n\n return false;\n },\n\n /** PrivateFunction: _onStreamFeaturesAfterSASL\n * Parameters:\n * (XMLElement) elem - The matching stanza.\n *\n * Returns:\n * false to remove the handler.\n */\n _onStreamFeaturesAfterSASL: function _onStreamFeaturesAfterSASL(elem) {\n // save stream:features for future usage\n this.features = elem;\n\n for (var i = 0; i < elem.childNodes.length; i++) {\n var child = elem.childNodes[i];\n\n if (child.nodeName === 'bind') {\n this.do_bind = true;\n }\n\n if (child.nodeName === 'session') {\n this.do_session = true;\n }\n }\n\n if (!this.do_bind) {\n this._changeConnectStatus(Strophe.Status.AUTHFAIL, null);\n\n return false;\n } else if (!this.options.explicitResourceBinding) {\n this.bind();\n } else {\n this._changeConnectStatus(Strophe.Status.BINDREQUIRED, null);\n }\n\n return false;\n },\n\n /** Function: bind\n *\n * Sends an IQ to the XMPP server to bind a JID resource for this session.\n *\n * https://tools.ietf.org/html/rfc6120#section-7.5\n *\n * If `explicitResourceBinding` was set to a truthy value in the options\n * passed to the Strophe.Connection constructor, then this function needs\n * to be called explicitly by the client author.\n *\n * Otherwise it'll be called automatically as soon as the XMPP server\n * advertises the \"urn:ietf:params:xml:ns:xmpp-bind\" stream feature.\n */\n bind: function bind() {\n if (!this.do_bind) {\n Strophe.log(Strophe.LogLevel.INFO, \"Strophe.Connection.prototype.bind called but \\\"do_bind\\\" is false\");\n return;\n }\n\n this._addSysHandler(this._onResourceBindResultIQ.bind(this), null, null, null, \"_bind_auth_2\");\n\n var resource = Strophe.getResourceFromJid(this.jid);\n\n if (resource) {\n this.send($iq({\n type: \"set\",\n id: \"_bind_auth_2\"\n }).c('bind', {\n xmlns: Strophe.NS.BIND\n }).c('resource', {}).t(resource).tree());\n } else {\n this.send($iq({\n type: \"set\",\n id: \"_bind_auth_2\"\n }).c('bind', {\n xmlns: Strophe.NS.BIND\n }).tree());\n }\n },\n\n /** PrivateFunction: _onResourceBindIQ\n * _Private_ handler for binding result and session start.\n *\n * Parameters:\n * (XMLElement) elem - The matching stanza.\n *\n * Returns:\n * false to remove the handler.\n */\n _onResourceBindResultIQ: function _onResourceBindResultIQ(elem) {\n if (elem.getAttribute(\"type\") === \"error\") {\n Strophe.warn(\"Resource binding failed.\");\n var conflict = elem.getElementsByTagName(\"conflict\");\n var condition;\n\n if (conflict.length > 0) {\n condition = Strophe.ErrorCondition.CONFLICT;\n }\n\n this._changeConnectStatus(Strophe.Status.AUTHFAIL, condition, elem);\n\n return false;\n } // TODO - need to grab errors\n\n\n var bind = elem.getElementsByTagName(\"bind\");\n\n if (bind.length > 0) {\n var jidNode = bind[0].getElementsByTagName(\"jid\");\n\n if (jidNode.length > 0) {\n this.jid = Strophe.getText(jidNode[0]);\n\n if (this.do_session) {\n this._establishSession();\n } else {\n this.authenticated = true;\n\n this._changeConnectStatus(Strophe.Status.CONNECTED, null);\n }\n }\n } else {\n Strophe.warn(\"Resource binding failed.\");\n\n this._changeConnectStatus(Strophe.Status.AUTHFAIL, null, elem);\n\n return false;\n }\n },\n\n /** PrivateFunction: _establishSession\n * Send IQ request to establish a session with the XMPP server.\n *\n * See https://xmpp.org/rfcs/rfc3921.html#session\n *\n * Note: The protocol for session establishment has been determined as\n * unnecessary and removed in RFC-6121.\n */\n _establishSession: function _establishSession() {\n if (!this.do_session) {\n throw new Error(\"Strophe.Connection.prototype._establishSession \" + \"called but apparently \".concat(Strophe.NS.SESSION, \" wasn't advertised by the server\"));\n }\n\n this._addSysHandler(this._onSessionResultIQ.bind(this), null, null, null, \"_session_auth_2\");\n\n this.send($iq({\n type: \"set\",\n id: \"_session_auth_2\"\n }).c('session', {\n xmlns: Strophe.NS.SESSION\n }).tree());\n },\n\n /** PrivateFunction: _onSessionResultIQ\n * _Private_ handler for the server's IQ response to a client's session\n * request.\n *\n * This sets Connection.authenticated to true on success, which\n * starts the processing of user handlers.\n *\n * See https://xmpp.org/rfcs/rfc3921.html#session\n *\n * Note: The protocol for session establishment has been determined as\n * unnecessary and removed in RFC-6121.\n *\n * Parameters:\n * (XMLElement) elem - The matching stanza.\n *\n * Returns:\n * false to remove the handler.\n */\n _onSessionResultIQ: function _onSessionResultIQ(elem) {\n if (elem.getAttribute(\"type\") === \"result\") {\n this.authenticated = true;\n\n this._changeConnectStatus(Strophe.Status.CONNECTED, null);\n } else if (elem.getAttribute(\"type\") === \"error\") {\n Strophe.warn(\"Session creation failed.\");\n\n this._changeConnectStatus(Strophe.Status.AUTHFAIL, null, elem);\n\n return false;\n }\n\n return false;\n },\n\n /** PrivateFunction: _sasl_failure_cb\n * _Private_ handler for SASL authentication failure.\n *\n * Parameters:\n * (XMLElement) elem - The matching stanza.\n *\n * Returns:\n * false to remove the handler.\n */\n _sasl_failure_cb: function _sasl_failure_cb(elem) {\n // delete unneeded handlers\n if (this._sasl_success_handler) {\n this.deleteHandler(this._sasl_success_handler);\n this._sasl_success_handler = null;\n }\n\n if (this._sasl_challenge_handler) {\n this.deleteHandler(this._sasl_challenge_handler);\n this._sasl_challenge_handler = null;\n }\n\n if (this._sasl_mechanism) this._sasl_mechanism.onFailure();\n\n this._changeConnectStatus(Strophe.Status.AUTHFAIL, null, elem);\n\n return false;\n },\n\n /** PrivateFunction: _auth2_cb\n * _Private_ handler to finish legacy authentication.\n *\n * This handler is called when the result from the jabber:iq:auth\n * stanza is returned.\n *\n * Parameters:\n * (XMLElement) elem - The stanza that triggered the callback.\n *\n * Returns:\n * false to remove the handler.\n */\n _auth2_cb: function _auth2_cb(elem) {\n if (elem.getAttribute(\"type\") === \"result\") {\n this.authenticated = true;\n\n this._changeConnectStatus(Strophe.Status.CONNECTED, null);\n } else if (elem.getAttribute(\"type\") === \"error\") {\n this._changeConnectStatus(Strophe.Status.AUTHFAIL, null, elem);\n\n this.disconnect('authentication failed');\n }\n\n return false;\n },\n\n /** PrivateFunction: _addSysTimedHandler\n * _Private_ function to add a system level timed handler.\n *\n * This function is used to add a Strophe.TimedHandler for the\n * library code. System timed handlers are allowed to run before\n * authentication is complete.\n *\n * Parameters:\n * (Integer) period - The period of the handler.\n * (Function) handler - The callback function.\n */\n _addSysTimedHandler: function _addSysTimedHandler(period, handler) {\n var thand = new Strophe.TimedHandler(period, handler);\n thand.user = false;\n this.addTimeds.push(thand);\n return thand;\n },\n\n /** PrivateFunction: _addSysHandler\n * _Private_ function to add a system level stanza handler.\n *\n * This function is used to add a Strophe.Handler for the\n * library code. System stanza handlers are allowed to run before\n * authentication is complete.\n *\n * Parameters:\n * (Function) handler - The callback function.\n * (String) ns - The namespace to match.\n * (String) name - The stanza name to match.\n * (String) type - The stanza type attribute to match.\n * (String) id - The stanza id attribute to match.\n */\n _addSysHandler: function _addSysHandler(handler, ns, name, type, id) {\n var hand = new Strophe.Handler(handler, ns, name, type, id);\n hand.user = false;\n this.addHandlers.push(hand);\n return hand;\n },\n\n /** PrivateFunction: _onDisconnectTimeout\n * _Private_ timeout handler for handling non-graceful disconnection.\n *\n * If the graceful disconnect process does not complete within the\n * time allotted, this handler finishes the disconnect anyway.\n *\n * Returns:\n * false to remove the handler.\n */\n _onDisconnectTimeout: function _onDisconnectTimeout() {\n Strophe.debug(\"_onDisconnectTimeout was called\");\n\n this._changeConnectStatus(Strophe.Status.CONNTIMEOUT, null);\n\n this._proto._onDisconnectTimeout(); // actually disconnect\n\n\n this._doDisconnect();\n\n return false;\n },\n\n /** PrivateFunction: _onIdle\n * _Private_ handler to process events during idle cycle.\n *\n * This handler is called every 100ms to fire timed handlers that\n * are ready and keep poll requests going.\n */\n _onIdle: function _onIdle() {\n var _this8 = this;\n\n // add timed handlers scheduled for addition\n // NOTE: we add before remove in the case a timed handler is\n // added and then deleted before the next _onIdle() call.\n while (this.addTimeds.length > 0) {\n this.timedHandlers.push(this.addTimeds.pop());\n } // remove timed handlers that have been scheduled for deletion\n\n\n while (this.removeTimeds.length > 0) {\n var thand = this.removeTimeds.pop();\n var i = this.timedHandlers.indexOf(thand);\n\n if (i >= 0) {\n this.timedHandlers.splice(i, 1);\n }\n } // call ready timed handlers\n\n\n var now = new Date().getTime();\n var newList = [];\n\n for (var _i6 = 0; _i6 < this.timedHandlers.length; _i6++) {\n var _thand = this.timedHandlers[_i6];\n\n if (this.authenticated || !_thand.user) {\n var since = _thand.lastCalled + _thand.period;\n\n if (since - now <= 0) {\n if (_thand.run()) {\n newList.push(_thand);\n }\n } else {\n newList.push(_thand);\n }\n }\n }\n\n this.timedHandlers = newList;\n clearTimeout(this._idleTimeout);\n\n this._proto._onIdle(); // reactivate the timer only if connected\n\n\n if (this.connected) {\n this._idleTimeout = setTimeout(function () {\n return _this8._onIdle();\n }, 100);\n }\n }\n };\n /** Class: Strophe.SASLMechanism\n *\n * encapsulates SASL authentication mechanisms.\n *\n * User code may override the priority for each mechanism or disable it completely.\n * See for information about changing priority and for informatian on\n * how to disable a mechanism.\n *\n * By default, all mechanisms are enabled and the priorities are\n *\n * OAUTHBEARER - 60\n * SCRAM-SHA1 - 50\n * DIGEST-MD5 - 40\n * PLAIN - 30\n * ANONYMOUS - 20\n * EXTERNAL - 10\n *\n * See: Strophe.Connection.addSupportedSASLMechanisms\n */\n\n /**\n * PrivateConstructor: Strophe.SASLMechanism\n * SASL auth mechanism abstraction.\n *\n * Parameters:\n * (String) name - SASL Mechanism name.\n * (Boolean) isClientFirst - If client should send response first without challenge.\n * (Number) priority - Priority.\n *\n * Returns:\n * A new Strophe.SASLMechanism object.\n */\n\n Strophe.SASLMechanism = function (name, isClientFirst, priority) {\n /** PrivateVariable: name\n * Mechanism name.\n */\n this.name = name;\n /** PrivateVariable: isClientFirst\n * If client sends response without initial server challenge.\n */\n\n this.isClientFirst = isClientFirst;\n /** Variable: priority\n * Determines which is chosen for authentication (Higher is better).\n * Users may override this to prioritize mechanisms differently.\n *\n * In the default configuration the priorities are\n *\n * SCRAM-SHA1 - 40\n * DIGEST-MD5 - 30\n * Plain - 20\n *\n * Example: (This will cause Strophe to choose the mechanism that the server sent first)\n *\n * > Strophe.SASLMD5.priority = Strophe.SASLSHA1.priority;\n *\n * See for a list of available mechanisms.\n *\n */\n\n this.priority = priority;\n };\n\n Strophe.SASLMechanism.prototype = {\n /**\n * Function: test\n * Checks if mechanism able to run.\n * To disable a mechanism, make this return false;\n *\n * To disable plain authentication run\n * > Strophe.SASLPlain.test = function() {\n * > return false;\n * > }\n *\n * See for a list of available mechanisms.\n *\n * Parameters:\n * (Strophe.Connection) connection - Target Connection.\n *\n * Returns:\n * (Boolean) If mechanism was able to run.\n */\n test: function test(connection) {\n return true;\n },\n\n /** PrivateFunction: onStart\n * Called before starting mechanism on some connection.\n *\n * Parameters:\n * (Strophe.Connection) connection - Target Connection.\n */\n onStart: function onStart(connection) {\n this._connection = connection;\n },\n\n /** PrivateFunction: onChallenge\n * Called by protocol implementation on incoming challenge. If client is\n * first (isClientFirst === true) challenge will be null on the first call.\n *\n * Parameters:\n * (Strophe.Connection) connection - Target Connection.\n * (String) challenge - current challenge to handle.\n *\n * Returns:\n * (String) Mechanism response.\n */\n onChallenge: function onChallenge(connection, challenge) {\n throw new Error(\"You should implement challenge handling!\");\n },\n\n /** PrivateFunction: onFailure\n * Protocol informs mechanism implementation about SASL failure.\n */\n onFailure: function onFailure() {\n this._connection = null;\n },\n\n /** PrivateFunction: onSuccess\n * Protocol informs mechanism implementation about SASL success.\n */\n onSuccess: function onSuccess() {\n this._connection = null;\n }\n };\n /** Constants: SASL mechanisms\n * Available authentication mechanisms\n *\n * Strophe.SASLAnonymous - SASL ANONYMOUS authentication.\n * Strophe.SASLPlain - SASL PLAIN authentication.\n * Strophe.SASLMD5 - SASL DIGEST-MD5 authentication\n * Strophe.SASLSHA1 - SASL SCRAM-SHA1 authentication\n * Strophe.SASLOAuthBearer - SASL OAuth Bearer authentication\n * Strophe.SASLExternal - SASL EXTERNAL authentication\n * Strophe.SASLXOAuth2 - SASL X-OAuth2 authentication\n */\n // Building SASL callbacks\n\n /** PrivateConstructor: SASLAnonymous\n * SASL ANONYMOUS authentication.\n */\n\n Strophe.SASLAnonymous = function () {};\n\n Strophe.SASLAnonymous.prototype = new Strophe.SASLMechanism(\"ANONYMOUS\", false, 20);\n\n Strophe.SASLAnonymous.prototype.test = function (connection) {\n return connection.authcid === null;\n };\n /** PrivateConstructor: SASLPlain\n * SASL PLAIN authentication.\n */\n\n\n Strophe.SASLPlain = function () {};\n\n Strophe.SASLPlain.prototype = new Strophe.SASLMechanism(\"PLAIN\", true, 50);\n\n Strophe.SASLPlain.prototype.test = function (connection) {\n return connection.authcid !== null;\n };\n\n Strophe.SASLPlain.prototype.onChallenge = function (connection) {\n var auth_str = connection.authzid;\n auth_str = auth_str + \"\\0\";\n auth_str = auth_str + connection.authcid;\n auth_str = auth_str + \"\\0\";\n auth_str = auth_str + connection.pass;\n return utils.utf16to8(auth_str);\n };\n /** PrivateConstructor: SASLSHA1\n * SASL SCRAM SHA 1 authentication.\n */\n\n\n Strophe.SASLSHA1 = function () {};\n\n Strophe.SASLSHA1.prototype = new Strophe.SASLMechanism(\"SCRAM-SHA-1\", true, 70);\n\n Strophe.SASLSHA1.prototype.test = function (connection) {\n return connection.authcid !== null;\n };\n\n Strophe.SASLSHA1.prototype.onChallenge = function (connection, challenge, test_cnonce) {\n var cnonce = test_cnonce || MD5.hexdigest(\"\" + Math.random() * 1234567890);\n var auth_str = \"n=\" + utils.utf16to8(connection.authcid);\n auth_str += \",r=\";\n auth_str += cnonce;\n connection._sasl_data.cnonce = cnonce;\n connection._sasl_data[\"client-first-message-bare\"] = auth_str;\n auth_str = \"n,,\" + auth_str;\n\n this.onChallenge = function (connection, challenge) {\n var nonce, salt, iter, Hi, U, U_old, i, k;\n var responseText = \"c=biws,\";\n var authMessage = \"\".concat(connection._sasl_data[\"client-first-message-bare\"], \",\").concat(challenge, \",\");\n var cnonce = connection._sasl_data.cnonce;\n var attribMatch = /([a-z]+)=([^,]+)(,|$)/;\n\n while (challenge.match(attribMatch)) {\n var matches = challenge.match(attribMatch);\n challenge = challenge.replace(matches[0], \"\");\n\n switch (matches[1]) {\n case \"r\":\n nonce = matches[2];\n break;\n\n case \"s\":\n salt = matches[2];\n break;\n\n case \"i\":\n iter = matches[2];\n break;\n }\n }\n\n if (nonce.substr(0, cnonce.length) !== cnonce) {\n connection._sasl_data = {};\n return connection._sasl_failure_cb();\n }\n\n responseText += \"r=\" + nonce;\n authMessage += responseText;\n salt = atob(salt);\n salt += \"\\x00\\x00\\x00\\x01\";\n var pass = utils.utf16to8(connection.pass);\n Hi = U_old = SHA1.core_hmac_sha1(pass, salt);\n\n for (i = 1; i < iter; i++) {\n U = SHA1.core_hmac_sha1(pass, SHA1.binb2str(U_old));\n\n for (k = 0; k < 5; k++) {\n Hi[k] ^= U[k];\n }\n\n U_old = U;\n }\n\n Hi = SHA1.binb2str(Hi);\n var clientKey = SHA1.core_hmac_sha1(Hi, \"Client Key\");\n var serverKey = SHA1.str_hmac_sha1(Hi, \"Server Key\");\n var clientSignature = SHA1.core_hmac_sha1(SHA1.str_sha1(SHA1.binb2str(clientKey)), authMessage);\n connection._sasl_data[\"server-signature\"] = SHA1.b64_hmac_sha1(serverKey, authMessage);\n\n for (k = 0; k < 5; k++) {\n clientKey[k] ^= clientSignature[k];\n }\n\n responseText += \",p=\" + btoa(SHA1.binb2str(clientKey));\n return responseText;\n };\n\n return auth_str;\n };\n /** PrivateConstructor: SASLMD5\n * SASL DIGEST MD5 authentication.\n */\n\n\n Strophe.SASLMD5 = function () {};\n\n Strophe.SASLMD5.prototype = new Strophe.SASLMechanism(\"DIGEST-MD5\", false, 60);\n\n Strophe.SASLMD5.prototype.test = function (connection) {\n return connection.authcid !== null;\n };\n /** PrivateFunction: _quote\n * _Private_ utility function to backslash escape and quote strings.\n *\n * Parameters:\n * (String) str - The string to be quoted.\n *\n * Returns:\n * quoted string\n */\n\n\n Strophe.SASLMD5.prototype._quote = function (str) {\n return '\"' + str.replace(/\\\\/g, \"\\\\\\\\\").replace(/\"/g, '\\\\\"') + '\"'; //\" end string workaround for emacs\n };\n\n Strophe.SASLMD5.prototype.onChallenge = function (connection, challenge, test_cnonce) {\n var attribMatch = /([a-z]+)=(\"[^\"]+\"|[^,\"]+)(?:,|$)/;\n var cnonce = test_cnonce || MD5.hexdigest(\"\" + Math.random() * 1234567890);\n var realm = \"\";\n var host = null;\n var nonce = \"\";\n var qop = \"\";\n\n while (challenge.match(attribMatch)) {\n var matches = challenge.match(attribMatch);\n challenge = challenge.replace(matches[0], \"\");\n matches[2] = matches[2].replace(/^\"(.+)\"$/, \"$1\");\n\n switch (matches[1]) {\n case \"realm\":\n realm = matches[2];\n break;\n\n case \"nonce\":\n nonce = matches[2];\n break;\n\n case \"qop\":\n qop = matches[2];\n break;\n\n case \"host\":\n host = matches[2];\n break;\n }\n }\n\n var digest_uri = connection.servtype + \"/\" + connection.domain;\n\n if (host !== null) {\n digest_uri = digest_uri + \"/\" + host;\n }\n\n var cred = utils.utf16to8(connection.authcid + \":\" + realm + \":\" + this._connection.pass);\n var A1 = MD5.hash(cred) + \":\" + nonce + \":\" + cnonce;\n var A2 = 'AUTHENTICATE:' + digest_uri;\n var responseText = \"\";\n responseText += 'charset=utf-8,';\n responseText += 'username=' + this._quote(utils.utf16to8(connection.authcid)) + ',';\n responseText += 'realm=' + this._quote(realm) + ',';\n responseText += 'nonce=' + this._quote(nonce) + ',';\n responseText += 'nc=00000001,';\n responseText += 'cnonce=' + this._quote(cnonce) + ',';\n responseText += 'digest-uri=' + this._quote(digest_uri) + ',';\n responseText += 'response=' + MD5.hexdigest(MD5.hexdigest(A1) + \":\" + nonce + \":00000001:\" + cnonce + \":auth:\" + MD5.hexdigest(A2)) + \",\";\n responseText += 'qop=auth';\n\n this.onChallenge = function () {\n return \"\";\n };\n\n return responseText;\n };\n /** PrivateConstructor: SASLOAuthBearer\n * SASL OAuth Bearer authentication.\n */\n\n\n Strophe.SASLOAuthBearer = function () {};\n\n Strophe.SASLOAuthBearer.prototype = new Strophe.SASLMechanism(\"OAUTHBEARER\", true, 40);\n\n Strophe.SASLOAuthBearer.prototype.test = function (connection) {\n return connection.pass !== null;\n };\n\n Strophe.SASLOAuthBearer.prototype.onChallenge = function (connection) {\n var auth_str = 'n,';\n\n if (connection.authcid !== null) {\n auth_str = auth_str + 'a=' + connection.authzid;\n }\n\n auth_str = auth_str + ',';\n auth_str = auth_str + \"\\x01\";\n auth_str = auth_str + 'auth=Bearer ';\n auth_str = auth_str + connection.pass;\n auth_str = auth_str + \"\\x01\";\n auth_str = auth_str + \"\\x01\";\n return utils.utf16to8(auth_str);\n };\n /** PrivateConstructor: SASLExternal\n * SASL EXTERNAL authentication.\n *\n * The EXTERNAL mechanism allows a client to request the server to use\n * credentials established by means external to the mechanism to\n * authenticate the client. The external means may be, for instance,\n * TLS services.\n */\n\n\n Strophe.SASLExternal = function () {};\n\n Strophe.SASLExternal.prototype = new Strophe.SASLMechanism(\"EXTERNAL\", true, 10);\n\n Strophe.SASLExternal.prototype.onChallenge = function (connection) {\n /** According to XEP-178, an authzid SHOULD NOT be presented when the\n * authcid contained or implied in the client certificate is the JID (i.e.\n * authzid) with which the user wants to log in as.\n *\n * To NOT send the authzid, the user should therefore set the authcid equal\n * to the JID when instantiating a new Strophe.Connection object.\n */\n return connection.authcid === connection.authzid ? '' : connection.authzid;\n };\n /** PrivateConstructor: SASLXOAuth2\n * SASL X-OAuth2 authentication.\n */\n\n\n Strophe.SASLXOAuth2 = function () {};\n\n Strophe.SASLXOAuth2.prototype = new Strophe.SASLMechanism(\"X-OAUTH2\", true, 30);\n\n Strophe.SASLXOAuth2.prototype.test = function (connection) {\n return connection.pass !== null;\n };\n\n Strophe.SASLXOAuth2.prototype.onChallenge = function (connection) {\n var auth_str = \"\\0\";\n\n if (connection.authcid !== null) {\n auth_str = auth_str + connection.authzid;\n }\n\n auth_str = auth_str + \"\\0\";\n auth_str = auth_str + connection.pass;\n return utils.utf16to8(auth_str);\n };\n var core = {\n 'Strophe': Strophe,\n '$build': $build,\n '$iq': $iq,\n '$msg': $msg,\n '$pres': $pres,\n 'SHA1': SHA1,\n 'MD5': MD5,\n 'b64_hmac_sha1': SHA1.b64_hmac_sha1,\n 'b64_sha1': SHA1.b64_sha1,\n 'str_hmac_sha1': SHA1.str_hmac_sha1,\n 'str_sha1': SHA1.str_sha1\n };\n\n /*\n This program is distributed under the terms of the MIT license.\n Please see the LICENSE file for details.\n\n Copyright 2006-2008, OGG, LLC\n */\n var Strophe$1 = core.Strophe;\n var $build$1 = core.$build;\n /** PrivateClass: Strophe.Request\n * _Private_ helper class that provides a cross implementation abstraction\n * for a BOSH related XMLHttpRequest.\n *\n * The Strophe.Request class is used internally to encapsulate BOSH request\n * information. It is not meant to be used from user's code.\n */\n\n /** PrivateConstructor: Strophe.Request\n * Create and initialize a new Strophe.Request object.\n *\n * Parameters:\n * (XMLElement) elem - The XML data to be sent in the request.\n * (Function) func - The function that will be called when the\n * XMLHttpRequest readyState changes.\n * (Integer) rid - The BOSH rid attribute associated with this request.\n * (Integer) sends - The number of times this same request has been sent.\n */\n\n Strophe$1.Request = function (elem, func, rid, sends) {\n this.id = ++Strophe$1._requestId;\n this.xmlData = elem;\n this.data = Strophe$1.serialize(elem); // save original function in case we need to make a new request\n // from this one.\n\n this.origFunc = func;\n this.func = func;\n this.rid = rid;\n this.date = NaN;\n this.sends = sends || 0;\n this.abort = false;\n this.dead = null;\n\n this.age = function () {\n if (!this.date) {\n return 0;\n }\n\n var now = new Date();\n return (now - this.date) / 1000;\n };\n\n this.timeDead = function () {\n if (!this.dead) {\n return 0;\n }\n\n var now = new Date();\n return (now - this.dead) / 1000;\n };\n\n this.xhr = this._newXHR();\n };\n\n Strophe$1.Request.prototype = {\n /** PrivateFunction: getResponse\n * Get a response from the underlying XMLHttpRequest.\n *\n * This function attempts to get a response from the request and checks\n * for errors.\n *\n * Throws:\n * \"parsererror\" - A parser error occured.\n * \"bad-format\" - The entity has sent XML that cannot be processed.\n *\n * Returns:\n * The DOM element tree of the response.\n */\n getResponse: function getResponse() {\n var node = null;\n\n if (this.xhr.responseXML && this.xhr.responseXML.documentElement) {\n node = this.xhr.responseXML.documentElement;\n\n if (node.tagName === \"parsererror\") {\n Strophe$1.error(\"invalid response received\");\n Strophe$1.error(\"responseText: \" + this.xhr.responseText);\n Strophe$1.error(\"responseXML: \" + Strophe$1.serialize(this.xhr.responseXML));\n throw new Error(\"parsererror\");\n }\n } else if (this.xhr.responseText) {\n // In React Native, we may get responseText but no responseXML. We can try to parse it manually.\n Strophe$1.debug(\"Got responseText but no responseXML; attempting to parse it with DOMParser...\");\n node = new DOMParser().parseFromString(this.xhr.responseText, 'application/xml').documentElement;\n\n if (!node) {\n throw new Error('Parsing produced null node');\n } else if (node.querySelector('parsererror')) {\n Strophe$1.error(\"invalid response received: \" + node.querySelector('parsererror').textContent);\n Strophe$1.error(\"responseText: \" + this.xhr.responseText);\n var error = new Error();\n error.name = Strophe$1.ErrorCondition.BAD_FORMAT;\n throw error;\n }\n }\n\n return node;\n },\n\n /** PrivateFunction: _newXHR\n * _Private_ helper function to create XMLHttpRequests.\n *\n * This function creates XMLHttpRequests across all implementations.\n *\n * Returns:\n * A new XMLHttpRequest.\n */\n _newXHR: function _newXHR() {\n var xhr = null;\n\n if (window.XMLHttpRequest) {\n xhr = new XMLHttpRequest();\n\n if (xhr.overrideMimeType) {\n xhr.overrideMimeType(\"text/xml; charset=utf-8\");\n }\n } else if (window.ActiveXObject) {\n xhr = new ActiveXObject(\"Microsoft.XMLHTTP\");\n } // use Function.bind() to prepend ourselves as an argument\n\n\n xhr.onreadystatechange = this.func.bind(null, this);\n return xhr;\n }\n };\n /** Class: Strophe.Bosh\n * _Private_ helper class that handles BOSH Connections\n *\n * The Strophe.Bosh class is used internally by Strophe.Connection\n * to encapsulate BOSH sessions. It is not meant to be used from user's code.\n */\n\n /** File: bosh.js\n * A JavaScript library to enable BOSH in Strophejs.\n *\n * this library uses Bidirectional-streams Over Synchronous HTTP (BOSH)\n * to emulate a persistent, stateful, two-way connection to an XMPP server.\n * More information on BOSH can be found in XEP 124.\n */\n\n /** PrivateConstructor: Strophe.Bosh\n * Create and initialize a Strophe.Bosh object.\n *\n * Parameters:\n * (Strophe.Connection) connection - The Strophe.Connection that will use BOSH.\n *\n * Returns:\n * A new Strophe.Bosh object.\n */\n\n Strophe$1.Bosh = function (connection) {\n this._conn = connection;\n /* request id for body tags */\n\n this.rid = Math.floor(Math.random() * 4294967295);\n /* The current session ID. */\n\n this.sid = null; // default BOSH values\n\n this.hold = 1;\n this.wait = 60;\n this.window = 5;\n this.errors = 0;\n this.inactivity = null;\n this.lastResponseHeaders = null;\n this._requests = [];\n };\n\n Strophe$1.Bosh.prototype = {\n /** Variable: strip\n *\n * BOSH-Connections will have all stanzas wrapped in a tag when\n * passed to or .\n * To strip this tag, User code can set to \"body\":\n *\n * > Strophe.Bosh.prototype.strip = \"body\";\n *\n * This will enable stripping of the body tag in both\n * and .\n */\n strip: null,\n\n /** PrivateFunction: _buildBody\n * _Private_ helper function to generate the wrapper for BOSH.\n *\n * Returns:\n * A Strophe.Builder with a element.\n */\n _buildBody: function _buildBody() {\n var bodyWrap = $build$1('body', {\n 'rid': this.rid++,\n 'xmlns': Strophe$1.NS.HTTPBIND\n });\n\n if (this.sid !== null) {\n bodyWrap.attrs({\n 'sid': this.sid\n });\n }\n\n if (this._conn.options.keepalive && this._conn._sessionCachingSupported()) {\n this._cacheSession();\n }\n\n return bodyWrap;\n },\n\n /** PrivateFunction: _reset\n * Reset the connection.\n *\n * This function is called by the reset function of the Strophe Connection\n */\n _reset: function _reset() {\n this.rid = Math.floor(Math.random() * 4294967295);\n this.sid = null;\n this.errors = 0;\n\n if (this._conn._sessionCachingSupported()) {\n window.sessionStorage.removeItem('strophe-bosh-session');\n }\n\n this._conn.nextValidRid(this.rid);\n },\n\n /** PrivateFunction: _connect\n * _Private_ function that initializes the BOSH connection.\n *\n * Creates and sends the Request that initializes the BOSH connection.\n */\n _connect: function _connect(wait, hold, route) {\n this.wait = wait || this.wait;\n this.hold = hold || this.hold;\n this.errors = 0;\n\n var body = this._buildBody().attrs({\n \"to\": this._conn.domain,\n \"xml:lang\": \"en\",\n \"wait\": this.wait,\n \"hold\": this.hold,\n \"content\": \"text/xml; charset=utf-8\",\n \"ver\": \"1.6\",\n \"xmpp:version\": \"1.0\",\n \"xmlns:xmpp\": Strophe$1.NS.BOSH\n });\n\n if (route) {\n body.attrs({\n 'route': route\n });\n }\n\n var _connect_cb = this._conn._connect_cb;\n\n this._requests.push(new Strophe$1.Request(body.tree(), this._onRequestStateChange.bind(this, _connect_cb.bind(this._conn)), body.tree().getAttribute(\"rid\")));\n\n this._throttledRequestHandler();\n },\n\n /** PrivateFunction: _attach\n * Attach to an already created and authenticated BOSH session.\n *\n * This function is provided to allow Strophe to attach to BOSH\n * sessions which have been created externally, perhaps by a Web\n * application. This is often used to support auto-login type features\n * without putting user credentials into the page.\n *\n * Parameters:\n * (String) jid - The full JID that is bound by the session.\n * (String) sid - The SID of the BOSH session.\n * (String) rid - The current RID of the BOSH session. This RID\n * will be used by the next request.\n * (Function) callback The connect callback function.\n * (Integer) wait - The optional HTTPBIND wait value. This is the\n * time the server will wait before returning an empty result for\n * a request. The default setting of 60 seconds is recommended.\n * Other settings will require tweaks to the Strophe.TIMEOUT value.\n * (Integer) hold - The optional HTTPBIND hold value. This is the\n * number of connections the server will hold at one time. This\n * should almost always be set to 1 (the default).\n * (Integer) wind - The optional HTTBIND window value. This is the\n * allowed range of request ids that are valid. The default is 5.\n */\n _attach: function _attach(jid, sid, rid, callback, wait, hold, wind) {\n this._conn.jid = jid;\n this.sid = sid;\n this.rid = rid;\n this._conn.connect_callback = callback;\n this._conn.domain = Strophe$1.getDomainFromJid(this._conn.jid);\n this._conn.authenticated = true;\n this._conn.connected = true;\n this.wait = wait || this.wait;\n this.hold = hold || this.hold;\n this.window = wind || this.window;\n\n this._conn._changeConnectStatus(Strophe$1.Status.ATTACHED, null);\n },\n\n /** PrivateFunction: _restore\n * Attempt to restore a cached BOSH session\n *\n * Parameters:\n * (String) jid - The full JID that is bound by the session.\n * This parameter is optional but recommended, specifically in cases\n * where prebinded BOSH sessions are used where it's important to know\n * that the right session is being restored.\n * (Function) callback The connect callback function.\n * (Integer) wait - The optional HTTPBIND wait value. This is the\n * time the server will wait before returning an empty result for\n * a request. The default setting of 60 seconds is recommended.\n * Other settings will require tweaks to the Strophe.TIMEOUT value.\n * (Integer) hold - The optional HTTPBIND hold value. This is the\n * number of connections the server will hold at one time. This\n * should almost always be set to 1 (the default).\n * (Integer) wind - The optional HTTBIND window value. This is the\n * allowed range of request ids that are valid. The default is 5.\n */\n _restore: function _restore(jid, callback, wait, hold, wind) {\n var session = JSON.parse(window.sessionStorage.getItem('strophe-bosh-session'));\n\n if (typeof session !== \"undefined\" && session !== null && session.rid && session.sid && session.jid && (typeof jid === \"undefined\" || jid === null || Strophe$1.getBareJidFromJid(session.jid) === Strophe$1.getBareJidFromJid(jid) || // If authcid is null, then it's an anonymous login, so\n // we compare only the domains:\n Strophe$1.getNodeFromJid(jid) === null && Strophe$1.getDomainFromJid(session.jid) === jid)) {\n this._conn.restored = true;\n\n this._attach(session.jid, session.sid, session.rid, callback, wait, hold, wind);\n } else {\n var error = new Error(\"_restore: no restoreable session.\");\n error.name = \"StropheSessionError\";\n throw error;\n }\n },\n\n /** PrivateFunction: _cacheSession\n * _Private_ handler for the beforeunload event.\n *\n * This handler is used to process the Bosh-part of the initial request.\n * Parameters:\n * (Strophe.Request) bodyWrap - The received stanza.\n */\n _cacheSession: function _cacheSession() {\n if (this._conn.authenticated) {\n if (this._conn.jid && this.rid && this.sid) {\n window.sessionStorage.setItem('strophe-bosh-session', JSON.stringify({\n 'jid': this._conn.jid,\n 'rid': this.rid,\n 'sid': this.sid\n }));\n }\n } else {\n window.sessionStorage.removeItem('strophe-bosh-session');\n }\n },\n\n /** PrivateFunction: _connect_cb\n * _Private_ handler for initial connection request.\n *\n * This handler is used to process the Bosh-part of the initial request.\n * Parameters:\n * (Strophe.Request) bodyWrap - The received stanza.\n */\n _connect_cb: function _connect_cb(bodyWrap) {\n var typ = bodyWrap.getAttribute(\"type\");\n\n if (typ !== null && typ === \"terminate\") {\n // an error occurred\n var cond = bodyWrap.getAttribute(\"condition\");\n Strophe$1.error(\"BOSH-Connection failed: \" + cond);\n var conflict = bodyWrap.getElementsByTagName(\"conflict\");\n\n if (cond !== null) {\n if (cond === \"remote-stream-error\" && conflict.length > 0) {\n cond = \"conflict\";\n }\n\n this._conn._changeConnectStatus(Strophe$1.Status.CONNFAIL, cond);\n } else {\n this._conn._changeConnectStatus(Strophe$1.Status.CONNFAIL, \"unknown\");\n }\n\n this._conn._doDisconnect(cond);\n\n return Strophe$1.Status.CONNFAIL;\n } // check to make sure we don't overwrite these if _connect_cb is\n // called multiple times in the case of missing stream:features\n\n\n if (!this.sid) {\n this.sid = bodyWrap.getAttribute(\"sid\");\n }\n\n var wind = bodyWrap.getAttribute('requests');\n\n if (wind) {\n this.window = parseInt(wind, 10);\n }\n\n var hold = bodyWrap.getAttribute('hold');\n\n if (hold) {\n this.hold = parseInt(hold, 10);\n }\n\n var wait = bodyWrap.getAttribute('wait');\n\n if (wait) {\n this.wait = parseInt(wait, 10);\n }\n\n var inactivity = bodyWrap.getAttribute('inactivity');\n\n if (inactivity) {\n this.inactivity = parseInt(inactivity, 10);\n }\n },\n\n /** PrivateFunction: _disconnect\n * _Private_ part of Connection.disconnect for Bosh\n *\n * Parameters:\n * (Request) pres - This stanza will be sent before disconnecting.\n */\n _disconnect: function _disconnect(pres) {\n this._sendTerminate(pres);\n },\n\n /** PrivateFunction: _doDisconnect\n * _Private_ function to disconnect.\n *\n * Resets the SID and RID.\n */\n _doDisconnect: function _doDisconnect() {\n this.sid = null;\n this.rid = Math.floor(Math.random() * 4294967295);\n\n if (this._conn._sessionCachingSupported()) {\n window.sessionStorage.removeItem('strophe-bosh-session');\n }\n\n this._conn.nextValidRid(this.rid);\n },\n\n /** PrivateFunction: _emptyQueue\n * _Private_ function to check if the Request queue is empty.\n *\n * Returns:\n * True, if there are no Requests queued, False otherwise.\n */\n _emptyQueue: function _emptyQueue() {\n return this._requests.length === 0;\n },\n\n /** PrivateFunction: _callProtocolErrorHandlers\n * _Private_ function to call error handlers registered for HTTP errors.\n *\n * Parameters:\n * (Strophe.Request) req - The request that is changing readyState.\n */\n _callProtocolErrorHandlers: function _callProtocolErrorHandlers(req) {\n var reqStatus = this._getRequestStatus(req);\n\n var err_callback = this._conn.protocolErrorHandlers.HTTP[reqStatus];\n\n if (err_callback) {\n err_callback.call(this, reqStatus);\n }\n },\n\n /** PrivateFunction: _hitError\n * _Private_ function to handle the error count.\n *\n * Requests are resent automatically until their error count reaches\n * 5. Each time an error is encountered, this function is called to\n * increment the count and disconnect if the count is too high.\n *\n * Parameters:\n * (Integer) reqStatus - The request status.\n */\n _hitError: function _hitError(reqStatus) {\n this.errors++;\n Strophe$1.warn(\"request errored, status: \" + reqStatus + \", number of errors: \" + this.errors);\n\n if (this.errors > 4) {\n this._conn._onDisconnectTimeout();\n }\n },\n\n /** PrivateFunction: _no_auth_received\n *\n * Called on stream start/restart when no stream:features\n * has been received and sends a blank poll request.\n */\n _no_auth_received: function _no_auth_received(callback) {\n Strophe$1.warn(\"Server did not yet offer a supported authentication \" + \"mechanism. Sending a blank poll request.\");\n\n if (callback) {\n callback = callback.bind(this._conn);\n } else {\n callback = this._conn._connect_cb.bind(this._conn);\n }\n\n var body = this._buildBody();\n\n this._requests.push(new Strophe$1.Request(body.tree(), this._onRequestStateChange.bind(this, callback), body.tree().getAttribute(\"rid\")));\n\n this._throttledRequestHandler();\n },\n\n /** PrivateFunction: _onDisconnectTimeout\n * _Private_ timeout handler for handling non-graceful disconnection.\n *\n * Cancels all remaining Requests and clears the queue.\n */\n _onDisconnectTimeout: function _onDisconnectTimeout() {\n this._abortAllRequests();\n },\n\n /** PrivateFunction: _abortAllRequests\n * _Private_ helper function that makes sure all pending requests are aborted.\n */\n _abortAllRequests: function _abortAllRequests() {\n while (this._requests.length > 0) {\n var req = this._requests.pop();\n\n req.abort = true;\n req.xhr.abort();\n\n req.xhr.onreadystatechange = function () {};\n }\n },\n\n /** PrivateFunction: _onIdle\n * _Private_ handler called by Strophe.Connection._onIdle\n *\n * Sends all queued Requests or polls with empty Request if there are none.\n */\n _onIdle: function _onIdle() {\n var data = this._conn._data; // if no requests are in progress, poll\n\n if (this._conn.authenticated && this._requests.length === 0 && data.length === 0 && !this._conn.disconnecting) {\n Strophe$1.debug(\"no requests during idle cycle, sending blank request\");\n data.push(null);\n }\n\n if (this._conn.paused) {\n return;\n }\n\n if (this._requests.length < 2 && data.length > 0) {\n var body = this._buildBody();\n\n for (var i = 0; i < data.length; i++) {\n if (data[i] !== null) {\n if (data[i] === \"restart\") {\n body.attrs({\n \"to\": this._conn.domain,\n \"xml:lang\": \"en\",\n \"xmpp:restart\": \"true\",\n \"xmlns:xmpp\": Strophe$1.NS.BOSH\n });\n } else {\n body.cnode(data[i]).up();\n }\n }\n }\n\n delete this._conn._data;\n this._conn._data = [];\n\n this._requests.push(new Strophe$1.Request(body.tree(), this._onRequestStateChange.bind(this, this._conn._dataRecv.bind(this._conn)), body.tree().getAttribute(\"rid\")));\n\n this._throttledRequestHandler();\n }\n\n if (this._requests.length > 0) {\n var time_elapsed = this._requests[0].age();\n\n if (this._requests[0].dead !== null) {\n if (this._requests[0].timeDead() > Math.floor(Strophe$1.SECONDARY_TIMEOUT * this.wait)) {\n this._throttledRequestHandler();\n }\n }\n\n if (time_elapsed > Math.floor(Strophe$1.TIMEOUT * this.wait)) {\n Strophe$1.warn(\"Request \" + this._requests[0].id + \" timed out, over \" + Math.floor(Strophe$1.TIMEOUT * this.wait) + \" seconds since last activity\");\n\n this._throttledRequestHandler();\n }\n }\n },\n\n /** PrivateFunction: _getRequestStatus\n *\n * Returns the HTTP status code from a Strophe.Request\n *\n * Parameters:\n * (Strophe.Request) req - The Strophe.Request instance.\n * (Integer) def - The default value that should be returned if no\n * status value was found.\n */\n _getRequestStatus: function _getRequestStatus(req, def) {\n var reqStatus;\n\n if (req.xhr.readyState === 4) {\n try {\n reqStatus = req.xhr.status;\n } catch (e) {\n // ignore errors from undefined status attribute. Works\n // around a browser bug\n Strophe$1.error(\"Caught an error while retrieving a request's status, \" + \"reqStatus: \" + reqStatus);\n }\n }\n\n if (typeof reqStatus === \"undefined\") {\n reqStatus = typeof def === 'number' ? def : 0;\n }\n\n return reqStatus;\n },\n\n /** PrivateFunction: _onRequestStateChange\n * _Private_ handler for Strophe.Request state changes.\n *\n * This function is called when the XMLHttpRequest readyState changes.\n * It contains a lot of error handling logic for the many ways that\n * requests can fail, and calls the request callback when requests\n * succeed.\n *\n * Parameters:\n * (Function) func - The handler for the request.\n * (Strophe.Request) req - The request that is changing readyState.\n */\n _onRequestStateChange: function _onRequestStateChange(func, req) {\n Strophe$1.debug(\"request id \" + req.id + \".\" + req.sends + \" state changed to \" + req.xhr.readyState);\n\n if (req.abort) {\n req.abort = false;\n return;\n }\n\n if (req.xhr.readyState !== 4) {\n // The request is not yet complete\n return;\n }\n\n var reqStatus = this._getRequestStatus(req);\n\n this.lastResponseHeaders = req.xhr.getAllResponseHeaders();\n\n if (this.disconnecting && reqStatus >= 400) {\n this._hitError(reqStatus);\n\n this._callProtocolErrorHandlers(req);\n\n return;\n }\n\n var valid_request = reqStatus > 0 && reqStatus < 500;\n var too_many_retries = req.sends > this._conn.maxRetries;\n\n if (valid_request || too_many_retries) {\n // remove from internal queue\n this._removeRequest(req);\n\n Strophe$1.debug(\"request id \" + req.id + \" should now be removed\");\n }\n\n if (reqStatus === 200) {\n // request succeeded\n var reqIs0 = this._requests[0] === req;\n var reqIs1 = this._requests[1] === req; // if request 1 finished, or request 0 finished and request\n // 1 is over Strophe.SECONDARY_TIMEOUT seconds old, we need to\n // restart the other - both will be in the first spot, as the\n // completed request has been removed from the queue already\n\n if (reqIs1 || reqIs0 && this._requests.length > 0 && this._requests[0].age() > Math.floor(Strophe$1.SECONDARY_TIMEOUT * this.wait)) {\n this._restartRequest(0);\n }\n\n this._conn.nextValidRid(Number(req.rid) + 1);\n\n Strophe$1.debug(\"request id \" + req.id + \".\" + req.sends + \" got 200\");\n func(req); // call handler\n\n this.errors = 0;\n } else if (reqStatus === 0 || reqStatus >= 400 && reqStatus < 600 || reqStatus >= 12000) {\n // request failed\n Strophe$1.error(\"request id \" + req.id + \".\" + req.sends + \" error \" + reqStatus + \" happened\");\n\n this._hitError(reqStatus);\n\n this._callProtocolErrorHandlers(req);\n\n if (reqStatus >= 400 && reqStatus < 500) {\n this._conn._changeConnectStatus(Strophe$1.Status.DISCONNECTING, null);\n\n this._conn._doDisconnect();\n }\n } else {\n Strophe$1.error(\"request id \" + req.id + \".\" + req.sends + \" error \" + reqStatus + \" happened\");\n }\n\n if (!valid_request && !too_many_retries) {\n this._throttledRequestHandler();\n } else if (too_many_retries && !this._conn.connected) {\n this._conn._changeConnectStatus(Strophe$1.Status.CONNFAIL, \"giving-up\");\n }\n },\n\n /** PrivateFunction: _processRequest\n * _Private_ function to process a request in the queue.\n *\n * This function takes requests off the queue and sends them and\n * restarts dead requests.\n *\n * Parameters:\n * (Integer) i - The index of the request in the queue.\n */\n _processRequest: function _processRequest(i) {\n var _this = this;\n\n var req = this._requests[i];\n\n var reqStatus = this._getRequestStatus(req, -1); // make sure we limit the number of retries\n\n\n if (req.sends > this._conn.maxRetries) {\n this._conn._onDisconnectTimeout();\n\n return;\n }\n\n var time_elapsed = req.age();\n var primary_timeout = !isNaN(time_elapsed) && time_elapsed > Math.floor(Strophe$1.TIMEOUT * this.wait);\n var secondary_timeout = req.dead !== null && req.timeDead() > Math.floor(Strophe$1.SECONDARY_TIMEOUT * this.wait);\n var server_error = req.xhr.readyState === 4 && (reqStatus < 1 || reqStatus >= 500);\n\n if (primary_timeout || secondary_timeout || server_error) {\n if (secondary_timeout) {\n Strophe$1.error(\"Request \".concat(this._requests[i].id, \" timed out (secondary), restarting\"));\n }\n\n req.abort = true;\n req.xhr.abort(); // setting to null fails on IE6, so set to empty function\n\n req.xhr.onreadystatechange = function () {};\n\n this._requests[i] = new Strophe$1.Request(req.xmlData, req.origFunc, req.rid, req.sends);\n req = this._requests[i];\n }\n\n if (req.xhr.readyState === 0) {\n Strophe$1.debug(\"request id \" + req.id + \".\" + req.sends + \" posting\");\n\n try {\n var content_type = this._conn.options.contentType || \"text/xml; charset=utf-8\";\n req.xhr.open(\"POST\", this._conn.service, this._conn.options.sync ? false : true);\n\n if (typeof req.xhr.setRequestHeader !== 'undefined') {\n // IE9 doesn't have setRequestHeader\n req.xhr.setRequestHeader(\"Content-Type\", content_type);\n }\n\n if (this._conn.options.withCredentials) {\n req.xhr.withCredentials = true;\n }\n } catch (e2) {\n Strophe$1.error(\"XHR open failed: \" + e2.toString());\n\n if (!this._conn.connected) {\n this._conn._changeConnectStatus(Strophe$1.Status.CONNFAIL, \"bad-service\");\n }\n\n this._conn.disconnect();\n\n return;\n } // Fires the XHR request -- may be invoked immediately\n // or on a gradually expanding retry window for reconnects\n\n\n var sendFunc = function sendFunc() {\n req.date = new Date();\n\n if (_this._conn.options.customHeaders) {\n var headers = _this._conn.options.customHeaders;\n\n for (var header in headers) {\n if (Object.prototype.hasOwnProperty.call(headers, header)) {\n req.xhr.setRequestHeader(header, headers[header]);\n }\n }\n }\n\n req.xhr.send(req.data);\n }; // Implement progressive backoff for reconnects --\n // First retry (send === 1) should also be instantaneous\n\n\n if (req.sends > 1) {\n // Using a cube of the retry number creates a nicely\n // expanding retry window\n var backoff = Math.min(Math.floor(Strophe$1.TIMEOUT * this.wait), Math.pow(req.sends, 3)) * 1000;\n setTimeout(function () {\n // XXX: setTimeout should be called only with function expressions (23974bc1)\n sendFunc();\n }, backoff);\n } else {\n sendFunc();\n }\n\n req.sends++;\n\n if (this._conn.xmlOutput !== Strophe$1.Connection.prototype.xmlOutput) {\n if (req.xmlData.nodeName === this.strip && req.xmlData.childNodes.length) {\n this._conn.xmlOutput(req.xmlData.childNodes[0]);\n } else {\n this._conn.xmlOutput(req.xmlData);\n }\n }\n\n if (this._conn.rawOutput !== Strophe$1.Connection.prototype.rawOutput) {\n this._conn.rawOutput(req.data);\n }\n } else {\n Strophe$1.debug(\"_processRequest: \" + (i === 0 ? \"first\" : \"second\") + \" request has readyState of \" + req.xhr.readyState);\n }\n },\n\n /** PrivateFunction: _removeRequest\n * _Private_ function to remove a request from the queue.\n *\n * Parameters:\n * (Strophe.Request) req - The request to remove.\n */\n _removeRequest: function _removeRequest(req) {\n Strophe$1.debug(\"removing request\");\n\n for (var i = this._requests.length - 1; i >= 0; i--) {\n if (req === this._requests[i]) {\n this._requests.splice(i, 1);\n }\n } // IE6 fails on setting to null, so set to empty function\n\n\n req.xhr.onreadystatechange = function () {};\n\n this._throttledRequestHandler();\n },\n\n /** PrivateFunction: _restartRequest\n * _Private_ function to restart a request that is presumed dead.\n *\n * Parameters:\n * (Integer) i - The index of the request in the queue.\n */\n _restartRequest: function _restartRequest(i) {\n var req = this._requests[i];\n\n if (req.dead === null) {\n req.dead = new Date();\n }\n\n this._processRequest(i);\n },\n\n /** PrivateFunction: _reqToData\n * _Private_ function to get a stanza out of a request.\n *\n * Tries to extract a stanza out of a Request Object.\n * When this fails the current connection will be disconnected.\n *\n * Parameters:\n * (Object) req - The Request.\n *\n * Returns:\n * The stanza that was passed.\n */\n _reqToData: function _reqToData(req) {\n try {\n return req.getResponse();\n } catch (e) {\n if (e.message !== \"parsererror\") {\n throw e;\n }\n\n this._conn.disconnect(\"strophe-parsererror\");\n }\n },\n\n /** PrivateFunction: _sendTerminate\n * _Private_ function to send initial disconnect sequence.\n *\n * This is the first step in a graceful disconnect. It sends\n * the BOSH server a terminate body and includes an unavailable\n * presence if authentication has completed.\n */\n _sendTerminate: function _sendTerminate(pres) {\n Strophe$1.debug(\"_sendTerminate was called\");\n\n var body = this._buildBody().attrs({\n type: \"terminate\"\n });\n\n if (pres) {\n body.cnode(pres.tree());\n }\n\n var req = new Strophe$1.Request(body.tree(), this._onRequestStateChange.bind(this, this._conn._dataRecv.bind(this._conn)), body.tree().getAttribute(\"rid\"));\n\n this._requests.push(req);\n\n this._throttledRequestHandler();\n },\n\n /** PrivateFunction: _send\n * _Private_ part of the Connection.send function for BOSH\n *\n * Just triggers the RequestHandler to send the messages that are in the queue\n */\n _send: function _send() {\n var _this2 = this;\n\n clearTimeout(this._conn._idleTimeout);\n\n this._throttledRequestHandler();\n\n this._conn._idleTimeout = setTimeout(function () {\n return _this2._conn._onIdle();\n }, 100);\n },\n\n /** PrivateFunction: _sendRestart\n *\n * Send an xmpp:restart stanza.\n */\n _sendRestart: function _sendRestart() {\n this._throttledRequestHandler();\n\n clearTimeout(this._conn._idleTimeout);\n },\n\n /** PrivateFunction: _throttledRequestHandler\n * _Private_ function to throttle requests to the connection window.\n *\n * This function makes sure we don't send requests so fast that the\n * request ids overflow the connection window in the case that one\n * request died.\n */\n _throttledRequestHandler: function _throttledRequestHandler() {\n if (!this._requests) {\n Strophe$1.debug(\"_throttledRequestHandler called with \" + \"undefined requests\");\n } else {\n Strophe$1.debug(\"_throttledRequestHandler called with \" + this._requests.length + \" requests\");\n }\n\n if (!this._requests || this._requests.length === 0) {\n return;\n }\n\n if (this._requests.length > 0) {\n this._processRequest(0);\n }\n\n if (this._requests.length > 1 && Math.abs(this._requests[0].rid - this._requests[1].rid) < this.window) {\n this._processRequest(1);\n }\n }\n };\n\n /*\n This program is distributed under the terms of the MIT license.\n Please see the LICENSE file for details.\n\n Copyright 2006-2008, OGG, LLC\n */\n var Strophe$2 = core.Strophe;\n var $build$2 = core.$build;\n /** Class: Strophe.WebSocket\n * _Private_ helper class that handles WebSocket Connections\n *\n * The Strophe.WebSocket class is used internally by Strophe.Connection\n * to encapsulate WebSocket sessions. It is not meant to be used from user's code.\n */\n\n /** File: websocket.js\n * A JavaScript library to enable XMPP over Websocket in Strophejs.\n *\n * This file implements XMPP over WebSockets for Strophejs.\n * If a Connection is established with a Websocket url (ws://...)\n * Strophe will use WebSockets.\n * For more information on XMPP-over-WebSocket see RFC 7395:\n * http://tools.ietf.org/html/rfc7395\n *\n * WebSocket support implemented by Andreas Guth (andreas.guth@rwth-aachen.de)\n */\n\n /** PrivateConstructor: Strophe.Websocket\n * Create and initialize a Strophe.WebSocket object.\n * Currently only sets the connection Object.\n *\n * Parameters:\n * (Strophe.Connection) connection - The Strophe.Connection that will use WebSockets.\n *\n * Returns:\n * A new Strophe.WebSocket object.\n */\n\n Strophe$2.Websocket = function (connection) {\n this._conn = connection;\n this.strip = \"wrapper\";\n var service = connection.service;\n\n if (service.indexOf(\"ws:\") !== 0 && service.indexOf(\"wss:\") !== 0) {\n // If the service is not an absolute URL, assume it is a path and put the absolute\n // URL together from options, current URL and the path.\n var new_service = \"\";\n\n if (connection.options.protocol === \"ws\" && window.location.protocol !== \"https:\") {\n new_service += \"ws\";\n } else {\n new_service += \"wss\";\n }\n\n new_service += \"://\" + window.location.host;\n\n if (service.indexOf(\"/\") !== 0) {\n new_service += window.location.pathname + service;\n } else {\n new_service += service;\n }\n\n connection.service = new_service;\n }\n };\n\n Strophe$2.Websocket.prototype = {\n /** PrivateFunction: _buildStream\n * _Private_ helper function to generate the start tag for WebSockets\n *\n * Returns:\n * A Strophe.Builder with a element.\n */\n _buildStream: function _buildStream() {\n return $build$2(\"open\", {\n \"xmlns\": Strophe$2.NS.FRAMING,\n \"to\": this._conn.domain,\n \"version\": '1.0'\n });\n },\n\n /** PrivateFunction: _check_streamerror\n * _Private_ checks a message for stream:error\n *\n * Parameters:\n * (Strophe.Request) bodyWrap - The received stanza.\n * connectstatus - The ConnectStatus that will be set on error.\n * Returns:\n * true if there was a streamerror, false otherwise.\n */\n _check_streamerror: function _check_streamerror(bodyWrap, connectstatus) {\n var errors;\n\n if (bodyWrap.getElementsByTagNameNS) {\n errors = bodyWrap.getElementsByTagNameNS(Strophe$2.NS.STREAM, \"error\");\n } else {\n errors = bodyWrap.getElementsByTagName(\"stream:error\");\n }\n\n if (errors.length === 0) {\n return false;\n }\n\n var error = errors[0];\n var condition = \"\";\n var text = \"\";\n var ns = \"urn:ietf:params:xml:ns:xmpp-streams\";\n\n for (var i = 0; i < error.childNodes.length; i++) {\n var e = error.childNodes[i];\n\n if (e.getAttribute(\"xmlns\") !== ns) {\n break;\n }\n\n if (e.nodeName === \"text\") {\n text = e.textContent;\n } else {\n condition = e.nodeName;\n }\n }\n\n var errorString = \"WebSocket stream error: \";\n\n if (condition) {\n errorString += condition;\n } else {\n errorString += \"unknown\";\n }\n\n if (text) {\n errorString += \" - \" + text;\n }\n\n Strophe$2.error(errorString); // close the connection on stream_error\n\n this._conn._changeConnectStatus(connectstatus, condition);\n\n this._conn._doDisconnect();\n\n return true;\n },\n\n /** PrivateFunction: _reset\n * Reset the connection.\n *\n * This function is called by the reset function of the Strophe Connection.\n * Is not needed by WebSockets.\n */\n _reset: function _reset() {\n return;\n },\n\n /** PrivateFunction: _connect\n * _Private_ function called by Strophe.Connection.connect\n *\n * Creates a WebSocket for a connection and assigns Callbacks to it.\n * Does nothing if there already is a WebSocket.\n */\n _connect: function _connect() {\n // Ensure that there is no open WebSocket from a previous Connection.\n this._closeSocket(); // Create the new WobSocket\n\n\n this.socket = new WebSocket(this._conn.service, \"xmpp\");\n this.socket.onopen = this._onOpen.bind(this);\n this.socket.onerror = this._onError.bind(this);\n this.socket.onclose = this._onClose.bind(this);\n this.socket.onmessage = this._connect_cb_wrapper.bind(this);\n },\n\n /** PrivateFunction: _connect_cb\n * _Private_ function called by Strophe.Connection._connect_cb\n *\n * checks for stream:error\n *\n * Parameters:\n * (Strophe.Request) bodyWrap - The received stanza.\n */\n _connect_cb: function _connect_cb(bodyWrap) {\n var error = this._check_streamerror(bodyWrap, Strophe$2.Status.CONNFAIL);\n\n if (error) {\n return Strophe$2.Status.CONNFAIL;\n }\n },\n\n /** PrivateFunction: _handleStreamStart\n * _Private_ function that checks the opening tag for errors.\n *\n * Disconnects if there is an error and returns false, true otherwise.\n *\n * Parameters:\n * (Node) message - Stanza containing the tag.\n */\n _handleStreamStart: function _handleStreamStart(message) {\n var error = false; // Check for errors in the tag\n\n var ns = message.getAttribute(\"xmlns\");\n\n if (typeof ns !== \"string\") {\n error = \"Missing xmlns in \";\n } else if (ns !== Strophe$2.NS.FRAMING) {\n error = \"Wrong xmlns in : \" + ns;\n }\n\n var ver = message.getAttribute(\"version\");\n\n if (typeof ver !== \"string\") {\n error = \"Missing version in \";\n } else if (ver !== \"1.0\") {\n error = \"Wrong version in : \" + ver;\n }\n\n if (error) {\n this._conn._changeConnectStatus(Strophe$2.Status.CONNFAIL, error);\n\n this._conn._doDisconnect();\n\n return false;\n }\n\n return true;\n },\n\n /** PrivateFunction: _connect_cb_wrapper\n * _Private_ function that handles the first connection messages.\n *\n * On receiving an opening stream tag this callback replaces itself with the real\n * message handler. On receiving a stream error the connection is terminated.\n */\n _connect_cb_wrapper: function _connect_cb_wrapper(message) {\n if (message.data.indexOf(\"\\s*)*/, \"\");\n if (data === '') return;\n var streamStart = new DOMParser().parseFromString(data, \"text/xml\").documentElement;\n\n this._conn.xmlInput(streamStart);\n\n this._conn.rawInput(message.data); //_handleStreamSteart will check for XML errors and disconnect on error\n\n\n if (this._handleStreamStart(streamStart)) {\n //_connect_cb will check for stream:error and disconnect on error\n this._connect_cb(streamStart);\n }\n } else if (message.data.indexOf(\"\n // Parse the raw string to an XML element\n var parsedMessage = new DOMParser().parseFromString(message.data, \"text/xml\").documentElement; // Report this input to the raw and xml handlers\n\n this._conn.xmlInput(parsedMessage);\n\n this._conn.rawInput(message.data);\n\n var see_uri = parsedMessage.getAttribute(\"see-other-uri\");\n\n if (see_uri) {\n var service = this._conn.service; // Valid scenarios: WSS->WSS, WS->ANY\n\n var isSecureRedirect = service.indexOf(\"wss:\") >= 0 && see_uri.indexOf(\"wss:\") >= 0 || service.indexOf(\"ws:\") >= 0;\n\n if (isSecureRedirect) {\n this._conn._changeConnectStatus(Strophe$2.Status.REDIRECT, \"Received see-other-uri, resetting connection\");\n\n this._conn.reset();\n\n this._conn.service = see_uri;\n\n this._connect();\n }\n } else {\n this._conn._changeConnectStatus(Strophe$2.Status.CONNFAIL, \"Received closing stream\");\n\n this._conn._doDisconnect();\n }\n } else {\n var string = this._streamWrap(message.data);\n\n var elem = new DOMParser().parseFromString(string, \"text/xml\").documentElement;\n this.socket.onmessage = this._onMessage.bind(this);\n\n this._conn._connect_cb(elem, null, message.data);\n }\n },\n\n /** PrivateFunction: _disconnect\n * _Private_ function called by Strophe.Connection.disconnect\n *\n * Disconnects and sends a last stanza if one is given\n *\n * Parameters:\n * (Request) pres - This stanza will be sent before disconnecting.\n */\n _disconnect: function _disconnect(pres) {\n if (this.socket && this.socket.readyState !== WebSocket.CLOSED) {\n if (pres) {\n this._conn.send(pres);\n }\n\n var close = $build$2(\"close\", {\n \"xmlns\": Strophe$2.NS.FRAMING\n });\n\n this._conn.xmlOutput(close.tree());\n\n var closeString = Strophe$2.serialize(close);\n\n this._conn.rawOutput(closeString);\n\n try {\n this.socket.send(closeString);\n } catch (e) {\n Strophe$2.warn(\"Couldn't send tag.\");\n }\n }\n\n this._conn._doDisconnect();\n },\n\n /** PrivateFunction: _doDisconnect\n * _Private_ function to disconnect.\n *\n * Just closes the Socket for WebSockets\n */\n _doDisconnect: function _doDisconnect() {\n Strophe$2.debug(\"WebSockets _doDisconnect was called\");\n\n this._closeSocket();\n },\n\n /** PrivateFunction _streamWrap\n * _Private_ helper function to wrap a stanza in a tag.\n * This is used so Strophe can process stanzas from WebSockets like BOSH\n */\n _streamWrap: function _streamWrap(stanza) {\n return \"\" + stanza + '';\n },\n\n /** PrivateFunction: _closeSocket\n * _Private_ function to close the WebSocket.\n *\n * Closes the socket if it is still open and deletes it\n */\n _closeSocket: function _closeSocket() {\n if (this.socket) {\n try {\n this.socket.onclose = null;\n this.socket.onerror = null;\n this.socket.onmessage = null;\n this.socket.close();\n } catch (e) {\n Strophe$2.debug(e.message);\n }\n }\n\n this.socket = null;\n },\n\n /** PrivateFunction: _emptyQueue\n * _Private_ function to check if the message queue is empty.\n *\n * Returns:\n * True, because WebSocket messages are send immediately after queueing.\n */\n _emptyQueue: function _emptyQueue() {\n return true;\n },\n\n /** PrivateFunction: _onClose\n * _Private_ function to handle websockets closing.\n *\n * Nothing to do here for WebSockets\n */\n _onClose: function _onClose(e) {\n if (this._conn.connected && !this._conn.disconnecting) {\n Strophe$2.error(\"Websocket closed unexpectedly\");\n\n this._conn._doDisconnect();\n } else if (e && e.code === 1006 && !this._conn.connected && this.socket) {\n // in case the onError callback was not called (Safari 10 does not\n // call onerror when the initial connection fails) we need to\n // dispatch a CONNFAIL status update to be consistent with the\n // behavior on other browsers.\n Strophe$2.error(\"Websocket closed unexcectedly\");\n\n this._conn._changeConnectStatus(Strophe$2.Status.CONNFAIL, \"The WebSocket connection could not be established or was disconnected.\");\n\n this._conn._doDisconnect();\n } else {\n Strophe$2.debug(\"Websocket closed\");\n }\n },\n\n /** PrivateFunction: _no_auth_received\n *\n * Called on stream start/restart when no stream:features\n * has been received.\n */\n _no_auth_received: function _no_auth_received(callback) {\n Strophe$2.error(\"Server did not offer a supported authentication mechanism\");\n\n this._conn._changeConnectStatus(Strophe$2.Status.CONNFAIL, Strophe$2.ErrorCondition.NO_AUTH_MECH);\n\n if (callback) {\n callback.call(this._conn);\n }\n\n this._conn._doDisconnect();\n },\n\n /** PrivateFunction: _onDisconnectTimeout\n * _Private_ timeout handler for handling non-graceful disconnection.\n *\n * This does nothing for WebSockets\n */\n _onDisconnectTimeout: function _onDisconnectTimeout() {},\n\n /** PrivateFunction: _abortAllRequests\n * _Private_ helper function that makes sure all pending requests are aborted.\n */\n _abortAllRequests: function _abortAllRequests() {},\n\n /** PrivateFunction: _onError\n * _Private_ function to handle websockets errors.\n *\n * Parameters:\n * (Object) error - The websocket error.\n */\n _onError: function _onError(error) {\n Strophe$2.error(\"Websocket error \" + error);\n\n this._conn._changeConnectStatus(Strophe$2.Status.CONNFAIL, \"The WebSocket connection could not be established or was disconnected.\");\n\n this._disconnect();\n },\n\n /** PrivateFunction: _onIdle\n * _Private_ function called by Strophe.Connection._onIdle\n *\n * sends all queued stanzas\n */\n _onIdle: function _onIdle() {\n var data = this._conn._data;\n\n if (data.length > 0 && !this._conn.paused) {\n for (var i = 0; i < data.length; i++) {\n if (data[i] !== null) {\n var stanza = void 0;\n\n if (data[i] === \"restart\") {\n stanza = this._buildStream().tree();\n } else {\n stanza = data[i];\n }\n\n var rawStanza = Strophe$2.serialize(stanza);\n\n this._conn.xmlOutput(stanza);\n\n this._conn.rawOutput(rawStanza);\n\n this.socket.send(rawStanza);\n }\n }\n\n this._conn._data = [];\n }\n },\n\n /** PrivateFunction: _onMessage\n * _Private_ function to handle websockets messages.\n *\n * This function parses each of the messages as if they are full documents.\n * [TODO : We may actually want to use a SAX Push parser].\n *\n * Since all XMPP traffic starts with\n * \n *\n * The first stanza will always fail to be parsed.\n *\n * Additionally, the seconds stanza will always be with\n * the stream NS defined in the previous stanza, so we need to 'force'\n * the inclusion of the NS in this stanza.\n *\n * Parameters:\n * (string) message - The websocket message.\n */\n _onMessage: function _onMessage(message) {\n var elem; // check for closing stream\n\n var close = '';\n\n if (message.data === close) {\n this._conn.rawInput(close);\n\n this._conn.xmlInput(message);\n\n if (!this._conn.disconnecting) {\n this._conn._doDisconnect();\n }\n\n return;\n } else if (message.data.search(\" tag before we close the connection\n\n\n return;\n }\n\n this._conn._dataRecv(elem, message.data);\n },\n\n /** PrivateFunction: _onOpen\n * _Private_ function to handle websockets connection setup.\n *\n * The opening stream tag is sent here.\n */\n _onOpen: function _onOpen() {\n Strophe$2.debug(\"Websocket open\");\n\n var start = this._buildStream();\n\n this._conn.xmlOutput(start.tree());\n\n var startString = Strophe$2.serialize(start);\n\n this._conn.rawOutput(startString);\n\n this.socket.send(startString);\n },\n\n /** PrivateFunction: _reqToData\n * _Private_ function to get a stanza out of a request.\n *\n * WebSockets don't use requests, so the passed argument is just returned.\n *\n * Parameters:\n * (Object) stanza - The stanza.\n *\n * Returns:\n * The stanza that was passed.\n */\n _reqToData: function _reqToData(stanza) {\n return stanza;\n },\n\n /** PrivateFunction: _send\n * _Private_ part of the Connection.send function for WebSocket\n *\n * Just flushes the messages that are in the queue\n */\n _send: function _send() {\n this._conn.flush();\n },\n\n /** PrivateFunction: _sendRestart\n *\n * Send an xmpp:restart stanza.\n */\n _sendRestart: function _sendRestart() {\n clearTimeout(this._conn._idleTimeout);\n\n this._conn._onIdle.bind(this._conn)();\n }\n };\n\n global$1.Strophe = core.Strophe;\n global$1.$build = core.$build;\n global$1.$iq = core.$iq;\n global$1.$msg = core.$msg;\n global$1.$pres = core.$pres;\n\n return core;\n\n}));\n","(function (global, factory) {\n\ttypeof exports === 'object' && typeof module !== 'undefined' ? factory(require('strophe.js')) :\n\ttypeof define === 'function' && define.amd ? define(['strophe.js'], factory) :\n\t(factory(global.window));\n}(this, (function (strophe_js) { 'use strict';\n\nstrophe_js.Strophe.addConnectionPlugin('disco',\n{\n _connection: null,\n _identities : [],\n _features : [],\n _items : [],\n /** Function: init\n * Plugin init\n *\n * Parameters:\n * (Strophe.Connection) conn - Strophe connection\n */\n init: function(conn)\n {\n this._connection = conn;\n this._identities = [];\n this._features = [];\n this._items = [];\n // disco info\n conn.addHandler(this._onDiscoInfo.bind(this), strophe_js.Strophe.NS.DISCO_INFO, 'iq', 'get', null, null);\n // disco items\n conn.addHandler(this._onDiscoItems.bind(this), strophe_js.Strophe.NS.DISCO_ITEMS, 'iq', 'get', null, null);\n },\n /** Function: addIdentity\n * See http://xmpp.org/registrar/disco-categories.html\n * Parameters:\n * (String) category - category of identity (like client, automation, etc ...)\n * (String) type - type of identity (like pc, web, bot , etc ...)\n * (String) name - name of identity in natural language\n * (String) lang - lang of name parameter\n *\n * Returns:\n * Boolean\n */\n addIdentity: function(category, type, name, lang)\n {\n for (var i=0; i 0;\n\n\t\t\treturn this._originalOnStreamFeaturesAfterSASL.apply(this._c, arguments);\n\t\t},\n\n\t\tstatusChanged: function (status) {\n\t\t\tthis._connectionStatus = status;\n\t\t\tif (!this.getResumeToken()\n\t\t\t\t&& (status === strophe_js.Strophe.Status.CONNECTED || status === strophe_js.Strophe.Status.DISCONNECTED)) {\n\t\t\t\tthis.logging && strophe_js.Strophe.debug('SM reset state');\n\n\t\t\t\tthis._serverProcesssedStanzasCounter = 0;\n\t\t\t\tthis._clientProcessedStanzasCounter = 0;\n\n\t\t\t\tthis._clientSentStanzasCounter = 0;\n\n\t\t\t\tthis._isStreamManagementEnabled = false;\n\t\t\t\tthis._requestResponseIntervalCount = 0;\n\n\t\t\t\t// FIXME not described in JSDocs\n\t\t\t\tthis._resuming = false;\n\n\t\t\t\tif (status === strophe_js.Strophe.Status.DISCONNECTED) {\n\t\t\t\t\tthis._isSupported = false;\n\t\t\t\t}\n\n\t\t\t\tthis._unacknowledgedStanzas = [];\n\n\t\t\t\tif (this._requestHandler) {\n\t\t\t\t\tthis._c.deleteHandler(this._requestHandler);\n\t\t\t\t}\n\n\t\t\t\tif (this._incomingHandler) {\n\t\t\t\t\tthis._c.deleteHandler(this._incomingHandler);\n\t\t\t\t}\n\n\t\t\t\tthis._requestHandler = this._c.addHandler(this._handleServerRequestHandler.bind(this), this._NS, 'r');\n\t\t\t\tthis._ackHandler = this._c.addHandler(this._handleServerAck.bind(this), this._NS, 'a');\n\t\t\t\tthis._incomingHandler = this._c.addHandler(this._incomingStanzaHandler.bind(this));\n\n\t\t\t\t// FIXME handler instances stored, but never used\n\t\t\t\tthis._enabledHandler = this._c._addSysHandler(this._handleEnabled.bind(this), this._NS, 'enabled');\n\t\t\t\tthis._resumeFailedHandler = this._c._addSysHandler(this._handleResumeFailed.bind(this), this._NS, 'failed');\n\t\t\t\tthis._resumedHandler = this._c._addSysHandler(this._handleResumed.bind(this), this._NS,'resumed');\n\n\t\t\t} else if (status === strophe_js.Strophe.Status.BINDREQUIRED) {\n\t\t\t\tthis._c.jid = this._storedJid;\n\n\t\t\t\t// Restore Strophe handlers\n\t\t\t\tfor (const property in this._resumeState) {\n\t\t\t\t\tthis._c[property] = this._resumeState[property];\n\t\t\t\t}\n\n\t\t\t\t// FIXME check conditions if there's session ID and if enabled\n\t\t\t\tthis._c.send(strophe_js.$build('resume', {\n\t\t\t\t\txmlns: this._NS,\n\t\t\t\t\th: this._clientProcessedStanzasCounter,\n\t\t\t\t\tprevid: this._resumeToken\n\t\t\t\t}));\n\t\t\t\tthis._c.flush();\n\t\t\t} else if (status === strophe_js.Strophe.Status.ERROR) {\n\t\t\t\tthis.logging && strophe_js.Strophe.debug('SM cleared resume token on error');\n\t\t\t\tthis._resumeToken = undefined;\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t* This method overrides the send method implemented by Strophe.Connection\n\t\t* to count outgoing stanzas\n\t\t*\n\t\t* @method Send\n\t\t* @public\n\t\t*/\n\t\txmlOutput: function(elem) {\n\t\t\tif (strophe_js.Strophe.isTagEqual(elem, 'iq') ||\n\t\t\t\tstrophe_js.Strophe.isTagEqual(elem, 'presence') ||\n\t\t\t\tstrophe_js.Strophe.isTagEqual(elem, 'message')) {\n\t\t\t\tthis._increaseSentStanzasCounter(elem);\n\t\t\t}\n\n\t\t\treturn this._originalXMLOutput.call(this._c, elem);\n\t\t},\n\n\t\t_handleEnabled: function(elem) {\n\t\t\tthis._isStreamManagementEnabled = true;\n\t\t\t// FIXME fail if requested, but not enabled\n\t\t\tthis._resumeToken = elem.getAttribute('resume') === 'true' && elem.getAttribute('id');\n\n\t\t\tthis._c.resume();\n\n\t\t\treturn true;\n\t\t},\n\n\t\t_handleResumeFailed: function(elem) {\n\t\t\tconst error = elem && elem.firstElementChild && elem.firstElementChild.tagName;\n\n\t\t\tthis._c._changeConnectStatus(strophe_js.Strophe.Status.ERROR, error, elem);\n\t\t\tthis._c._doDisconnect();\n\n\t\t\treturn true;\n\t\t},\n\n\t\t_handleResumed: function(elem) {\n\t\t\t// FIXME check if in the correct state\n\t\t\tvar handledCount = parseInt(elem.getAttribute('h'));\n\t\t\tthis._handleAcknowledgedStanzas(handledCount, this._serverProcesssedStanzasCounter);\n\n\t\t\tthis._resuming = false;\n\t\t\tthis._c.do_bind = false; // No need to bind our resource anymore\n\t\t\tthis._c.authenticated = true;\n\t\t\tthis._c.restored = true;\n\n\t\t\tif (this._unacknowledgedStanzas.length > 0) {\n\t\t\t\tthis.logging && strophe_js.Strophe.debug('SM Sending unacknowledged stanzas', this._unacknowledgedStanzas);\n\t\t\t\tfor(const stanza of this._unacknowledgedStanzas) {\n\t\t\t\t\tthis._c.send(stanza);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tthis.logging && strophe_js.Strophe.debug('SM No unacknowledged stanzas', this._unacknowledgedStanzas);\n\t\t\t}\n\n\t\t\tthis._c._changeConnectStatus(strophe_js.Strophe.Status.CONNECTED, null);\n\n\t\t\treturn true;\n\t\t},\n\n\t\t_incomingStanzaHandler: function(elem) {\n\t\t\tif (strophe_js.Strophe.isTagEqual(elem, 'iq') || strophe_js.Strophe.isTagEqual(elem, 'presence') || strophe_js.Strophe.isTagEqual(elem, 'message')) {\n\t\t\t\tthis._increaseReceivedStanzasCounter();\n\n\t\t\t\tif (this.autoSendCountOnEveryIncomingStanza) {\n\t\t\t\t\tthis._answerProcessedStanzas();\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn true;\n\t\t},\n\n\t\t_handleAcknowledgedStanzas: function(reportedHandledCount, lastKnownHandledCount) {\n\t\t\tvar delta = reportedHandledCount - lastKnownHandledCount;\n\n\t\t\tif (delta < 0) {\n\t\t\t\tthis._throwError('New reported stanza count lower than previous. New: ' + reportedHandledCount + ' - Previous: ' + lastKnownHandledCount);\n\t\t\t}\n\n\t\t\tif (delta > this._unacknowledgedStanzas.length) {\n\t\t\t\tthis._throwError('Higher reported acknowledge count than unacknowledged stanzas. Reported Acknowledge Count: ' + delta + ' - Unacknowledge Stanza Count: ' + this._unacknowledgedStanzas.length + ' - New: ' + reportedHandledCount + ' - Previous: ' + lastKnownHandledCount);\n\t\t\t}\n\n\t\t\tfor(var i = 0; i < delta; i++) {\n\t\t\t\tvar stanza = this._unacknowledgedStanzas.shift();\n\t\t\t\tfor (var j = 0; j < this._acknowledgedStanzaListeners.length; j++) {\n\t\t\t\t\tthis._acknowledgedStanzaListeners[j](stanza);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (this.logging && this._unacknowledgedStanzas.length > 0) {\n\t\t\t\tstrophe_js.Strophe.warn('SM Unacknowledged stanzas', this._unacknowledgedStanzas);\n\t\t\t}\n\n\t\t\tthis._serverProcesssedStanzasCounter = reportedHandledCount;\n\n\t\t\tif (this.requestResponseInterval > 0) {\n\t\t\t\tthis._requestResponseIntervalCount = 0;\n\t\t\t}\n\t\t},\n\n\t\t_handleServerRequestHandler: function() {\n\t\t\tthis._answerProcessedStanzas();\n\n\t\t\treturn true;\n\t\t},\n\n\t\t_handleServerAck: function(elem){\n\t\t\tvar handledCount = parseInt(elem.getAttribute('h'));\n\t\t\tthis._handleAcknowledgedStanzas(handledCount, this._serverProcesssedStanzasCounter);\n\n\t\t\treturn true;\n\t\t},\n\n\t\t_answerProcessedStanzas: function() {\n\t\t\tif (this._isStreamManagementEnabled) {\n\t\t\t\tthis._c.send(strophe_js.$build('a', { xmlns: this._NS, h: this._clientProcessedStanzasCounter }));\n\t\t\t}\n\t\t},\n\n\t\t_increaseSentStanzasCounter: function(elem) {\n\t\t\tif (this._isStreamManagementEnabled) {\n\t\t\t\tif (this._unacknowledgedStanzas.indexOf(elem) !== -1) {\n\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tthis._unacknowledgedStanzas.push(elem);\n\t\t\t\tthis._clientSentStanzasCounter++;\n\n\t\t\t\tif (this.requestResponseInterval > 0) {\n\t\t\t\t\tthis._requestResponseIntervalCount++;\n\n\t\t\t\t\tif (this._requestResponseIntervalCount === this.requestResponseInterval) {\n\t\t\t\t\t\t// FIXME Can not call send from onIdle.\n\t\t\t\t\t\tsetTimeout(() => {\n\t\t\t\t\t\t\tif (this._connectionStatus === strophe_js.Strophe.Status.CONNECTED) {\n\t\t\t\t\t\t\t\tthis.requestAcknowledgement();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}, 1);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\t_increaseReceivedStanzasCounter: function() {\n\t\t\tif (this._isStreamManagementEnabled) {\n\t\t\t\tthis._clientProcessedStanzasCounter++;\n\t\t\t}\n\t\t},\n\n\t\t_throwError: function(msg) {\n\t\t\tstrophe_js.Strophe.error(msg);\n\t\t\tthrow new Error(msg);\n\t\t}\n\n\t});\n\n})));\n//# sourceMappingURL=strophe.stream-management.js.map\n","\"use strict\";\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.default = void 0;\n\n/**\n * Convert array of 16 byte values to UUID string format of the form:\n * XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX\n */\nconst byteToHex = [];\n\nfor (let i = 0; i < 256; ++i) {\n byteToHex.push((i + 0x100).toString(16).substr(1));\n}\n\nfunction bytesToUuid(buf, offset) {\n const i = offset || 0;\n const bth = byteToHex; // Note: Be careful editing this code! It's been tuned for performance\n // and works in ways you may not expect. See https://github.com/uuidjs/uuid/pull/434\n\n return (bth[buf[i + 0]] + bth[buf[i + 1]] + bth[buf[i + 2]] + bth[buf[i + 3]] + '-' + bth[buf[i + 4]] + bth[buf[i + 5]] + '-' + bth[buf[i + 6]] + bth[buf[i + 7]] + '-' + bth[buf[i + 8]] + bth[buf[i + 9]] + '-' + bth[buf[i + 10]] + bth[buf[i + 11]] + bth[buf[i + 12]] + bth[buf[i + 13]] + bth[buf[i + 14]] + bth[buf[i + 15]]).toLowerCase();\n}\n\nvar _default = bytesToUuid;\nexports.default = _default;","\"use strict\";\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nObject.defineProperty(exports, \"v1\", {\n enumerable: true,\n get: function () {\n return _v.default;\n }\n});\nObject.defineProperty(exports, \"v3\", {\n enumerable: true,\n get: function () {\n return _v2.default;\n }\n});\nObject.defineProperty(exports, \"v4\", {\n enumerable: true,\n get: function () {\n return _v3.default;\n }\n});\nObject.defineProperty(exports, \"v5\", {\n enumerable: true,\n get: function () {\n return _v4.default;\n }\n});\n\nvar _v = _interopRequireDefault(require(\"./v1.js\"));\n\nvar _v2 = _interopRequireDefault(require(\"./v3.js\"));\n\nvar _v3 = _interopRequireDefault(require(\"./v4.js\"));\n\nvar _v4 = _interopRequireDefault(require(\"./v5.js\"));\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }","\"use strict\";\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.default = void 0;\n\n/*\n * Browser-compatible JavaScript MD5\n *\n * Modification of JavaScript MD5\n * https://github.com/blueimp/JavaScript-MD5\n *\n * Copyright 2011, Sebastian Tschan\n * https://blueimp.net\n *\n * Licensed under the MIT license:\n * https://opensource.org/licenses/MIT\n *\n * Based on\n * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message\n * Digest Algorithm, as defined in RFC 1321.\n * Version 2.2 Copyright (C) Paul Johnston 1999 - 2009\n * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet\n * Distributed under the BSD License\n * See http://pajhome.org.uk/crypt/md5 for more info.\n */\nfunction md5(bytes) {\n if (typeof bytes === 'string') {\n const msg = unescape(encodeURIComponent(bytes)); // UTF8 escape\n\n bytes = new Uint8Array(msg.length);\n\n for (let i = 0; i < msg.length; ++i) {\n bytes[i] = msg.charCodeAt(i);\n }\n }\n\n return md5ToHexEncodedArray(wordsToMd5(bytesToWords(bytes), bytes.length * 8));\n}\n/*\n * Convert an array of little-endian words to an array of bytes\n */\n\n\nfunction md5ToHexEncodedArray(input) {\n const output = [];\n const length32 = input.length * 32;\n const hexTab = '0123456789abcdef';\n\n for (let i = 0; i < length32; i += 8) {\n const x = input[i >> 5] >>> i % 32 & 0xff;\n const hex = parseInt(hexTab.charAt(x >>> 4 & 0x0f) + hexTab.charAt(x & 0x0f), 16);\n output.push(hex);\n }\n\n return output;\n}\n/**\n * Calculate output length with padding and bit length\n */\n\n\nfunction getOutputLength(inputLength8) {\n return (inputLength8 + 64 >>> 9 << 4) + 14 + 1;\n}\n/*\n * Calculate the MD5 of an array of little-endian words, and a bit length.\n */\n\n\nfunction wordsToMd5(x, len) {\n /* append padding */\n x[len >> 5] |= 0x80 << len % 32;\n x[getOutputLength(len) - 1] = len;\n let a = 1732584193;\n let b = -271733879;\n let c = -1732584194;\n let d = 271733878;\n\n for (let i = 0; i < x.length; i += 16) {\n const olda = a;\n const oldb = b;\n const oldc = c;\n const oldd = d;\n a = md5ff(a, b, c, d, x[i], 7, -680876936);\n d = md5ff(d, a, b, c, x[i + 1], 12, -389564586);\n c = md5ff(c, d, a, b, x[i + 2], 17, 606105819);\n b = md5ff(b, c, d, a, x[i + 3], 22, -1044525330);\n a = md5ff(a, b, c, d, x[i + 4], 7, -176418897);\n d = md5ff(d, a, b, c, x[i + 5], 12, 1200080426);\n c = md5ff(c, d, a, b, x[i + 6], 17, -1473231341);\n b = md5ff(b, c, d, a, x[i + 7], 22, -45705983);\n a = md5ff(a, b, c, d, x[i + 8], 7, 1770035416);\n d = md5ff(d, a, b, c, x[i + 9], 12, -1958414417);\n c = md5ff(c, d, a, b, x[i + 10], 17, -42063);\n b = md5ff(b, c, d, a, x[i + 11], 22, -1990404162);\n a = md5ff(a, b, c, d, x[i + 12], 7, 1804603682);\n d = md5ff(d, a, b, c, x[i + 13], 12, -40341101);\n c = md5ff(c, d, a, b, x[i + 14], 17, -1502002290);\n b = md5ff(b, c, d, a, x[i + 15], 22, 1236535329);\n a = md5gg(a, b, c, d, x[i + 1], 5, -165796510);\n d = md5gg(d, a, b, c, x[i + 6], 9, -1069501632);\n c = md5gg(c, d, a, b, x[i + 11], 14, 643717713);\n b = md5gg(b, c, d, a, x[i], 20, -373897302);\n a = md5gg(a, b, c, d, x[i + 5], 5, -701558691);\n d = md5gg(d, a, b, c, x[i + 10], 9, 38016083);\n c = md5gg(c, d, a, b, x[i + 15], 14, -660478335);\n b = md5gg(b, c, d, a, x[i + 4], 20, -405537848);\n a = md5gg(a, b, c, d, x[i + 9], 5, 568446438);\n d = md5gg(d, a, b, c, x[i + 14], 9, -1019803690);\n c = md5gg(c, d, a, b, x[i + 3], 14, -187363961);\n b = md5gg(b, c, d, a, x[i + 8], 20, 1163531501);\n a = md5gg(a, b, c, d, x[i + 13], 5, -1444681467);\n d = md5gg(d, a, b, c, x[i + 2], 9, -51403784);\n c = md5gg(c, d, a, b, x[i + 7], 14, 1735328473);\n b = md5gg(b, c, d, a, x[i + 12], 20, -1926607734);\n a = md5hh(a, b, c, d, x[i + 5], 4, -378558);\n d = md5hh(d, a, b, c, x[i + 8], 11, -2022574463);\n c = md5hh(c, d, a, b, x[i + 11], 16, 1839030562);\n b = md5hh(b, c, d, a, x[i + 14], 23, -35309556);\n a = md5hh(a, b, c, d, x[i + 1], 4, -1530992060);\n d = md5hh(d, a, b, c, x[i + 4], 11, 1272893353);\n c = md5hh(c, d, a, b, x[i + 7], 16, -155497632);\n b = md5hh(b, c, d, a, x[i + 10], 23, -1094730640);\n a = md5hh(a, b, c, d, x[i + 13], 4, 681279174);\n d = md5hh(d, a, b, c, x[i], 11, -358537222);\n c = md5hh(c, d, a, b, x[i + 3], 16, -722521979);\n b = md5hh(b, c, d, a, x[i + 6], 23, 76029189);\n a = md5hh(a, b, c, d, x[i + 9], 4, -640364487);\n d = md5hh(d, a, b, c, x[i + 12], 11, -421815835);\n c = md5hh(c, d, a, b, x[i + 15], 16, 530742520);\n b = md5hh(b, c, d, a, x[i + 2], 23, -995338651);\n a = md5ii(a, b, c, d, x[i], 6, -198630844);\n d = md5ii(d, a, b, c, x[i + 7], 10, 1126891415);\n c = md5ii(c, d, a, b, x[i + 14], 15, -1416354905);\n b = md5ii(b, c, d, a, x[i + 5], 21, -57434055);\n a = md5ii(a, b, c, d, x[i + 12], 6, 1700485571);\n d = md5ii(d, a, b, c, x[i + 3], 10, -1894986606);\n c = md5ii(c, d, a, b, x[i + 10], 15, -1051523);\n b = md5ii(b, c, d, a, x[i + 1], 21, -2054922799);\n a = md5ii(a, b, c, d, x[i + 8], 6, 1873313359);\n d = md5ii(d, a, b, c, x[i + 15], 10, -30611744);\n c = md5ii(c, d, a, b, x[i + 6], 15, -1560198380);\n b = md5ii(b, c, d, a, x[i + 13], 21, 1309151649);\n a = md5ii(a, b, c, d, x[i + 4], 6, -145523070);\n d = md5ii(d, a, b, c, x[i + 11], 10, -1120210379);\n c = md5ii(c, d, a, b, x[i + 2], 15, 718787259);\n b = md5ii(b, c, d, a, x[i + 9], 21, -343485551);\n a = safeAdd(a, olda);\n b = safeAdd(b, oldb);\n c = safeAdd(c, oldc);\n d = safeAdd(d, oldd);\n }\n\n return [a, b, c, d];\n}\n/*\n * Convert an array bytes to an array of little-endian words\n * Characters >255 have their high-byte silently ignored.\n */\n\n\nfunction bytesToWords(input) {\n if (input.length === 0) {\n return [];\n }\n\n const length8 = input.length * 8;\n const output = new Uint32Array(getOutputLength(length8));\n\n for (let i = 0; i < length8; i += 8) {\n output[i >> 5] |= (input[i / 8] & 0xff) << i % 32;\n }\n\n return output;\n}\n/*\n * Add integers, wrapping at 2^32. This uses 16-bit operations internally\n * to work around bugs in some JS interpreters.\n */\n\n\nfunction safeAdd(x, y) {\n const lsw = (x & 0xffff) + (y & 0xffff);\n const msw = (x >> 16) + (y >> 16) + (lsw >> 16);\n return msw << 16 | lsw & 0xffff;\n}\n/*\n * Bitwise rotate a 32-bit number to the left.\n */\n\n\nfunction bitRotateLeft(num, cnt) {\n return num << cnt | num >>> 32 - cnt;\n}\n/*\n * These functions implement the four basic operations the algorithm uses.\n */\n\n\nfunction md5cmn(q, a, b, x, s, t) {\n return safeAdd(bitRotateLeft(safeAdd(safeAdd(a, q), safeAdd(x, t)), s), b);\n}\n\nfunction md5ff(a, b, c, d, x, s, t) {\n return md5cmn(b & c | ~b & d, a, b, x, s, t);\n}\n\nfunction md5gg(a, b, c, d, x, s, t) {\n return md5cmn(b & d | c & ~d, a, b, x, s, t);\n}\n\nfunction md5hh(a, b, c, d, x, s, t) {\n return md5cmn(b ^ c ^ d, a, b, x, s, t);\n}\n\nfunction md5ii(a, b, c, d, x, s, t) {\n return md5cmn(c ^ (b | ~d), a, b, x, s, t);\n}\n\nvar _default = md5;\nexports.default = _default;","\"use strict\";\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.default = rng;\n// Unique ID creation requires a high quality random # generator. In the browser we therefore\n// require the crypto API and do not support built-in fallback to lower quality random number\n// generators (like Math.random()).\n// getRandomValues needs to be invoked in a context where \"this\" is a Crypto implementation. Also,\n// find the complete implementation of crypto (msCrypto) on IE11.\nconst getRandomValues = typeof crypto !== 'undefined' && crypto.getRandomValues && crypto.getRandomValues.bind(crypto) || typeof msCrypto !== 'undefined' && typeof msCrypto.getRandomValues === 'function' && msCrypto.getRandomValues.bind(msCrypto);\nconst rnds8 = new Uint8Array(16);\n\nfunction rng() {\n if (!getRandomValues) {\n throw new Error('crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported');\n }\n\n return getRandomValues(rnds8);\n}","\"use strict\";\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.default = void 0;\n\n// Adapted from Chris Veness' SHA1 code at\n// http://www.movable-type.co.uk/scripts/sha1.html\nfunction f(s, x, y, z) {\n switch (s) {\n case 0:\n return x & y ^ ~x & z;\n\n case 1:\n return x ^ y ^ z;\n\n case 2:\n return x & y ^ x & z ^ y & z;\n\n case 3:\n return x ^ y ^ z;\n }\n}\n\nfunction ROTL(x, n) {\n return x << n | x >>> 32 - n;\n}\n\nfunction sha1(bytes) {\n const K = [0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xca62c1d6];\n const H = [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0];\n\n if (typeof bytes === 'string') {\n const msg = unescape(encodeURIComponent(bytes)); // UTF8 escape\n\n bytes = [];\n\n for (let i = 0; i < msg.length; ++i) {\n bytes.push(msg.charCodeAt(i));\n }\n }\n\n bytes.push(0x80);\n const l = bytes.length / 4 + 2;\n const N = Math.ceil(l / 16);\n const M = new Array(N);\n\n for (let i = 0; i < N; ++i) {\n const arr = new Uint32Array(16);\n\n for (let j = 0; j < 16; ++j) {\n arr[j] = bytes[i * 64 + j * 4] << 24 | bytes[i * 64 + j * 4 + 1] << 16 | bytes[i * 64 + j * 4 + 2] << 8 | bytes[i * 64 + j * 4 + 3];\n }\n\n M[i] = arr;\n }\n\n M[N - 1][14] = (bytes.length - 1) * 8 / Math.pow(2, 32);\n M[N - 1][14] = Math.floor(M[N - 1][14]);\n M[N - 1][15] = (bytes.length - 1) * 8 & 0xffffffff;\n\n for (let i = 0; i < N; ++i) {\n const W = new Uint32Array(80);\n\n for (let t = 0; t < 16; ++t) {\n W[t] = M[i][t];\n }\n\n for (let t = 16; t < 80; ++t) {\n W[t] = ROTL(W[t - 3] ^ W[t - 8] ^ W[t - 14] ^ W[t - 16], 1);\n }\n\n let a = H[0];\n let b = H[1];\n let c = H[2];\n let d = H[3];\n let e = H[4];\n\n for (let t = 0; t < 80; ++t) {\n const s = Math.floor(t / 20);\n const T = ROTL(a, 5) + f(s, b, c, d) + e + K[s] + W[t] >>> 0;\n e = d;\n d = c;\n c = ROTL(b, 30) >>> 0;\n b = a;\n a = T;\n }\n\n H[0] = H[0] + a >>> 0;\n H[1] = H[1] + b >>> 0;\n H[2] = H[2] + c >>> 0;\n H[3] = H[3] + d >>> 0;\n H[4] = H[4] + e >>> 0;\n }\n\n return [H[0] >> 24 & 0xff, H[0] >> 16 & 0xff, H[0] >> 8 & 0xff, H[0] & 0xff, H[1] >> 24 & 0xff, H[1] >> 16 & 0xff, H[1] >> 8 & 0xff, H[1] & 0xff, H[2] >> 24 & 0xff, H[2] >> 16 & 0xff, H[2] >> 8 & 0xff, H[2] & 0xff, H[3] >> 24 & 0xff, H[3] >> 16 & 0xff, H[3] >> 8 & 0xff, H[3] & 0xff, H[4] >> 24 & 0xff, H[4] >> 16 & 0xff, H[4] >> 8 & 0xff, H[4] & 0xff];\n}\n\nvar _default = sha1;\nexports.default = _default;","\"use strict\";\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.default = void 0;\n\nvar _rng = _interopRequireDefault(require(\"./rng.js\"));\n\nvar _bytesToUuid = _interopRequireDefault(require(\"./bytesToUuid.js\"));\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\n// **`v1()` - Generate time-based UUID**\n//\n// Inspired by https://github.com/LiosK/UUID.js\n// and http://docs.python.org/library/uuid.html\nlet _nodeId;\n\nlet _clockseq; // Previous uuid creation time\n\n\nlet _lastMSecs = 0;\nlet _lastNSecs = 0; // See https://github.com/uuidjs/uuid for API details\n\nfunction v1(options, buf, offset) {\n let i = buf && offset || 0;\n const b = buf || [];\n options = options || {};\n let node = options.node || _nodeId;\n let clockseq = options.clockseq !== undefined ? options.clockseq : _clockseq; // node and clockseq need to be initialized to random values if they're not\n // specified. We do this lazily to minimize issues related to insufficient\n // system entropy. See #189\n\n if (node == null || clockseq == null) {\n const seedBytes = options.random || (options.rng || _rng.default)();\n\n if (node == null) {\n // Per 4.5, create and 48-bit node id, (47 random bits + multicast bit = 1)\n node = _nodeId = [seedBytes[0] | 0x01, seedBytes[1], seedBytes[2], seedBytes[3], seedBytes[4], seedBytes[5]];\n }\n\n if (clockseq == null) {\n // Per 4.2.2, randomize (14 bit) clockseq\n clockseq = _clockseq = (seedBytes[6] << 8 | seedBytes[7]) & 0x3fff;\n }\n } // UUID timestamps are 100 nano-second units since the Gregorian epoch,\n // (1582-10-15 00:00). JSNumbers aren't precise enough for this, so\n // time is handled internally as 'msecs' (integer milliseconds) and 'nsecs'\n // (100-nanoseconds offset from msecs) since unix epoch, 1970-01-01 00:00.\n\n\n let msecs = options.msecs !== undefined ? options.msecs : Date.now(); // Per 4.2.1.2, use count of uuid's generated during the current clock\n // cycle to simulate higher resolution clock\n\n let nsecs = options.nsecs !== undefined ? options.nsecs : _lastNSecs + 1; // Time since last uuid creation (in msecs)\n\n const dt = msecs - _lastMSecs + (nsecs - _lastNSecs) / 10000; // Per 4.2.1.2, Bump clockseq on clock regression\n\n if (dt < 0 && options.clockseq === undefined) {\n clockseq = clockseq + 1 & 0x3fff;\n } // Reset nsecs if clock regresses (new clockseq) or we've moved onto a new\n // time interval\n\n\n if ((dt < 0 || msecs > _lastMSecs) && options.nsecs === undefined) {\n nsecs = 0;\n } // Per 4.2.1.2 Throw error if too many uuids are requested\n\n\n if (nsecs >= 10000) {\n throw new Error(\"uuid.v1(): Can't create more than 10M uuids/sec\");\n }\n\n _lastMSecs = msecs;\n _lastNSecs = nsecs;\n _clockseq = clockseq; // Per 4.1.4 - Convert from unix epoch to Gregorian epoch\n\n msecs += 12219292800000; // `time_low`\n\n const tl = ((msecs & 0xfffffff) * 10000 + nsecs) % 0x100000000;\n b[i++] = tl >>> 24 & 0xff;\n b[i++] = tl >>> 16 & 0xff;\n b[i++] = tl >>> 8 & 0xff;\n b[i++] = tl & 0xff; // `time_mid`\n\n const tmh = msecs / 0x100000000 * 10000 & 0xfffffff;\n b[i++] = tmh >>> 8 & 0xff;\n b[i++] = tmh & 0xff; // `time_high_and_version`\n\n b[i++] = tmh >>> 24 & 0xf | 0x10; // include version\n\n b[i++] = tmh >>> 16 & 0xff; // `clock_seq_hi_and_reserved` (Per 4.2.2 - include variant)\n\n b[i++] = clockseq >>> 8 | 0x80; // `clock_seq_low`\n\n b[i++] = clockseq & 0xff; // `node`\n\n for (let n = 0; n < 6; ++n) {\n b[i + n] = node[n];\n }\n\n return buf || (0, _bytesToUuid.default)(b);\n}\n\nvar _default = v1;\nexports.default = _default;","\"use strict\";\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.default = void 0;\n\nvar _v = _interopRequireDefault(require(\"./v35.js\"));\n\nvar _md = _interopRequireDefault(require(\"./md5.js\"));\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nconst v3 = (0, _v.default)('v3', 0x30, _md.default);\nvar _default = v3;\nexports.default = _default;","\"use strict\";\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.default = _default;\nexports.URL = exports.DNS = void 0;\n\nvar _bytesToUuid = _interopRequireDefault(require(\"./bytesToUuid.js\"));\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction uuidToBytes(uuid) {\n // Note: We assume we're being passed a valid uuid string\n const bytes = [];\n uuid.replace(/[a-fA-F0-9]{2}/g, function (hex) {\n bytes.push(parseInt(hex, 16));\n });\n return bytes;\n}\n\nfunction stringToBytes(str) {\n str = unescape(encodeURIComponent(str)); // UTF8 escape\n\n const bytes = [];\n\n for (let i = 0; i < str.length; ++i) {\n bytes.push(str.charCodeAt(i));\n }\n\n return bytes;\n}\n\nconst DNS = '6ba7b810-9dad-11d1-80b4-00c04fd430c8';\nexports.DNS = DNS;\nconst URL = '6ba7b811-9dad-11d1-80b4-00c04fd430c8';\nexports.URL = URL;\n\nfunction _default(name, version, hashfunc) {\n function generateUUID(value, namespace, buf, offset) {\n const off = buf && offset || 0;\n if (typeof value === 'string') value = stringToBytes(value);\n if (typeof namespace === 'string') namespace = uuidToBytes(namespace);\n\n if (!Array.isArray(value)) {\n throw TypeError('value must be an array of bytes');\n }\n\n if (!Array.isArray(namespace) || namespace.length !== 16) {\n throw TypeError('namespace must be uuid string or an Array of 16 byte values');\n } // Per 4.3\n\n\n const bytes = hashfunc(namespace.concat(value));\n bytes[6] = bytes[6] & 0x0f | version;\n bytes[8] = bytes[8] & 0x3f | 0x80;\n\n if (buf) {\n for (let idx = 0; idx < 16; ++idx) {\n buf[off + idx] = bytes[idx];\n }\n }\n\n return buf || (0, _bytesToUuid.default)(bytes);\n } // Function#name is not settable on some platforms (#270)\n\n\n try {\n generateUUID.name = name; // eslint-disable-next-line no-empty\n } catch (err) {} // For CommonJS default export support\n\n\n generateUUID.DNS = DNS;\n generateUUID.URL = URL;\n return generateUUID;\n}","\"use strict\";\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.default = void 0;\n\nvar _rng = _interopRequireDefault(require(\"./rng.js\"));\n\nvar _bytesToUuid = _interopRequireDefault(require(\"./bytesToUuid.js\"));\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction v4(options, buf, offset) {\n if (typeof options === 'string') {\n buf = options === 'binary' ? new Uint8Array(16) : null;\n options = null;\n }\n\n options = options || {};\n\n const rnds = options.random || (options.rng || _rng.default)(); // Per 4.4, set bits for version and `clock_seq_hi_and_reserved`\n\n\n rnds[6] = rnds[6] & 0x0f | 0x40;\n rnds[8] = rnds[8] & 0x3f | 0x80; // Copy bytes to buffer, if provided\n\n if (buf) {\n const start = offset || 0;\n\n for (let i = 0; i < 16; ++i) {\n buf[start + i] = rnds[i];\n }\n\n return buf;\n }\n\n return (0, _bytesToUuid.default)(rnds);\n}\n\nvar _default = v4;\nexports.default = _default;","\"use strict\";\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.default = void 0;\n\nvar _v = _interopRequireDefault(require(\"./v35.js\"));\n\nvar _sha = _interopRequireDefault(require(\"./sha1.js\"));\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nconst v5 = (0, _v.default)('v5', 0x50, _sha.default);\nvar _default = v5;\nexports.default = _default;","/*\n * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by a BSD-style license\n * that can be found in the LICENSE file in the root of the source\n * tree.\n */\n /* eslint-env node */\n'use strict';\n\nlet logDisabled_ = true;\nlet deprecationWarnings_ = true;\n\n/**\n * Extract browser version out of the provided user agent string.\n *\n * @param {!string} uastring userAgent string.\n * @param {!string} expr Regular expression used as match criteria.\n * @param {!number} pos position in the version string to be returned.\n * @return {!number} browser version.\n */\nexport function extractVersion(uastring, expr, pos) {\n const match = uastring.match(expr);\n return match && match.length >= pos && parseInt(match[pos], 10);\n}\n\n// Wraps the peerconnection event eventNameToWrap in a function\n// which returns the modified event object (or false to prevent\n// the event).\nexport function wrapPeerConnectionEvent(window, eventNameToWrap, wrapper) {\n if (!window.RTCPeerConnection) {\n return;\n }\n const proto = window.RTCPeerConnection.prototype;\n const nativeAddEventListener = proto.addEventListener;\n proto.addEventListener = function(nativeEventName, cb) {\n if (nativeEventName !== eventNameToWrap) {\n return nativeAddEventListener.apply(this, arguments);\n }\n const wrappedCallback = (e) => {\n const modifiedEvent = wrapper(e);\n if (modifiedEvent) {\n if (cb.handleEvent) {\n cb.handleEvent(modifiedEvent);\n } else {\n cb(modifiedEvent);\n }\n }\n };\n this._eventMap = this._eventMap || {};\n if (!this._eventMap[eventNameToWrap]) {\n this._eventMap[eventNameToWrap] = new Map();\n }\n this._eventMap[eventNameToWrap].set(cb, wrappedCallback);\n return nativeAddEventListener.apply(this, [nativeEventName,\n wrappedCallback]);\n };\n\n const nativeRemoveEventListener = proto.removeEventListener;\n proto.removeEventListener = function(nativeEventName, cb) {\n if (nativeEventName !== eventNameToWrap || !this._eventMap\n || !this._eventMap[eventNameToWrap]) {\n return nativeRemoveEventListener.apply(this, arguments);\n }\n if (!this._eventMap[eventNameToWrap].has(cb)) {\n return nativeRemoveEventListener.apply(this, arguments);\n }\n const unwrappedCb = this._eventMap[eventNameToWrap].get(cb);\n this._eventMap[eventNameToWrap].delete(cb);\n if (this._eventMap[eventNameToWrap].size === 0) {\n delete this._eventMap[eventNameToWrap];\n }\n if (Object.keys(this._eventMap).length === 0) {\n delete this._eventMap;\n }\n return nativeRemoveEventListener.apply(this, [nativeEventName,\n unwrappedCb]);\n };\n\n Object.defineProperty(proto, 'on' + eventNameToWrap, {\n get() {\n return this['_on' + eventNameToWrap];\n },\n set(cb) {\n if (this['_on' + eventNameToWrap]) {\n this.removeEventListener(eventNameToWrap,\n this['_on' + eventNameToWrap]);\n delete this['_on' + eventNameToWrap];\n }\n if (cb) {\n this.addEventListener(eventNameToWrap,\n this['_on' + eventNameToWrap] = cb);\n }\n },\n enumerable: true,\n configurable: true\n });\n}\n\nexport function disableLog(bool) {\n if (typeof bool !== 'boolean') {\n return new Error('Argument type: ' + typeof bool +\n '. Please use a boolean.');\n }\n logDisabled_ = bool;\n return (bool) ? 'adapter.js logging disabled' :\n 'adapter.js logging enabled';\n}\n\n/**\n * Disable or enable deprecation warnings\n * @param {!boolean} bool set to true to disable warnings.\n */\nexport function disableWarnings(bool) {\n if (typeof bool !== 'boolean') {\n return new Error('Argument type: ' + typeof bool +\n '. Please use a boolean.');\n }\n deprecationWarnings_ = !bool;\n return 'adapter.js deprecation warnings ' + (bool ? 'disabled' : 'enabled');\n}\n\nexport function log() {\n if (typeof window === 'object') {\n if (logDisabled_) {\n return;\n }\n if (typeof console !== 'undefined' && typeof console.log === 'function') {\n console.log.apply(console, arguments);\n }\n }\n}\n\n/**\n * Shows a deprecation warning suggesting the modern and spec-compatible API.\n */\nexport function deprecated(oldMethod, newMethod) {\n if (!deprecationWarnings_) {\n return;\n }\n console.warn(oldMethod + ' is deprecated, please use ' + newMethod +\n ' instead.');\n}\n\n/**\n * Browser detector.\n *\n * @return {object} result containing browser and version\n * properties.\n */\nexport function detectBrowser(window) {\n // Returned result object.\n const result = {browser: null, version: null};\n\n // Fail early if it's not a browser\n if (typeof window === 'undefined' || !window.navigator) {\n result.browser = 'Not a browser.';\n return result;\n }\n\n const {navigator} = window;\n\n if (navigator.mozGetUserMedia) { // Firefox.\n result.browser = 'firefox';\n result.version = extractVersion(navigator.userAgent,\n /Firefox\\/(\\d+)\\./, 1);\n } else if (navigator.webkitGetUserMedia ||\n (window.isSecureContext === false && window.webkitRTCPeerConnection &&\n !window.RTCIceGatherer)) {\n // Chrome, Chromium, Webview, Opera.\n // Version matches Chrome/WebRTC version.\n // Chrome 74 removed webkitGetUserMedia on http as well so we need the\n // more complicated fallback to webkitRTCPeerConnection.\n result.browser = 'chrome';\n result.version = extractVersion(navigator.userAgent,\n /Chrom(e|ium)\\/(\\d+)\\./, 2);\n } else if (window.RTCPeerConnection &&\n navigator.userAgent.match(/AppleWebKit\\/(\\d+)\\./)) { // Safari.\n result.browser = 'safari';\n result.version = extractVersion(navigator.userAgent,\n /AppleWebKit\\/(\\d+)\\./, 1);\n result.supportsUnifiedPlan = window.RTCRtpTransceiver &&\n 'currentDirection' in window.RTCRtpTransceiver.prototype;\n } else { // Default fallthrough: not supported.\n result.browser = 'Not a supported browser.';\n return result;\n }\n\n return result;\n}\n\n/**\n * Checks if something is an object.\n *\n * @param {*} val The something you want to check.\n * @return true if val is an object, false otherwise.\n */\nfunction isObject(val) {\n return Object.prototype.toString.call(val) === '[object Object]';\n}\n\n/**\n * Remove all empty objects and undefined values\n * from a nested object -- an enhanced and vanilla version\n * of Lodash's `compact`.\n */\nexport function compactObject(data) {\n if (!isObject(data)) {\n return data;\n }\n\n return Object.keys(data).reduce(function(accumulator, key) {\n const isObj = isObject(data[key]);\n const value = isObj ? compactObject(data[key]) : data[key];\n const isEmptyObject = isObj && !Object.keys(value).length;\n if (value === undefined || isEmptyObject) {\n return accumulator;\n }\n return Object.assign(accumulator, {[key]: value});\n }, {});\n}\n\n/* iterates the stats graph recursively. */\nexport function walkStats(stats, base, resultSet) {\n if (!base || resultSet.has(base.id)) {\n return;\n }\n resultSet.set(base.id, base);\n Object.keys(base).forEach(name => {\n if (name.endsWith('Id')) {\n walkStats(stats, stats.get(base[name]), resultSet);\n } else if (name.endsWith('Ids')) {\n base[name].forEach(id => {\n walkStats(stats, stats.get(id), resultSet);\n });\n }\n });\n}\n\n/* filter getStats for a sender/receiver track. */\nexport function filterStats(result, track, outbound) {\n const streamStatsType = outbound ? 'outbound-rtp' : 'inbound-rtp';\n const filteredResult = new Map();\n if (track === null) {\n return filteredResult;\n }\n const trackStats = [];\n result.forEach(value => {\n if (value.type === 'track' &&\n value.trackIdentifier === track.id) {\n trackStats.push(value);\n }\n });\n trackStats.forEach(trackStat => {\n result.forEach(stats => {\n if (stats.type === streamStatsType && stats.trackId === trackStat.id) {\n walkStats(result, stats, filteredResult);\n }\n });\n });\n return filteredResult;\n}\n\n","/*\n * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by a BSD-style license\n * that can be found in the LICENSE file in the root of the source\n * tree.\n */\n/* eslint-env node */\n'use strict';\nimport * as utils from '../utils.js';\nconst logging = utils.log;\n\nexport function shimGetUserMedia(window, browserDetails) {\n const navigator = window && window.navigator;\n\n if (!navigator.mediaDevices) {\n return;\n }\n\n const constraintsToChrome_ = function(c) {\n if (typeof c !== 'object' || c.mandatory || c.optional) {\n return c;\n }\n const cc = {};\n Object.keys(c).forEach(key => {\n if (key === 'require' || key === 'advanced' || key === 'mediaSource') {\n return;\n }\n const r = (typeof c[key] === 'object') ? c[key] : {ideal: c[key]};\n if (r.exact !== undefined && typeof r.exact === 'number') {\n r.min = r.max = r.exact;\n }\n const oldname_ = function(prefix, name) {\n if (prefix) {\n return prefix + name.charAt(0).toUpperCase() + name.slice(1);\n }\n return (name === 'deviceId') ? 'sourceId' : name;\n };\n if (r.ideal !== undefined) {\n cc.optional = cc.optional || [];\n let oc = {};\n if (typeof r.ideal === 'number') {\n oc[oldname_('min', key)] = r.ideal;\n cc.optional.push(oc);\n oc = {};\n oc[oldname_('max', key)] = r.ideal;\n cc.optional.push(oc);\n } else {\n oc[oldname_('', key)] = r.ideal;\n cc.optional.push(oc);\n }\n }\n if (r.exact !== undefined && typeof r.exact !== 'number') {\n cc.mandatory = cc.mandatory || {};\n cc.mandatory[oldname_('', key)] = r.exact;\n } else {\n ['min', 'max'].forEach(mix => {\n if (r[mix] !== undefined) {\n cc.mandatory = cc.mandatory || {};\n cc.mandatory[oldname_(mix, key)] = r[mix];\n }\n });\n }\n });\n if (c.advanced) {\n cc.optional = (cc.optional || []).concat(c.advanced);\n }\n return cc;\n };\n\n const shimConstraints_ = function(constraints, func) {\n if (browserDetails.version >= 61) {\n return func(constraints);\n }\n constraints = JSON.parse(JSON.stringify(constraints));\n if (constraints && typeof constraints.audio === 'object') {\n const remap = function(obj, a, b) {\n if (a in obj && !(b in obj)) {\n obj[b] = obj[a];\n delete obj[a];\n }\n };\n constraints = JSON.parse(JSON.stringify(constraints));\n remap(constraints.audio, 'autoGainControl', 'googAutoGainControl');\n remap(constraints.audio, 'noiseSuppression', 'googNoiseSuppression');\n constraints.audio = constraintsToChrome_(constraints.audio);\n }\n if (constraints && typeof constraints.video === 'object') {\n // Shim facingMode for mobile & surface pro.\n let face = constraints.video.facingMode;\n face = face && ((typeof face === 'object') ? face : {ideal: face});\n const getSupportedFacingModeLies = browserDetails.version < 66;\n\n if ((face && (face.exact === 'user' || face.exact === 'environment' ||\n face.ideal === 'user' || face.ideal === 'environment')) &&\n !(navigator.mediaDevices.getSupportedConstraints &&\n navigator.mediaDevices.getSupportedConstraints().facingMode &&\n !getSupportedFacingModeLies)) {\n delete constraints.video.facingMode;\n let matches;\n if (face.exact === 'environment' || face.ideal === 'environment') {\n matches = ['back', 'rear'];\n } else if (face.exact === 'user' || face.ideal === 'user') {\n matches = ['front'];\n }\n if (matches) {\n // Look for matches in label, or use last cam for back (typical).\n return navigator.mediaDevices.enumerateDevices()\n .then(devices => {\n devices = devices.filter(d => d.kind === 'videoinput');\n let dev = devices.find(d => matches.some(match =>\n d.label.toLowerCase().includes(match)));\n if (!dev && devices.length && matches.includes('back')) {\n dev = devices[devices.length - 1]; // more likely the back cam\n }\n if (dev) {\n constraints.video.deviceId = face.exact ? {exact: dev.deviceId} :\n {ideal: dev.deviceId};\n }\n constraints.video = constraintsToChrome_(constraints.video);\n logging('chrome: ' + JSON.stringify(constraints));\n return func(constraints);\n });\n }\n }\n constraints.video = constraintsToChrome_(constraints.video);\n }\n logging('chrome: ' + JSON.stringify(constraints));\n return func(constraints);\n };\n\n const shimError_ = function(e) {\n if (browserDetails.version >= 64) {\n return e;\n }\n return {\n name: {\n PermissionDeniedError: 'NotAllowedError',\n PermissionDismissedError: 'NotAllowedError',\n InvalidStateError: 'NotAllowedError',\n DevicesNotFoundError: 'NotFoundError',\n ConstraintNotSatisfiedError: 'OverconstrainedError',\n TrackStartError: 'NotReadableError',\n MediaDeviceFailedDueToShutdown: 'NotAllowedError',\n MediaDeviceKillSwitchOn: 'NotAllowedError',\n TabCaptureError: 'AbortError',\n ScreenCaptureError: 'AbortError',\n DeviceCaptureError: 'AbortError'\n }[e.name] || e.name,\n message: e.message,\n constraint: e.constraint || e.constraintName,\n toString() {\n return this.name + (this.message && ': ') + this.message;\n }\n };\n };\n\n const getUserMedia_ = function(constraints, onSuccess, onError) {\n shimConstraints_(constraints, c => {\n navigator.webkitGetUserMedia(c, onSuccess, e => {\n if (onError) {\n onError(shimError_(e));\n }\n });\n });\n };\n navigator.getUserMedia = getUserMedia_.bind(navigator);\n\n // Even though Chrome 45 has navigator.mediaDevices and a getUserMedia\n // function which returns a Promise, it does not accept spec-style\n // constraints.\n if (navigator.mediaDevices.getUserMedia) {\n const origGetUserMedia = navigator.mediaDevices.getUserMedia.\n bind(navigator.mediaDevices);\n navigator.mediaDevices.getUserMedia = function(cs) {\n return shimConstraints_(cs, c => origGetUserMedia(c).then(stream => {\n if (c.audio && !stream.getAudioTracks().length ||\n c.video && !stream.getVideoTracks().length) {\n stream.getTracks().forEach(track => {\n track.stop();\n });\n throw new DOMException('', 'NotFoundError');\n }\n return stream;\n }, e => Promise.reject(shimError_(e))));\n };\n }\n}\n","/*\n * Copyright (c) 2018 The adapter.js project authors. All Rights Reserved.\n *\n * Use of this source code is governed by a BSD-style license\n * that can be found in the LICENSE file in the root of the source\n * tree.\n */\n/* eslint-env node */\n'use strict';\nexport function shimGetDisplayMedia(window, getSourceId) {\n if (window.navigator.mediaDevices &&\n 'getDisplayMedia' in window.navigator.mediaDevices) {\n return;\n }\n if (!(window.navigator.mediaDevices)) {\n return;\n }\n // getSourceId is a function that returns a promise resolving with\n // the sourceId of the screen/window/tab to be shared.\n if (typeof getSourceId !== 'function') {\n console.error('shimGetDisplayMedia: getSourceId argument is not ' +\n 'a function');\n return;\n }\n window.navigator.mediaDevices.getDisplayMedia =\n function getDisplayMedia(constraints) {\n return getSourceId(constraints)\n .then(sourceId => {\n const widthSpecified = constraints.video && constraints.video.width;\n const heightSpecified = constraints.video &&\n constraints.video.height;\n const frameRateSpecified = constraints.video &&\n constraints.video.frameRate;\n constraints.video = {\n mandatory: {\n chromeMediaSource: 'desktop',\n chromeMediaSourceId: sourceId,\n maxFrameRate: frameRateSpecified || 3\n }\n };\n if (widthSpecified) {\n constraints.video.mandatory.maxWidth = widthSpecified;\n }\n if (heightSpecified) {\n constraints.video.mandatory.maxHeight = heightSpecified;\n }\n return window.navigator.mediaDevices.getUserMedia(constraints);\n });\n };\n}\n","/*\n * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by a BSD-style license\n * that can be found in the LICENSE file in the root of the source\n * tree.\n */\n /* eslint-env node */\n'use strict';\nimport * as utils from '../utils.js';\n\nexport {shimGetUserMedia} from './getusermedia';\nexport {shimGetDisplayMedia} from './getdisplaymedia';\n\nexport function shimMediaStream(window) {\n window.MediaStream = window.MediaStream || window.webkitMediaStream;\n}\n\nexport function shimOnTrack(window) {\n if (typeof window === 'object' && window.RTCPeerConnection && !('ontrack' in\n window.RTCPeerConnection.prototype)) {\n Object.defineProperty(window.RTCPeerConnection.prototype, 'ontrack', {\n get() {\n return this._ontrack;\n },\n set(f) {\n if (this._ontrack) {\n this.removeEventListener('track', this._ontrack);\n }\n this.addEventListener('track', this._ontrack = f);\n },\n enumerable: true,\n configurable: true\n });\n const origSetRemoteDescription =\n window.RTCPeerConnection.prototype.setRemoteDescription;\n window.RTCPeerConnection.prototype.setRemoteDescription =\n function setRemoteDescription() {\n if (!this._ontrackpoly) {\n this._ontrackpoly = (e) => {\n // onaddstream does not fire when a track is added to an existing\n // stream. But stream.onaddtrack is implemented so we use that.\n e.stream.addEventListener('addtrack', te => {\n let receiver;\n if (window.RTCPeerConnection.prototype.getReceivers) {\n receiver = this.getReceivers()\n .find(r => r.track && r.track.id === te.track.id);\n } else {\n receiver = {track: te.track};\n }\n\n const event = new Event('track');\n event.track = te.track;\n event.receiver = receiver;\n event.transceiver = {receiver};\n event.streams = [e.stream];\n this.dispatchEvent(event);\n });\n e.stream.getTracks().forEach(track => {\n let receiver;\n if (window.RTCPeerConnection.prototype.getReceivers) {\n receiver = this.getReceivers()\n .find(r => r.track && r.track.id === track.id);\n } else {\n receiver = {track};\n }\n const event = new Event('track');\n event.track = track;\n event.receiver = receiver;\n event.transceiver = {receiver};\n event.streams = [e.stream];\n this.dispatchEvent(event);\n });\n };\n this.addEventListener('addstream', this._ontrackpoly);\n }\n return origSetRemoteDescription.apply(this, arguments);\n };\n } else {\n // even if RTCRtpTransceiver is in window, it is only used and\n // emitted in unified-plan. Unfortunately this means we need\n // to unconditionally wrap the event.\n utils.wrapPeerConnectionEvent(window, 'track', e => {\n if (!e.transceiver) {\n Object.defineProperty(e, 'transceiver',\n {value: {receiver: e.receiver}});\n }\n return e;\n });\n }\n}\n\nexport function shimGetSendersWithDtmf(window) {\n // Overrides addTrack/removeTrack, depends on shimAddTrackRemoveTrack.\n if (typeof window === 'object' && window.RTCPeerConnection &&\n !('getSenders' in window.RTCPeerConnection.prototype) &&\n 'createDTMFSender' in window.RTCPeerConnection.prototype) {\n const shimSenderWithDtmf = function(pc, track) {\n return {\n track,\n get dtmf() {\n if (this._dtmf === undefined) {\n if (track.kind === 'audio') {\n this._dtmf = pc.createDTMFSender(track);\n } else {\n this._dtmf = null;\n }\n }\n return this._dtmf;\n },\n _pc: pc\n };\n };\n\n // augment addTrack when getSenders is not available.\n if (!window.RTCPeerConnection.prototype.getSenders) {\n window.RTCPeerConnection.prototype.getSenders = function getSenders() {\n this._senders = this._senders || [];\n return this._senders.slice(); // return a copy of the internal state.\n };\n const origAddTrack = window.RTCPeerConnection.prototype.addTrack;\n window.RTCPeerConnection.prototype.addTrack =\n function addTrack(track, stream) {\n let sender = origAddTrack.apply(this, arguments);\n if (!sender) {\n sender = shimSenderWithDtmf(this, track);\n this._senders.push(sender);\n }\n return sender;\n };\n\n const origRemoveTrack = window.RTCPeerConnection.prototype.removeTrack;\n window.RTCPeerConnection.prototype.removeTrack =\n function removeTrack(sender) {\n origRemoveTrack.apply(this, arguments);\n const idx = this._senders.indexOf(sender);\n if (idx !== -1) {\n this._senders.splice(idx, 1);\n }\n };\n }\n const origAddStream = window.RTCPeerConnection.prototype.addStream;\n window.RTCPeerConnection.prototype.addStream = function addStream(stream) {\n this._senders = this._senders || [];\n origAddStream.apply(this, [stream]);\n stream.getTracks().forEach(track => {\n this._senders.push(shimSenderWithDtmf(this, track));\n });\n };\n\n const origRemoveStream = window.RTCPeerConnection.prototype.removeStream;\n window.RTCPeerConnection.prototype.removeStream =\n function removeStream(stream) {\n this._senders = this._senders || [];\n origRemoveStream.apply(this, [stream]);\n\n stream.getTracks().forEach(track => {\n const sender = this._senders.find(s => s.track === track);\n if (sender) { // remove sender\n this._senders.splice(this._senders.indexOf(sender), 1);\n }\n });\n };\n } else if (typeof window === 'object' && window.RTCPeerConnection &&\n 'getSenders' in window.RTCPeerConnection.prototype &&\n 'createDTMFSender' in window.RTCPeerConnection.prototype &&\n window.RTCRtpSender &&\n !('dtmf' in window.RTCRtpSender.prototype)) {\n const origGetSenders = window.RTCPeerConnection.prototype.getSenders;\n window.RTCPeerConnection.prototype.getSenders = function getSenders() {\n const senders = origGetSenders.apply(this, []);\n senders.forEach(sender => sender._pc = this);\n return senders;\n };\n\n Object.defineProperty(window.RTCRtpSender.prototype, 'dtmf', {\n get() {\n if (this._dtmf === undefined) {\n if (this.track.kind === 'audio') {\n this._dtmf = this._pc.createDTMFSender(this.track);\n } else {\n this._dtmf = null;\n }\n }\n return this._dtmf;\n }\n });\n }\n}\n\nexport function shimGetStats(window) {\n if (!window.RTCPeerConnection) {\n return;\n }\n\n const origGetStats = window.RTCPeerConnection.prototype.getStats;\n window.RTCPeerConnection.prototype.getStats = function getStats() {\n const [selector, onSucc, onErr] = arguments;\n\n // If selector is a function then we are in the old style stats so just\n // pass back the original getStats format to avoid breaking old users.\n if (arguments.length > 0 && typeof selector === 'function') {\n return origGetStats.apply(this, arguments);\n }\n\n // When spec-style getStats is supported, return those when called with\n // either no arguments or the selector argument is null.\n if (origGetStats.length === 0 && (arguments.length === 0 ||\n typeof selector !== 'function')) {\n return origGetStats.apply(this, []);\n }\n\n const fixChromeStats_ = function(response) {\n const standardReport = {};\n const reports = response.result();\n reports.forEach(report => {\n const standardStats = {\n id: report.id,\n timestamp: report.timestamp,\n type: {\n localcandidate: 'local-candidate',\n remotecandidate: 'remote-candidate'\n }[report.type] || report.type\n };\n report.names().forEach(name => {\n standardStats[name] = report.stat(name);\n });\n standardReport[standardStats.id] = standardStats;\n });\n\n return standardReport;\n };\n\n // shim getStats with maplike support\n const makeMapStats = function(stats) {\n return new Map(Object.keys(stats).map(key => [key, stats[key]]));\n };\n\n if (arguments.length >= 2) {\n const successCallbackWrapper_ = function(response) {\n onSucc(makeMapStats(fixChromeStats_(response)));\n };\n\n return origGetStats.apply(this, [successCallbackWrapper_,\n selector]);\n }\n\n // promise-support\n return new Promise((resolve, reject) => {\n origGetStats.apply(this, [\n function(response) {\n resolve(makeMapStats(fixChromeStats_(response)));\n }, reject]);\n }).then(onSucc, onErr);\n };\n}\n\nexport function shimSenderReceiverGetStats(window) {\n if (!(typeof window === 'object' && window.RTCPeerConnection &&\n window.RTCRtpSender && window.RTCRtpReceiver)) {\n return;\n }\n\n // shim sender stats.\n if (!('getStats' in window.RTCRtpSender.prototype)) {\n const origGetSenders = window.RTCPeerConnection.prototype.getSenders;\n if (origGetSenders) {\n window.RTCPeerConnection.prototype.getSenders = function getSenders() {\n const senders = origGetSenders.apply(this, []);\n senders.forEach(sender => sender._pc = this);\n return senders;\n };\n }\n\n const origAddTrack = window.RTCPeerConnection.prototype.addTrack;\n if (origAddTrack) {\n window.RTCPeerConnection.prototype.addTrack = function addTrack() {\n const sender = origAddTrack.apply(this, arguments);\n sender._pc = this;\n return sender;\n };\n }\n window.RTCRtpSender.prototype.getStats = function getStats() {\n const sender = this;\n return this._pc.getStats().then(result =>\n /* Note: this will include stats of all senders that\n * send a track with the same id as sender.track as\n * it is not possible to identify the RTCRtpSender.\n */\n utils.filterStats(result, sender.track, true));\n };\n }\n\n // shim receiver stats.\n if (!('getStats' in window.RTCRtpReceiver.prototype)) {\n const origGetReceivers = window.RTCPeerConnection.prototype.getReceivers;\n if (origGetReceivers) {\n window.RTCPeerConnection.prototype.getReceivers =\n function getReceivers() {\n const receivers = origGetReceivers.apply(this, []);\n receivers.forEach(receiver => receiver._pc = this);\n return receivers;\n };\n }\n utils.wrapPeerConnectionEvent(window, 'track', e => {\n e.receiver._pc = e.srcElement;\n return e;\n });\n window.RTCRtpReceiver.prototype.getStats = function getStats() {\n const receiver = this;\n return this._pc.getStats().then(result =>\n utils.filterStats(result, receiver.track, false));\n };\n }\n\n if (!('getStats' in window.RTCRtpSender.prototype &&\n 'getStats' in window.RTCRtpReceiver.prototype)) {\n return;\n }\n\n // shim RTCPeerConnection.getStats(track).\n const origGetStats = window.RTCPeerConnection.prototype.getStats;\n window.RTCPeerConnection.prototype.getStats = function getStats() {\n if (arguments.length > 0 &&\n arguments[0] instanceof window.MediaStreamTrack) {\n const track = arguments[0];\n let sender;\n let receiver;\n let err;\n this.getSenders().forEach(s => {\n if (s.track === track) {\n if (sender) {\n err = true;\n } else {\n sender = s;\n }\n }\n });\n this.getReceivers().forEach(r => {\n if (r.track === track) {\n if (receiver) {\n err = true;\n } else {\n receiver = r;\n }\n }\n return r.track === track;\n });\n if (err || (sender && receiver)) {\n return Promise.reject(new DOMException(\n 'There are more than one sender or receiver for the track.',\n 'InvalidAccessError'));\n } else if (sender) {\n return sender.getStats();\n } else if (receiver) {\n return receiver.getStats();\n }\n return Promise.reject(new DOMException(\n 'There is no sender or receiver for the track.',\n 'InvalidAccessError'));\n }\n return origGetStats.apply(this, arguments);\n };\n}\n\nexport function shimAddTrackRemoveTrackWithNative(window) {\n // shim addTrack/removeTrack with native variants in order to make\n // the interactions with legacy getLocalStreams behave as in other browsers.\n // Keeps a mapping stream.id => [stream, rtpsenders...]\n window.RTCPeerConnection.prototype.getLocalStreams =\n function getLocalStreams() {\n this._shimmedLocalStreams = this._shimmedLocalStreams || {};\n return Object.keys(this._shimmedLocalStreams)\n .map(streamId => this._shimmedLocalStreams[streamId][0]);\n };\n\n const origAddTrack = window.RTCPeerConnection.prototype.addTrack;\n window.RTCPeerConnection.prototype.addTrack =\n function addTrack(track, stream) {\n if (!stream) {\n return origAddTrack.apply(this, arguments);\n }\n this._shimmedLocalStreams = this._shimmedLocalStreams || {};\n\n const sender = origAddTrack.apply(this, arguments);\n if (!this._shimmedLocalStreams[stream.id]) {\n this._shimmedLocalStreams[stream.id] = [stream, sender];\n } else if (this._shimmedLocalStreams[stream.id].indexOf(sender) === -1) {\n this._shimmedLocalStreams[stream.id].push(sender);\n }\n return sender;\n };\n\n const origAddStream = window.RTCPeerConnection.prototype.addStream;\n window.RTCPeerConnection.prototype.addStream = function addStream(stream) {\n this._shimmedLocalStreams = this._shimmedLocalStreams || {};\n\n stream.getTracks().forEach(track => {\n const alreadyExists = this.getSenders().find(s => s.track === track);\n if (alreadyExists) {\n throw new DOMException('Track already exists.',\n 'InvalidAccessError');\n }\n });\n const existingSenders = this.getSenders();\n origAddStream.apply(this, arguments);\n const newSenders = this.getSenders()\n .filter(newSender => existingSenders.indexOf(newSender) === -1);\n this._shimmedLocalStreams[stream.id] = [stream].concat(newSenders);\n };\n\n const origRemoveStream = window.RTCPeerConnection.prototype.removeStream;\n window.RTCPeerConnection.prototype.removeStream =\n function removeStream(stream) {\n this._shimmedLocalStreams = this._shimmedLocalStreams || {};\n delete this._shimmedLocalStreams[stream.id];\n return origRemoveStream.apply(this, arguments);\n };\n\n const origRemoveTrack = window.RTCPeerConnection.prototype.removeTrack;\n window.RTCPeerConnection.prototype.removeTrack =\n function removeTrack(sender) {\n this._shimmedLocalStreams = this._shimmedLocalStreams || {};\n if (sender) {\n Object.keys(this._shimmedLocalStreams).forEach(streamId => {\n const idx = this._shimmedLocalStreams[streamId].indexOf(sender);\n if (idx !== -1) {\n this._shimmedLocalStreams[streamId].splice(idx, 1);\n }\n if (this._shimmedLocalStreams[streamId].length === 1) {\n delete this._shimmedLocalStreams[streamId];\n }\n });\n }\n return origRemoveTrack.apply(this, arguments);\n };\n}\n\nexport function shimAddTrackRemoveTrack(window, browserDetails) {\n if (!window.RTCPeerConnection) {\n return;\n }\n // shim addTrack and removeTrack.\n if (window.RTCPeerConnection.prototype.addTrack &&\n browserDetails.version >= 65) {\n return shimAddTrackRemoveTrackWithNative(window);\n }\n\n // also shim pc.getLocalStreams when addTrack is shimmed\n // to return the original streams.\n const origGetLocalStreams = window.RTCPeerConnection.prototype\n .getLocalStreams;\n window.RTCPeerConnection.prototype.getLocalStreams =\n function getLocalStreams() {\n const nativeStreams = origGetLocalStreams.apply(this);\n this._reverseStreams = this._reverseStreams || {};\n return nativeStreams.map(stream => this._reverseStreams[stream.id]);\n };\n\n const origAddStream = window.RTCPeerConnection.prototype.addStream;\n window.RTCPeerConnection.prototype.addStream = function addStream(stream) {\n this._streams = this._streams || {};\n this._reverseStreams = this._reverseStreams || {};\n\n stream.getTracks().forEach(track => {\n const alreadyExists = this.getSenders().find(s => s.track === track);\n if (alreadyExists) {\n throw new DOMException('Track already exists.',\n 'InvalidAccessError');\n }\n });\n // Add identity mapping for consistency with addTrack.\n // Unless this is being used with a stream from addTrack.\n if (!this._reverseStreams[stream.id]) {\n const newStream = new window.MediaStream(stream.getTracks());\n this._streams[stream.id] = newStream;\n this._reverseStreams[newStream.id] = stream;\n stream = newStream;\n }\n origAddStream.apply(this, [stream]);\n };\n\n const origRemoveStream = window.RTCPeerConnection.prototype.removeStream;\n window.RTCPeerConnection.prototype.removeStream =\n function removeStream(stream) {\n this._streams = this._streams || {};\n this._reverseStreams = this._reverseStreams || {};\n\n origRemoveStream.apply(this, [(this._streams[stream.id] || stream)]);\n delete this._reverseStreams[(this._streams[stream.id] ?\n this._streams[stream.id].id : stream.id)];\n delete this._streams[stream.id];\n };\n\n window.RTCPeerConnection.prototype.addTrack =\n function addTrack(track, stream) {\n if (this.signalingState === 'closed') {\n throw new DOMException(\n 'The RTCPeerConnection\\'s signalingState is \\'closed\\'.',\n 'InvalidStateError');\n }\n const streams = [].slice.call(arguments, 1);\n if (streams.length !== 1 ||\n !streams[0].getTracks().find(t => t === track)) {\n // this is not fully correct but all we can manage without\n // [[associated MediaStreams]] internal slot.\n throw new DOMException(\n 'The adapter.js addTrack polyfill only supports a single ' +\n ' stream which is associated with the specified track.',\n 'NotSupportedError');\n }\n\n const alreadyExists = this.getSenders().find(s => s.track === track);\n if (alreadyExists) {\n throw new DOMException('Track already exists.',\n 'InvalidAccessError');\n }\n\n this._streams = this._streams || {};\n this._reverseStreams = this._reverseStreams || {};\n const oldStream = this._streams[stream.id];\n if (oldStream) {\n // this is using odd Chrome behaviour, use with caution:\n // https://bugs.chromium.org/p/webrtc/issues/detail?id=7815\n // Note: we rely on the high-level addTrack/dtmf shim to\n // create the sender with a dtmf sender.\n oldStream.addTrack(track);\n\n // Trigger ONN async.\n Promise.resolve().then(() => {\n this.dispatchEvent(new Event('negotiationneeded'));\n });\n } else {\n const newStream = new window.MediaStream([track]);\n this._streams[stream.id] = newStream;\n this._reverseStreams[newStream.id] = stream;\n this.addStream(newStream);\n }\n return this.getSenders().find(s => s.track === track);\n };\n\n // replace the internal stream id with the external one and\n // vice versa.\n function replaceInternalStreamId(pc, description) {\n let sdp = description.sdp;\n Object.keys(pc._reverseStreams || []).forEach(internalId => {\n const externalStream = pc._reverseStreams[internalId];\n const internalStream = pc._streams[externalStream.id];\n sdp = sdp.replace(new RegExp(internalStream.id, 'g'),\n externalStream.id);\n });\n return new RTCSessionDescription({\n type: description.type,\n sdp\n });\n }\n function replaceExternalStreamId(pc, description) {\n let sdp = description.sdp;\n Object.keys(pc._reverseStreams || []).forEach(internalId => {\n const externalStream = pc._reverseStreams[internalId];\n const internalStream = pc._streams[externalStream.id];\n sdp = sdp.replace(new RegExp(externalStream.id, 'g'),\n internalStream.id);\n });\n return new RTCSessionDescription({\n type: description.type,\n sdp\n });\n }\n ['createOffer', 'createAnswer'].forEach(function(method) {\n const nativeMethod = window.RTCPeerConnection.prototype[method];\n const methodObj = {[method]() {\n const args = arguments;\n const isLegacyCall = arguments.length &&\n typeof arguments[0] === 'function';\n if (isLegacyCall) {\n return nativeMethod.apply(this, [\n (description) => {\n const desc = replaceInternalStreamId(this, description);\n args[0].apply(null, [desc]);\n },\n (err) => {\n if (args[1]) {\n args[1].apply(null, err);\n }\n }, arguments[2]\n ]);\n }\n return nativeMethod.apply(this, arguments)\n .then(description => replaceInternalStreamId(this, description));\n }};\n window.RTCPeerConnection.prototype[method] = methodObj[method];\n });\n\n const origSetLocalDescription =\n window.RTCPeerConnection.prototype.setLocalDescription;\n window.RTCPeerConnection.prototype.setLocalDescription =\n function setLocalDescription() {\n if (!arguments.length || !arguments[0].type) {\n return origSetLocalDescription.apply(this, arguments);\n }\n arguments[0] = replaceExternalStreamId(this, arguments[0]);\n return origSetLocalDescription.apply(this, arguments);\n };\n\n // TODO: mangle getStats: https://w3c.github.io/webrtc-stats/#dom-rtcmediastreamstats-streamidentifier\n\n const origLocalDescription = Object.getOwnPropertyDescriptor(\n window.RTCPeerConnection.prototype, 'localDescription');\n Object.defineProperty(window.RTCPeerConnection.prototype,\n 'localDescription', {\n get() {\n const description = origLocalDescription.get.apply(this);\n if (description.type === '') {\n return description;\n }\n return replaceInternalStreamId(this, description);\n }\n });\n\n window.RTCPeerConnection.prototype.removeTrack =\n function removeTrack(sender) {\n if (this.signalingState === 'closed') {\n throw new DOMException(\n 'The RTCPeerConnection\\'s signalingState is \\'closed\\'.',\n 'InvalidStateError');\n }\n // We can not yet check for sender instanceof RTCRtpSender\n // since we shim RTPSender. So we check if sender._pc is set.\n if (!sender._pc) {\n throw new DOMException('Argument 1 of RTCPeerConnection.removeTrack ' +\n 'does not implement interface RTCRtpSender.', 'TypeError');\n }\n const isLocal = sender._pc === this;\n if (!isLocal) {\n throw new DOMException('Sender was not created by this connection.',\n 'InvalidAccessError');\n }\n\n // Search for the native stream the senders track belongs to.\n this._streams = this._streams || {};\n let stream;\n Object.keys(this._streams).forEach(streamid => {\n const hasTrack = this._streams[streamid].getTracks()\n .find(track => sender.track === track);\n if (hasTrack) {\n stream = this._streams[streamid];\n }\n });\n\n if (stream) {\n if (stream.getTracks().length === 1) {\n // if this is the last track of the stream, remove the stream. This\n // takes care of any shimmed _senders.\n this.removeStream(this._reverseStreams[stream.id]);\n } else {\n // relying on the same odd chrome behaviour as above.\n stream.removeTrack(sender.track);\n }\n this.dispatchEvent(new Event('negotiationneeded'));\n }\n };\n}\n\nexport function shimPeerConnection(window, browserDetails) {\n if (!window.RTCPeerConnection && window.webkitRTCPeerConnection) {\n // very basic support for old versions.\n window.RTCPeerConnection = window.webkitRTCPeerConnection;\n }\n if (!window.RTCPeerConnection) {\n return;\n }\n\n // shim implicit creation of RTCSessionDescription/RTCIceCandidate\n if (browserDetails.version < 53) {\n ['setLocalDescription', 'setRemoteDescription', 'addIceCandidate']\n .forEach(function(method) {\n const nativeMethod = window.RTCPeerConnection.prototype[method];\n const methodObj = {[method]() {\n arguments[0] = new ((method === 'addIceCandidate') ?\n window.RTCIceCandidate :\n window.RTCSessionDescription)(arguments[0]);\n return nativeMethod.apply(this, arguments);\n }};\n window.RTCPeerConnection.prototype[method] = methodObj[method];\n });\n }\n}\n\n// Attempt to fix ONN in plan-b mode.\nexport function fixNegotiationNeeded(window, browserDetails) {\n utils.wrapPeerConnectionEvent(window, 'negotiationneeded', e => {\n const pc = e.target;\n if (browserDetails.version < 72 || (pc.getConfiguration &&\n pc.getConfiguration().sdpSemantics === 'plan-b')) {\n if (pc.signalingState !== 'stable') {\n return;\n }\n }\n return e;\n });\n}\n","/*\n * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by a BSD-style license\n * that can be found in the LICENSE file in the root of the source\n * tree.\n */\n/* eslint-env node */\n'use strict';\n\nimport * as utils from '../utils';\n\nexport function shimGetUserMedia(window, browserDetails) {\n const navigator = window && window.navigator;\n const MediaStreamTrack = window && window.MediaStreamTrack;\n\n navigator.getUserMedia = function(constraints, onSuccess, onError) {\n // Replace Firefox 44+'s deprecation warning with unprefixed version.\n utils.deprecated('navigator.getUserMedia',\n 'navigator.mediaDevices.getUserMedia');\n navigator.mediaDevices.getUserMedia(constraints).then(onSuccess, onError);\n };\n\n if (!(browserDetails.version > 55 &&\n 'autoGainControl' in navigator.mediaDevices.getSupportedConstraints())) {\n const remap = function(obj, a, b) {\n if (a in obj && !(b in obj)) {\n obj[b] = obj[a];\n delete obj[a];\n }\n };\n\n const nativeGetUserMedia = navigator.mediaDevices.getUserMedia.\n bind(navigator.mediaDevices);\n navigator.mediaDevices.getUserMedia = function(c) {\n if (typeof c === 'object' && typeof c.audio === 'object') {\n c = JSON.parse(JSON.stringify(c));\n remap(c.audio, 'autoGainControl', 'mozAutoGainControl');\n remap(c.audio, 'noiseSuppression', 'mozNoiseSuppression');\n }\n return nativeGetUserMedia(c);\n };\n\n if (MediaStreamTrack && MediaStreamTrack.prototype.getSettings) {\n const nativeGetSettings = MediaStreamTrack.prototype.getSettings;\n MediaStreamTrack.prototype.getSettings = function() {\n const obj = nativeGetSettings.apply(this, arguments);\n remap(obj, 'mozAutoGainControl', 'autoGainControl');\n remap(obj, 'mozNoiseSuppression', 'noiseSuppression');\n return obj;\n };\n }\n\n if (MediaStreamTrack && MediaStreamTrack.prototype.applyConstraints) {\n const nativeApplyConstraints =\n MediaStreamTrack.prototype.applyConstraints;\n MediaStreamTrack.prototype.applyConstraints = function(c) {\n if (this.kind === 'audio' && typeof c === 'object') {\n c = JSON.parse(JSON.stringify(c));\n remap(c, 'autoGainControl', 'mozAutoGainControl');\n remap(c, 'noiseSuppression', 'mozNoiseSuppression');\n }\n return nativeApplyConstraints.apply(this, [c]);\n };\n }\n }\n}\n","/*\n * Copyright (c) 2018 The adapter.js project authors. All Rights Reserved.\n *\n * Use of this source code is governed by a BSD-style license\n * that can be found in the LICENSE file in the root of the source\n * tree.\n */\n/* eslint-env node */\n'use strict';\n\nexport function shimGetDisplayMedia(window, preferredMediaSource) {\n if (window.navigator.mediaDevices &&\n 'getDisplayMedia' in window.navigator.mediaDevices) {\n return;\n }\n if (!(window.navigator.mediaDevices)) {\n return;\n }\n window.navigator.mediaDevices.getDisplayMedia =\n function getDisplayMedia(constraints) {\n if (!(constraints && constraints.video)) {\n const err = new DOMException('getDisplayMedia without video ' +\n 'constraints is undefined');\n err.name = 'NotFoundError';\n // from https://heycam.github.io/webidl/#idl-DOMException-error-names\n err.code = 8;\n return Promise.reject(err);\n }\n if (constraints.video === true) {\n constraints.video = {mediaSource: preferredMediaSource};\n } else {\n constraints.video.mediaSource = preferredMediaSource;\n }\n return window.navigator.mediaDevices.getUserMedia(constraints);\n };\n}\n","/*\n * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by a BSD-style license\n * that can be found in the LICENSE file in the root of the source\n * tree.\n */\n/* eslint-env node */\n'use strict';\n\nimport * as utils from '../utils';\nexport {shimGetUserMedia} from './getusermedia';\nexport {shimGetDisplayMedia} from './getdisplaymedia';\n\nexport function shimOnTrack(window) {\n if (typeof window === 'object' && window.RTCTrackEvent &&\n ('receiver' in window.RTCTrackEvent.prototype) &&\n !('transceiver' in window.RTCTrackEvent.prototype)) {\n Object.defineProperty(window.RTCTrackEvent.prototype, 'transceiver', {\n get() {\n return {receiver: this.receiver};\n }\n });\n }\n}\n\nexport function shimPeerConnection(window, browserDetails) {\n if (typeof window !== 'object' ||\n !(window.RTCPeerConnection || window.mozRTCPeerConnection)) {\n return; // probably media.peerconnection.enabled=false in about:config\n }\n if (!window.RTCPeerConnection && window.mozRTCPeerConnection) {\n // very basic support for old versions.\n window.RTCPeerConnection = window.mozRTCPeerConnection;\n }\n\n if (browserDetails.version < 53) {\n // shim away need for obsolete RTCIceCandidate/RTCSessionDescription.\n ['setLocalDescription', 'setRemoteDescription', 'addIceCandidate']\n .forEach(function(method) {\n const nativeMethod = window.RTCPeerConnection.prototype[method];\n const methodObj = {[method]() {\n arguments[0] = new ((method === 'addIceCandidate') ?\n window.RTCIceCandidate :\n window.RTCSessionDescription)(arguments[0]);\n return nativeMethod.apply(this, arguments);\n }};\n window.RTCPeerConnection.prototype[method] = methodObj[method];\n });\n }\n\n const modernStatsTypes = {\n inboundrtp: 'inbound-rtp',\n outboundrtp: 'outbound-rtp',\n candidatepair: 'candidate-pair',\n localcandidate: 'local-candidate',\n remotecandidate: 'remote-candidate'\n };\n\n const nativeGetStats = window.RTCPeerConnection.prototype.getStats;\n window.RTCPeerConnection.prototype.getStats = function getStats() {\n const [selector, onSucc, onErr] = arguments;\n return nativeGetStats.apply(this, [selector || null])\n .then(stats => {\n if (browserDetails.version < 53 && !onSucc) {\n // Shim only promise getStats with spec-hyphens in type names\n // Leave callback version alone; misc old uses of forEach before Map\n try {\n stats.forEach(stat => {\n stat.type = modernStatsTypes[stat.type] || stat.type;\n });\n } catch (e) {\n if (e.name !== 'TypeError') {\n throw e;\n }\n // Avoid TypeError: \"type\" is read-only, in old versions. 34-43ish\n stats.forEach((stat, i) => {\n stats.set(i, Object.assign({}, stat, {\n type: modernStatsTypes[stat.type] || stat.type\n }));\n });\n }\n }\n return stats;\n })\n .then(onSucc, onErr);\n };\n}\n\nexport function shimSenderGetStats(window) {\n if (!(typeof window === 'object' && window.RTCPeerConnection &&\n window.RTCRtpSender)) {\n return;\n }\n if (window.RTCRtpSender && 'getStats' in window.RTCRtpSender.prototype) {\n return;\n }\n const origGetSenders = window.RTCPeerConnection.prototype.getSenders;\n if (origGetSenders) {\n window.RTCPeerConnection.prototype.getSenders = function getSenders() {\n const senders = origGetSenders.apply(this, []);\n senders.forEach(sender => sender._pc = this);\n return senders;\n };\n }\n\n const origAddTrack = window.RTCPeerConnection.prototype.addTrack;\n if (origAddTrack) {\n window.RTCPeerConnection.prototype.addTrack = function addTrack() {\n const sender = origAddTrack.apply(this, arguments);\n sender._pc = this;\n return sender;\n };\n }\n window.RTCRtpSender.prototype.getStats = function getStats() {\n return this.track ? this._pc.getStats(this.track) :\n Promise.resolve(new Map());\n };\n}\n\nexport function shimReceiverGetStats(window) {\n if (!(typeof window === 'object' && window.RTCPeerConnection &&\n window.RTCRtpSender)) {\n return;\n }\n if (window.RTCRtpSender && 'getStats' in window.RTCRtpReceiver.prototype) {\n return;\n }\n const origGetReceivers = window.RTCPeerConnection.prototype.getReceivers;\n if (origGetReceivers) {\n window.RTCPeerConnection.prototype.getReceivers = function getReceivers() {\n const receivers = origGetReceivers.apply(this, []);\n receivers.forEach(receiver => receiver._pc = this);\n return receivers;\n };\n }\n utils.wrapPeerConnectionEvent(window, 'track', e => {\n e.receiver._pc = e.srcElement;\n return e;\n });\n window.RTCRtpReceiver.prototype.getStats = function getStats() {\n return this._pc.getStats(this.track);\n };\n}\n\nexport function shimRemoveStream(window) {\n if (!window.RTCPeerConnection ||\n 'removeStream' in window.RTCPeerConnection.prototype) {\n return;\n }\n window.RTCPeerConnection.prototype.removeStream =\n function removeStream(stream) {\n utils.deprecated('removeStream', 'removeTrack');\n this.getSenders().forEach(sender => {\n if (sender.track && stream.getTracks().includes(sender.track)) {\n this.removeTrack(sender);\n }\n });\n };\n}\n\nexport function shimRTCDataChannel(window) {\n // rename DataChannel to RTCDataChannel (native fix in FF60):\n // https://bugzilla.mozilla.org/show_bug.cgi?id=1173851\n if (window.DataChannel && !window.RTCDataChannel) {\n window.RTCDataChannel = window.DataChannel;\n }\n}\n\nexport function shimAddTransceiver(window) {\n // https://github.com/webrtcHacks/adapter/issues/998#issuecomment-516921647\n // Firefox ignores the init sendEncodings options passed to addTransceiver\n // https://bugzilla.mozilla.org/show_bug.cgi?id=1396918\n if (!(typeof window === 'object' && window.RTCPeerConnection)) {\n return;\n }\n const origAddTransceiver = window.RTCPeerConnection.prototype.addTransceiver;\n if (origAddTransceiver) {\n window.RTCPeerConnection.prototype.addTransceiver =\n function addTransceiver() {\n this.setParametersPromises = [];\n const initParameters = arguments[1];\n const shouldPerformCheck = initParameters &&\n 'sendEncodings' in initParameters;\n if (shouldPerformCheck) {\n // If sendEncodings params are provided, validate grammar\n initParameters.sendEncodings.forEach((encodingParam) => {\n if ('rid' in encodingParam) {\n const ridRegex = /^[a-z0-9]{0,16}$/i;\n if (!ridRegex.test(encodingParam.rid)) {\n throw new TypeError('Invalid RID value provided.');\n }\n }\n if ('scaleResolutionDownBy' in encodingParam) {\n if (!(parseFloat(encodingParam.scaleResolutionDownBy) >= 1.0)) {\n throw new RangeError('scale_resolution_down_by must be >= 1.0');\n }\n }\n if ('maxFramerate' in encodingParam) {\n if (!(parseFloat(encodingParam.maxFramerate) >= 0)) {\n throw new RangeError('max_framerate must be >= 0.0');\n }\n }\n });\n }\n const transceiver = origAddTransceiver.apply(this, arguments);\n if (shouldPerformCheck) {\n // Check if the init options were applied. If not we do this in an\n // asynchronous way and save the promise reference in a global object.\n // This is an ugly hack, but at the same time is way more robust than\n // checking the sender parameters before and after the createOffer\n // Also note that after the createoffer we are not 100% sure that\n // the params were asynchronously applied so we might miss the\n // opportunity to recreate offer.\n const {sender} = transceiver;\n const params = sender.getParameters();\n if (!('encodings' in params) ||\n // Avoid being fooled by patched getParameters() below.\n (params.encodings.length === 1 &&\n Object.keys(params.encodings[0]).length === 0)) {\n params.encodings = initParameters.sendEncodings;\n sender.sendEncodings = initParameters.sendEncodings;\n this.setParametersPromises.push(sender.setParameters(params)\n .then(() => {\n delete sender.sendEncodings;\n }).catch(() => {\n delete sender.sendEncodings;\n })\n );\n }\n }\n return transceiver;\n };\n }\n}\n\nexport function shimGetParameters(window) {\n if (!(typeof window === 'object' && window.RTCRtpSender)) {\n return;\n }\n const origGetParameters = window.RTCRtpSender.prototype.getParameters;\n if (origGetParameters) {\n window.RTCRtpSender.prototype.getParameters =\n function getParameters() {\n const params = origGetParameters.apply(this, arguments);\n if (!('encodings' in params)) {\n params.encodings = [].concat(this.sendEncodings || [{}]);\n }\n return params;\n };\n }\n}\n\nexport function shimCreateOffer(window) {\n // https://github.com/webrtcHacks/adapter/issues/998#issuecomment-516921647\n // Firefox ignores the init sendEncodings options passed to addTransceiver\n // https://bugzilla.mozilla.org/show_bug.cgi?id=1396918\n if (!(typeof window === 'object' && window.RTCPeerConnection)) {\n return;\n }\n const origCreateOffer = window.RTCPeerConnection.prototype.createOffer;\n window.RTCPeerConnection.prototype.createOffer = function createOffer() {\n if (this.setParametersPromises && this.setParametersPromises.length) {\n return Promise.all(this.setParametersPromises)\n .then(() => {\n return origCreateOffer.apply(this, arguments);\n })\n .finally(() => {\n this.setParametersPromises = [];\n });\n }\n return origCreateOffer.apply(this, arguments);\n };\n}\n\nexport function shimCreateAnswer(window) {\n // https://github.com/webrtcHacks/adapter/issues/998#issuecomment-516921647\n // Firefox ignores the init sendEncodings options passed to addTransceiver\n // https://bugzilla.mozilla.org/show_bug.cgi?id=1396918\n if (!(typeof window === 'object' && window.RTCPeerConnection)) {\n return;\n }\n const origCreateAnswer = window.RTCPeerConnection.prototype.createAnswer;\n window.RTCPeerConnection.prototype.createAnswer = function createAnswer() {\n if (this.setParametersPromises && this.setParametersPromises.length) {\n return Promise.all(this.setParametersPromises)\n .then(() => {\n return origCreateAnswer.apply(this, arguments);\n })\n .finally(() => {\n this.setParametersPromises = [];\n });\n }\n return origCreateAnswer.apply(this, arguments);\n };\n}\n","/*\n * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by a BSD-style license\n * that can be found in the LICENSE file in the root of the source\n * tree.\n */\n'use strict';\nimport * as utils from '../utils';\n\nexport function shimLocalStreamsAPI(window) {\n if (typeof window !== 'object' || !window.RTCPeerConnection) {\n return;\n }\n if (!('getLocalStreams' in window.RTCPeerConnection.prototype)) {\n window.RTCPeerConnection.prototype.getLocalStreams =\n function getLocalStreams() {\n if (!this._localStreams) {\n this._localStreams = [];\n }\n return this._localStreams;\n };\n }\n if (!('addStream' in window.RTCPeerConnection.prototype)) {\n const _addTrack = window.RTCPeerConnection.prototype.addTrack;\n window.RTCPeerConnection.prototype.addStream = function addStream(stream) {\n if (!this._localStreams) {\n this._localStreams = [];\n }\n if (!this._localStreams.includes(stream)) {\n this._localStreams.push(stream);\n }\n // Try to emulate Chrome's behaviour of adding in audio-video order.\n // Safari orders by track id.\n stream.getAudioTracks().forEach(track => _addTrack.call(this, track,\n stream));\n stream.getVideoTracks().forEach(track => _addTrack.call(this, track,\n stream));\n };\n\n window.RTCPeerConnection.prototype.addTrack =\n function addTrack(track, ...streams) {\n if (streams) {\n streams.forEach((stream) => {\n if (!this._localStreams) {\n this._localStreams = [stream];\n } else if (!this._localStreams.includes(stream)) {\n this._localStreams.push(stream);\n }\n });\n }\n return _addTrack.apply(this, arguments);\n };\n }\n if (!('removeStream' in window.RTCPeerConnection.prototype)) {\n window.RTCPeerConnection.prototype.removeStream =\n function removeStream(stream) {\n if (!this._localStreams) {\n this._localStreams = [];\n }\n const index = this._localStreams.indexOf(stream);\n if (index === -1) {\n return;\n }\n this._localStreams.splice(index, 1);\n const tracks = stream.getTracks();\n this.getSenders().forEach(sender => {\n if (tracks.includes(sender.track)) {\n this.removeTrack(sender);\n }\n });\n };\n }\n}\n\nexport function shimRemoteStreamsAPI(window) {\n if (typeof window !== 'object' || !window.RTCPeerConnection) {\n return;\n }\n if (!('getRemoteStreams' in window.RTCPeerConnection.prototype)) {\n window.RTCPeerConnection.prototype.getRemoteStreams =\n function getRemoteStreams() {\n return this._remoteStreams ? this._remoteStreams : [];\n };\n }\n if (!('onaddstream' in window.RTCPeerConnection.prototype)) {\n Object.defineProperty(window.RTCPeerConnection.prototype, 'onaddstream', {\n get() {\n return this._onaddstream;\n },\n set(f) {\n if (this._onaddstream) {\n this.removeEventListener('addstream', this._onaddstream);\n this.removeEventListener('track', this._onaddstreampoly);\n }\n this.addEventListener('addstream', this._onaddstream = f);\n this.addEventListener('track', this._onaddstreampoly = (e) => {\n e.streams.forEach(stream => {\n if (!this._remoteStreams) {\n this._remoteStreams = [];\n }\n if (this._remoteStreams.includes(stream)) {\n return;\n }\n this._remoteStreams.push(stream);\n const event = new Event('addstream');\n event.stream = stream;\n this.dispatchEvent(event);\n });\n });\n }\n });\n const origSetRemoteDescription =\n window.RTCPeerConnection.prototype.setRemoteDescription;\n window.RTCPeerConnection.prototype.setRemoteDescription =\n function setRemoteDescription() {\n const pc = this;\n if (!this._onaddstreampoly) {\n this.addEventListener('track', this._onaddstreampoly = function(e) {\n e.streams.forEach(stream => {\n if (!pc._remoteStreams) {\n pc._remoteStreams = [];\n }\n if (pc._remoteStreams.indexOf(stream) >= 0) {\n return;\n }\n pc._remoteStreams.push(stream);\n const event = new Event('addstream');\n event.stream = stream;\n pc.dispatchEvent(event);\n });\n });\n }\n return origSetRemoteDescription.apply(pc, arguments);\n };\n }\n}\n\nexport function shimCallbacksAPI(window) {\n if (typeof window !== 'object' || !window.RTCPeerConnection) {\n return;\n }\n const prototype = window.RTCPeerConnection.prototype;\n const origCreateOffer = prototype.createOffer;\n const origCreateAnswer = prototype.createAnswer;\n const setLocalDescription = prototype.setLocalDescription;\n const setRemoteDescription = prototype.setRemoteDescription;\n const addIceCandidate = prototype.addIceCandidate;\n\n prototype.createOffer =\n function createOffer(successCallback, failureCallback) {\n const options = (arguments.length >= 2) ? arguments[2] : arguments[0];\n const promise = origCreateOffer.apply(this, [options]);\n if (!failureCallback) {\n return promise;\n }\n promise.then(successCallback, failureCallback);\n return Promise.resolve();\n };\n\n prototype.createAnswer =\n function createAnswer(successCallback, failureCallback) {\n const options = (arguments.length >= 2) ? arguments[2] : arguments[0];\n const promise = origCreateAnswer.apply(this, [options]);\n if (!failureCallback) {\n return promise;\n }\n promise.then(successCallback, failureCallback);\n return Promise.resolve();\n };\n\n let withCallback = function(description, successCallback, failureCallback) {\n const promise = setLocalDescription.apply(this, [description]);\n if (!failureCallback) {\n return promise;\n }\n promise.then(successCallback, failureCallback);\n return Promise.resolve();\n };\n prototype.setLocalDescription = withCallback;\n\n withCallback = function(description, successCallback, failureCallback) {\n const promise = setRemoteDescription.apply(this, [description]);\n if (!failureCallback) {\n return promise;\n }\n promise.then(successCallback, failureCallback);\n return Promise.resolve();\n };\n prototype.setRemoteDescription = withCallback;\n\n withCallback = function(candidate, successCallback, failureCallback) {\n const promise = addIceCandidate.apply(this, [candidate]);\n if (!failureCallback) {\n return promise;\n }\n promise.then(successCallback, failureCallback);\n return Promise.resolve();\n };\n prototype.addIceCandidate = withCallback;\n}\n\nexport function shimGetUserMedia(window) {\n const navigator = window && window.navigator;\n\n if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {\n // shim not needed in Safari 12.1\n const mediaDevices = navigator.mediaDevices;\n const _getUserMedia = mediaDevices.getUserMedia.bind(mediaDevices);\n navigator.mediaDevices.getUserMedia = (constraints) => {\n return _getUserMedia(shimConstraints(constraints));\n };\n }\n\n if (!navigator.getUserMedia && navigator.mediaDevices &&\n navigator.mediaDevices.getUserMedia) {\n navigator.getUserMedia = function getUserMedia(constraints, cb, errcb) {\n navigator.mediaDevices.getUserMedia(constraints)\n .then(cb, errcb);\n }.bind(navigator);\n }\n}\n\nexport function shimConstraints(constraints) {\n if (constraints && constraints.video !== undefined) {\n return Object.assign({},\n constraints,\n {video: utils.compactObject(constraints.video)}\n );\n }\n\n return constraints;\n}\n\nexport function shimRTCIceServerUrls(window) {\n if (!window.RTCPeerConnection) {\n return;\n }\n // migrate from non-spec RTCIceServer.url to RTCIceServer.urls\n const OrigPeerConnection = window.RTCPeerConnection;\n window.RTCPeerConnection =\n function RTCPeerConnection(pcConfig, pcConstraints) {\n if (pcConfig && pcConfig.iceServers) {\n const newIceServers = [];\n for (let i = 0; i < pcConfig.iceServers.length; i++) {\n let server = pcConfig.iceServers[i];\n if (!server.hasOwnProperty('urls') &&\n server.hasOwnProperty('url')) {\n utils.deprecated('RTCIceServer.url', 'RTCIceServer.urls');\n server = JSON.parse(JSON.stringify(server));\n server.urls = server.url;\n delete server.url;\n newIceServers.push(server);\n } else {\n newIceServers.push(pcConfig.iceServers[i]);\n }\n }\n pcConfig.iceServers = newIceServers;\n }\n return new OrigPeerConnection(pcConfig, pcConstraints);\n };\n window.RTCPeerConnection.prototype = OrigPeerConnection.prototype;\n // wrap static methods. Currently just generateCertificate.\n if ('generateCertificate' in OrigPeerConnection) {\n Object.defineProperty(window.RTCPeerConnection, 'generateCertificate', {\n get() {\n return OrigPeerConnection.generateCertificate;\n }\n });\n }\n}\n\nexport function shimTrackEventTransceiver(window) {\n // Add event.transceiver member over deprecated event.receiver\n if (typeof window === 'object' && window.RTCTrackEvent &&\n 'receiver' in window.RTCTrackEvent.prototype &&\n !('transceiver' in window.RTCTrackEvent.prototype)) {\n Object.defineProperty(window.RTCTrackEvent.prototype, 'transceiver', {\n get() {\n return {receiver: this.receiver};\n }\n });\n }\n}\n\nexport function shimCreateOfferLegacy(window) {\n const origCreateOffer = window.RTCPeerConnection.prototype.createOffer;\n window.RTCPeerConnection.prototype.createOffer =\n function createOffer(offerOptions) {\n if (offerOptions) {\n if (typeof offerOptions.offerToReceiveAudio !== 'undefined') {\n // support bit values\n offerOptions.offerToReceiveAudio =\n !!offerOptions.offerToReceiveAudio;\n }\n const audioTransceiver = this.getTransceivers().find(transceiver =>\n transceiver.receiver.track.kind === 'audio');\n if (offerOptions.offerToReceiveAudio === false && audioTransceiver) {\n if (audioTransceiver.direction === 'sendrecv') {\n if (audioTransceiver.setDirection) {\n audioTransceiver.setDirection('sendonly');\n } else {\n audioTransceiver.direction = 'sendonly';\n }\n } else if (audioTransceiver.direction === 'recvonly') {\n if (audioTransceiver.setDirection) {\n audioTransceiver.setDirection('inactive');\n } else {\n audioTransceiver.direction = 'inactive';\n }\n }\n } else if (offerOptions.offerToReceiveAudio === true &&\n !audioTransceiver) {\n this.addTransceiver('audio');\n }\n\n if (typeof offerOptions.offerToReceiveVideo !== 'undefined') {\n // support bit values\n offerOptions.offerToReceiveVideo =\n !!offerOptions.offerToReceiveVideo;\n }\n const videoTransceiver = this.getTransceivers().find(transceiver =>\n transceiver.receiver.track.kind === 'video');\n if (offerOptions.offerToReceiveVideo === false && videoTransceiver) {\n if (videoTransceiver.direction === 'sendrecv') {\n if (videoTransceiver.setDirection) {\n videoTransceiver.setDirection('sendonly');\n } else {\n videoTransceiver.direction = 'sendonly';\n }\n } else if (videoTransceiver.direction === 'recvonly') {\n if (videoTransceiver.setDirection) {\n videoTransceiver.setDirection('inactive');\n } else {\n videoTransceiver.direction = 'inactive';\n }\n }\n } else if (offerOptions.offerToReceiveVideo === true &&\n !videoTransceiver) {\n this.addTransceiver('video');\n }\n }\n return origCreateOffer.apply(this, arguments);\n };\n}\n\nexport function shimAudioContext(window) {\n if (typeof window !== 'object' || window.AudioContext) {\n return;\n }\n window.AudioContext = window.webkitAudioContext;\n}\n","/*\n * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by a BSD-style license\n * that can be found in the LICENSE file in the root of the source\n * tree.\n */\n/* eslint-env node */\n'use strict';\n\nimport SDPUtils from 'sdp';\nimport * as utils from './utils';\n\nexport function shimRTCIceCandidate(window) {\n // foundation is arbitrarily chosen as an indicator for full support for\n // https://w3c.github.io/webrtc-pc/#rtcicecandidate-interface\n if (!window.RTCIceCandidate || (window.RTCIceCandidate && 'foundation' in\n window.RTCIceCandidate.prototype)) {\n return;\n }\n\n const NativeRTCIceCandidate = window.RTCIceCandidate;\n window.RTCIceCandidate = function RTCIceCandidate(args) {\n // Remove the a= which shouldn't be part of the candidate string.\n if (typeof args === 'object' && args.candidate &&\n args.candidate.indexOf('a=') === 0) {\n args = JSON.parse(JSON.stringify(args));\n args.candidate = args.candidate.substr(2);\n }\n\n if (args.candidate && args.candidate.length) {\n // Augment the native candidate with the parsed fields.\n const nativeCandidate = new NativeRTCIceCandidate(args);\n const parsedCandidate = SDPUtils.parseCandidate(args.candidate);\n const augmentedCandidate = Object.assign(nativeCandidate,\n parsedCandidate);\n\n // Add a serializer that does not serialize the extra attributes.\n augmentedCandidate.toJSON = function toJSON() {\n return {\n candidate: augmentedCandidate.candidate,\n sdpMid: augmentedCandidate.sdpMid,\n sdpMLineIndex: augmentedCandidate.sdpMLineIndex,\n usernameFragment: augmentedCandidate.usernameFragment,\n };\n };\n return augmentedCandidate;\n }\n return new NativeRTCIceCandidate(args);\n };\n window.RTCIceCandidate.prototype = NativeRTCIceCandidate.prototype;\n\n // Hook up the augmented candidate in onicecandidate and\n // addEventListener('icecandidate', ...)\n utils.wrapPeerConnectionEvent(window, 'icecandidate', e => {\n if (e.candidate) {\n Object.defineProperty(e, 'candidate', {\n value: new window.RTCIceCandidate(e.candidate),\n writable: 'false'\n });\n }\n return e;\n });\n}\n\nexport function shimMaxMessageSize(window, browserDetails) {\n if (!window.RTCPeerConnection) {\n return;\n }\n\n if (!('sctp' in window.RTCPeerConnection.prototype)) {\n Object.defineProperty(window.RTCPeerConnection.prototype, 'sctp', {\n get() {\n return typeof this._sctp === 'undefined' ? null : this._sctp;\n }\n });\n }\n\n const sctpInDescription = function(description) {\n if (!description || !description.sdp) {\n return false;\n }\n const sections = SDPUtils.splitSections(description.sdp);\n sections.shift();\n return sections.some(mediaSection => {\n const mLine = SDPUtils.parseMLine(mediaSection);\n return mLine && mLine.kind === 'application'\n && mLine.protocol.indexOf('SCTP') !== -1;\n });\n };\n\n const getRemoteFirefoxVersion = function(description) {\n // TODO: Is there a better solution for detecting Firefox?\n const match = description.sdp.match(/mozilla...THIS_IS_SDPARTA-(\\d+)/);\n if (match === null || match.length < 2) {\n return -1;\n }\n const version = parseInt(match[1], 10);\n // Test for NaN (yes, this is ugly)\n return version !== version ? -1 : version;\n };\n\n const getCanSendMaxMessageSize = function(remoteIsFirefox) {\n // Every implementation we know can send at least 64 KiB.\n // Note: Although Chrome is technically able to send up to 256 KiB, the\n // data does not reach the other peer reliably.\n // See: https://bugs.chromium.org/p/webrtc/issues/detail?id=8419\n let canSendMaxMessageSize = 65536;\n if (browserDetails.browser === 'firefox') {\n if (browserDetails.version < 57) {\n if (remoteIsFirefox === -1) {\n // FF < 57 will send in 16 KiB chunks using the deprecated PPID\n // fragmentation.\n canSendMaxMessageSize = 16384;\n } else {\n // However, other FF (and RAWRTC) can reassemble PPID-fragmented\n // messages. Thus, supporting ~2 GiB when sending.\n canSendMaxMessageSize = 2147483637;\n }\n } else if (browserDetails.version < 60) {\n // Currently, all FF >= 57 will reset the remote maximum message size\n // to the default value when a data channel is created at a later\n // stage. :(\n // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1426831\n canSendMaxMessageSize =\n browserDetails.version === 57 ? 65535 : 65536;\n } else {\n // FF >= 60 supports sending ~2 GiB\n canSendMaxMessageSize = 2147483637;\n }\n }\n return canSendMaxMessageSize;\n };\n\n const getMaxMessageSize = function(description, remoteIsFirefox) {\n // Note: 65536 bytes is the default value from the SDP spec. Also,\n // every implementation we know supports receiving 65536 bytes.\n let maxMessageSize = 65536;\n\n // FF 57 has a slightly incorrect default remote max message size, so\n // we need to adjust it here to avoid a failure when sending.\n // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1425697\n if (browserDetails.browser === 'firefox'\n && browserDetails.version === 57) {\n maxMessageSize = 65535;\n }\n\n const match = SDPUtils.matchPrefix(description.sdp,\n 'a=max-message-size:');\n if (match.length > 0) {\n maxMessageSize = parseInt(match[0].substr(19), 10);\n } else if (browserDetails.browser === 'firefox' &&\n remoteIsFirefox !== -1) {\n // If the maximum message size is not present in the remote SDP and\n // both local and remote are Firefox, the remote peer can receive\n // ~2 GiB.\n maxMessageSize = 2147483637;\n }\n return maxMessageSize;\n };\n\n const origSetRemoteDescription =\n window.RTCPeerConnection.prototype.setRemoteDescription;\n window.RTCPeerConnection.prototype.setRemoteDescription =\n function setRemoteDescription() {\n this._sctp = null;\n // Chrome decided to not expose .sctp in plan-b mode.\n // As usual, adapter.js has to do an 'ugly worakaround'\n // to cover up the mess.\n if (browserDetails.browser === 'chrome' && browserDetails.version >= 76) {\n const {sdpSemantics} = this.getConfiguration();\n if (sdpSemantics === 'plan-b') {\n Object.defineProperty(this, 'sctp', {\n get() {\n return typeof this._sctp === 'undefined' ? null : this._sctp;\n },\n enumerable: true,\n configurable: true,\n });\n }\n }\n\n if (sctpInDescription(arguments[0])) {\n // Check if the remote is FF.\n const isFirefox = getRemoteFirefoxVersion(arguments[0]);\n\n // Get the maximum message size the local peer is capable of sending\n const canSendMMS = getCanSendMaxMessageSize(isFirefox);\n\n // Get the maximum message size of the remote peer.\n const remoteMMS = getMaxMessageSize(arguments[0], isFirefox);\n\n // Determine final maximum message size\n let maxMessageSize;\n if (canSendMMS === 0 && remoteMMS === 0) {\n maxMessageSize = Number.POSITIVE_INFINITY;\n } else if (canSendMMS === 0 || remoteMMS === 0) {\n maxMessageSize = Math.max(canSendMMS, remoteMMS);\n } else {\n maxMessageSize = Math.min(canSendMMS, remoteMMS);\n }\n\n // Create a dummy RTCSctpTransport object and the 'maxMessageSize'\n // attribute.\n const sctp = {};\n Object.defineProperty(sctp, 'maxMessageSize', {\n get() {\n return maxMessageSize;\n }\n });\n this._sctp = sctp;\n }\n\n return origSetRemoteDescription.apply(this, arguments);\n };\n}\n\nexport function shimSendThrowTypeError(window) {\n if (!(window.RTCPeerConnection &&\n 'createDataChannel' in window.RTCPeerConnection.prototype)) {\n return;\n }\n\n // Note: Although Firefox >= 57 has a native implementation, the maximum\n // message size can be reset for all data channels at a later stage.\n // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1426831\n\n function wrapDcSend(dc, pc) {\n const origDataChannelSend = dc.send;\n dc.send = function send() {\n const data = arguments[0];\n const length = data.length || data.size || data.byteLength;\n if (dc.readyState === 'open' &&\n pc.sctp && length > pc.sctp.maxMessageSize) {\n throw new TypeError('Message too large (can send a maximum of ' +\n pc.sctp.maxMessageSize + ' bytes)');\n }\n return origDataChannelSend.apply(dc, arguments);\n };\n }\n const origCreateDataChannel =\n window.RTCPeerConnection.prototype.createDataChannel;\n window.RTCPeerConnection.prototype.createDataChannel =\n function createDataChannel() {\n const dataChannel = origCreateDataChannel.apply(this, arguments);\n wrapDcSend(dataChannel, this);\n return dataChannel;\n };\n utils.wrapPeerConnectionEvent(window, 'datachannel', e => {\n wrapDcSend(e.channel, e.target);\n return e;\n });\n}\n\n\n/* shims RTCConnectionState by pretending it is the same as iceConnectionState.\n * See https://bugs.chromium.org/p/webrtc/issues/detail?id=6145#c12\n * for why this is a valid hack in Chrome. In Firefox it is slightly incorrect\n * since DTLS failures would be hidden. See\n * https://bugzilla.mozilla.org/show_bug.cgi?id=1265827\n * for the Firefox tracking bug.\n */\nexport function shimConnectionState(window) {\n if (!window.RTCPeerConnection ||\n 'connectionState' in window.RTCPeerConnection.prototype) {\n return;\n }\n const proto = window.RTCPeerConnection.prototype;\n Object.defineProperty(proto, 'connectionState', {\n get() {\n return {\n completed: 'connected',\n checking: 'connecting'\n }[this.iceConnectionState] || this.iceConnectionState;\n },\n enumerable: true,\n configurable: true\n });\n Object.defineProperty(proto, 'onconnectionstatechange', {\n get() {\n return this._onconnectionstatechange || null;\n },\n set(cb) {\n if (this._onconnectionstatechange) {\n this.removeEventListener('connectionstatechange',\n this._onconnectionstatechange);\n delete this._onconnectionstatechange;\n }\n if (cb) {\n this.addEventListener('connectionstatechange',\n this._onconnectionstatechange = cb);\n }\n },\n enumerable: true,\n configurable: true\n });\n\n ['setLocalDescription', 'setRemoteDescription'].forEach((method) => {\n const origMethod = proto[method];\n proto[method] = function() {\n if (!this._connectionstatechangepoly) {\n this._connectionstatechangepoly = e => {\n const pc = e.target;\n if (pc._lastConnectionState !== pc.connectionState) {\n pc._lastConnectionState = pc.connectionState;\n const newEvent = new Event('connectionstatechange', e);\n pc.dispatchEvent(newEvent);\n }\n return e;\n };\n this.addEventListener('iceconnectionstatechange',\n this._connectionstatechangepoly);\n }\n return origMethod.apply(this, arguments);\n };\n });\n}\n\nexport function removeExtmapAllowMixed(window, browserDetails) {\n /* remove a=extmap-allow-mixed for webrtc.org < M71 */\n if (!window.RTCPeerConnection) {\n return;\n }\n if (browserDetails.browser === 'chrome' && browserDetails.version >= 71) {\n return;\n }\n if (browserDetails.browser === 'safari' && browserDetails.version >= 605) {\n return;\n }\n const nativeSRD = window.RTCPeerConnection.prototype.setRemoteDescription;\n window.RTCPeerConnection.prototype.setRemoteDescription =\n function setRemoteDescription(desc) {\n if (desc && desc.sdp && desc.sdp.indexOf('\\na=extmap-allow-mixed') !== -1) {\n const sdp = desc.sdp.split('\\n').filter((line) => {\n return line.trim() !== 'a=extmap-allow-mixed';\n }).join('\\n');\n // Safari enforces read-only-ness of RTCSessionDescription fields.\n if (window.RTCSessionDescription &&\n desc instanceof window.RTCSessionDescription) {\n arguments[0] = new window.RTCSessionDescription({\n type: desc.type,\n sdp,\n });\n } else {\n desc.sdp = sdp;\n }\n }\n return nativeSRD.apply(this, arguments);\n };\n}\n\nexport function shimAddIceCandidateNullOrEmpty(window, browserDetails) {\n // Support for addIceCandidate(null or undefined)\n // as well as addIceCandidate({candidate: \"\", ...})\n // https://bugs.chromium.org/p/chromium/issues/detail?id=978582\n // Note: must be called before other polyfills which change the signature.\n if (!(window.RTCPeerConnection && window.RTCPeerConnection.prototype)) {\n return;\n }\n const nativeAddIceCandidate =\n window.RTCPeerConnection.prototype.addIceCandidate;\n if (!nativeAddIceCandidate || nativeAddIceCandidate.length === 0) {\n return;\n }\n window.RTCPeerConnection.prototype.addIceCandidate =\n function addIceCandidate() {\n if (!arguments[0]) {\n if (arguments[1]) {\n arguments[1].apply(null);\n }\n return Promise.resolve();\n }\n // Firefox 68+ emits and processes {candidate: \"\", ...}, ignore\n // in older versions.\n // Native support for ignoring exists for Chrome M77+.\n // Safari ignores as well, exact version unknown but works in the same\n // version that also ignores addIceCandidate(null).\n if (((browserDetails.browser === 'chrome' && browserDetails.version < 78)\n || (browserDetails.browser === 'firefox'\n && browserDetails.version < 68)\n || (browserDetails.browser === 'safari'))\n && arguments[0] && arguments[0].candidate === '') {\n return Promise.resolve();\n }\n return nativeAddIceCandidate.apply(this, arguments);\n };\n}\n","/*\n * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by a BSD-style license\n * that can be found in the LICENSE file in the root of the source\n * tree.\n */\n/* eslint-env node */\n\n'use strict';\n\nimport {adapterFactory} from './adapter_factory.js';\n\nconst adapter =\n adapterFactory({window: typeof window === 'undefined' ? undefined : window});\nexport default adapter;\n","/*\n * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by a BSD-style license\n * that can be found in the LICENSE file in the root of the source\n * tree.\n */\nimport * as utils from './utils';\n\n // Browser shims.\nimport * as chromeShim from './chrome/chrome_shim';\nimport * as firefoxShim from './firefox/firefox_shim';\nimport * as safariShim from './safari/safari_shim';\nimport * as commonShim from './common_shim';\nimport * as sdp from 'sdp';\n\n// Shimming starts here.\nexport function adapterFactory({window} = {}, options = {\n shimChrome: true,\n shimFirefox: true,\n shimSafari: true,\n}) {\n // Utils.\n const logging = utils.log;\n const browserDetails = utils.detectBrowser(window);\n\n const adapter = {\n browserDetails,\n commonShim,\n extractVersion: utils.extractVersion,\n disableLog: utils.disableLog,\n disableWarnings: utils.disableWarnings,\n // Expose sdp as a convenience. For production apps include directly.\n sdp,\n };\n\n // Shim browser if found.\n switch (browserDetails.browser) {\n case 'chrome':\n if (!chromeShim || !chromeShim.shimPeerConnection ||\n !options.shimChrome) {\n logging('Chrome shim is not included in this adapter release.');\n return adapter;\n }\n if (browserDetails.version === null) {\n logging('Chrome shim can not determine version, not shimming.');\n return adapter;\n }\n logging('adapter.js shimming chrome.');\n // Export to the adapter global object visible in the browser.\n adapter.browserShim = chromeShim;\n\n // Must be called before shimPeerConnection.\n commonShim.shimAddIceCandidateNullOrEmpty(window, browserDetails);\n\n chromeShim.shimGetUserMedia(window, browserDetails);\n chromeShim.shimMediaStream(window, browserDetails);\n chromeShim.shimPeerConnection(window, browserDetails);\n chromeShim.shimOnTrack(window, browserDetails);\n chromeShim.shimAddTrackRemoveTrack(window, browserDetails);\n chromeShim.shimGetSendersWithDtmf(window, browserDetails);\n chromeShim.shimGetStats(window, browserDetails);\n chromeShim.shimSenderReceiverGetStats(window, browserDetails);\n chromeShim.fixNegotiationNeeded(window, browserDetails);\n\n commonShim.shimRTCIceCandidate(window, browserDetails);\n commonShim.shimConnectionState(window, browserDetails);\n commonShim.shimMaxMessageSize(window, browserDetails);\n commonShim.shimSendThrowTypeError(window, browserDetails);\n commonShim.removeExtmapAllowMixed(window, browserDetails);\n break;\n case 'firefox':\n if (!firefoxShim || !firefoxShim.shimPeerConnection ||\n !options.shimFirefox) {\n logging('Firefox shim is not included in this adapter release.');\n return adapter;\n }\n logging('adapter.js shimming firefox.');\n // Export to the adapter global object visible in the browser.\n adapter.browserShim = firefoxShim;\n\n // Must be called before shimPeerConnection.\n commonShim.shimAddIceCandidateNullOrEmpty(window, browserDetails);\n\n firefoxShim.shimGetUserMedia(window, browserDetails);\n firefoxShim.shimPeerConnection(window, browserDetails);\n firefoxShim.shimOnTrack(window, browserDetails);\n firefoxShim.shimRemoveStream(window, browserDetails);\n firefoxShim.shimSenderGetStats(window, browserDetails);\n firefoxShim.shimReceiverGetStats(window, browserDetails);\n firefoxShim.shimRTCDataChannel(window, browserDetails);\n firefoxShim.shimAddTransceiver(window, browserDetails);\n firefoxShim.shimGetParameters(window, browserDetails);\n firefoxShim.shimCreateOffer(window, browserDetails);\n firefoxShim.shimCreateAnswer(window, browserDetails);\n\n commonShim.shimRTCIceCandidate(window, browserDetails);\n commonShim.shimConnectionState(window, browserDetails);\n commonShim.shimMaxMessageSize(window, browserDetails);\n commonShim.shimSendThrowTypeError(window, browserDetails);\n break;\n case 'safari':\n if (!safariShim || !options.shimSafari) {\n logging('Safari shim is not included in this adapter release.');\n return adapter;\n }\n logging('adapter.js shimming safari.');\n // Export to the adapter global object visible in the browser.\n adapter.browserShim = safariShim;\n\n // Must be called before shimCallbackAPI.\n commonShim.shimAddIceCandidateNullOrEmpty(window, browserDetails);\n\n safariShim.shimRTCIceServerUrls(window, browserDetails);\n safariShim.shimCreateOfferLegacy(window, browserDetails);\n safariShim.shimCallbacksAPI(window, browserDetails);\n safariShim.shimLocalStreamsAPI(window, browserDetails);\n safariShim.shimRemoteStreamsAPI(window, browserDetails);\n safariShim.shimTrackEventTransceiver(window, browserDetails);\n safariShim.shimGetUserMedia(window, browserDetails);\n safariShim.shimAudioContext(window, browserDetails);\n\n commonShim.shimRTCIceCandidate(window, browserDetails);\n commonShim.shimMaxMessageSize(window, browserDetails);\n commonShim.shimSendThrowTypeError(window, browserDetails);\n commonShim.removeExtmapAllowMixed(window, browserDetails);\n break;\n default:\n logging('Unsupported browser!');\n break;\n }\n\n return adapter;\n}\n","// The module cache\nvar __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tvar cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tvar module = __webpack_module_cache__[moduleId] = {\n\t\tid: moduleId,\n\t\tloaded: false,\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n\t// Flag the module as loaded\n\tmodule.loaded = true;\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n","__webpack_require__.amdO = {};","// getDefaultExport function for compatibility with non-harmony modules\n__webpack_require__.n = (module) => {\n\tvar getter = module && module.__esModule ?\n\t\t() => (module['default']) :\n\t\t() => (module);\n\t__webpack_require__.d(getter, { a: getter });\n\treturn getter;\n};","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.g = (function() {\n\tif (typeof globalThis === 'object') return globalThis;\n\ttry {\n\t\treturn this || new Function('return this')();\n\t} catch (e) {\n\t\tif (typeof window === 'object') return window;\n\t}\n})();","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","__webpack_require__.nmd = (module) => {\n\tmodule.paths = [];\n\tif (!module.children) module.children = [];\n\treturn module;\n};","// startup\n// Load entry module and return exports\n// This entry module used 'module' so it can't be inlined\nvar __webpack_exports__ = __webpack_require__(4449);\n"],"names":["root","factory","exports","module","define","amd","self","Logger","LogCollector","logStorage","options","this","stringifyObjects","storeInterval","maxEntryLength","Object","keys","levels","forEach","logLevel","_log","apply","arguments","bind","storeLogsIntervalID","queue","totalLen","outputCache","prototype","stringify","someObject","JSON","error","formatLogMessage","msg","i","len","length","arg","ERROR","timestamp","prevMessage","prevMessageText","text","count","push","_flush","start","_reschedulePublishInterval","window","clearTimeout","setTimeout","flush","force","reschedule","isReady","cachedQueue","storeLogs","stop","consoleTransport","console","globalTransports","addGlobalTransport","transport","indexOf","removeGlobalTransport","transportIdx","splice","globalOptions","getCallerInfo","callerInfo","methodName","fileLocation","line","column","Error","stack","split","m","match","substr","log","logger","level","args","Array","slice","call","disableCallerInfo","transports","concat","t","l","logPrefixes","Date","toISOString","id","fullLogParts","methods","setGlobalOptions","setLevel","TRACE","DEBUG","INFO","LOG","WARN","idLoggers","loggers","curLevel","getLogger","setLogLevelById","setLogLevel","grammar","v","name","reg","o","names","format","s","u","e","p","z","r","c","b","a","rate","address","subtype","direction","config","sessionConfig","str","raddr","tcptype","generation","attribute","value","maxMessageSize","RegExp","dir2","clksrcExt","mediaClockValue","rateNumerator","rateDenominator","key","obj","parser","writer","write","parse","parseParams","parseFmtpConfig","parsePayloads","parseRemoteCandidates","parseImageAttributes","parseSimulcastStreamList","toIntIfInt","String","Number","parseReg","location","content","needsBlank","keyLocation","rawName","attachProperties","validLine","test","sdp","session","media","filter","type","rtp","fmtp","j","paramReducer","acc","expr","undefined","reduce","toString","map","candidates","parts","component","ip","port","item","substring","stream","scid","paused","formatRegExp","formatStr","replace","x","makeLine","Function","n","defaultOuterOrder","defaultInnerOrder","opts","version","mLine","payloads","outerOrder","innerOrder","el","join","transform","transformUtils","parseSsrcs","writeSsrcs","getSsrcAttribute","ssrc","attributeName","ssrcs","ssrcInfo","Simulcast","numOfLayers","ssrcCache","processVideo","action","isArray","validateDescription","desc","clearSsrcCache","setSsrcCache","_parseSimLayers","simGroup","ssrcGroups","find","group","semantics","ssrcStr","parseInt","_buildNewToOldSsrcMap","newSsrcList","oldSsrcList","ssrcMap","newSsrc","oldSsrc","_fillInSourceDataFromCache","newSimSsrcs","newMsid","newCname","ssrcsToReplace","ssrcsToAdd","values","_generateSourceData","primarySsrc","addAssociatedStream","primarySsrcCname","primarySsrcMsid","usesUnifiedPlan","msid","simSsrcs","simSsrc","Math","floor","random","_restoreSimulcast","numSsrcs","index","array","numGroups","fidGroup","mungeRemoteDescription","enableConferenceFlag","explodeRemoteSimulcast","sources","order","simulcastSsrcs","cname","relatedGroup","relatedSsrcs","relatedSSRC","simulcastGroup","info","nuke","implodeRemoteSimulcast","invalid","some","assertGoogConference","xGoogleFlag","removeGoogConference","RTCSessionDescription","mungeLocalDescription","source","previous_async","async","only_once","fn","called","noConflict","_toString","_isArray","_each","arr","iterator","_map","results","_keys","k","hasOwnProperty","process","nextTick","setImmediate","each","callback","completed","done","err","eachSeries","iterate","forEachSeries","eachLimit","limit","_eachLimit","forEachLimit","started","running","replenish","doParallel","doSeries","_asyncMap","eachfn","mapSeries","mapLimit","_mapLimit","doParallelLimit","memo","inject","foldl","reduceRight","reversed","reverse","foldr","_filter","sort","filterSeries","select","selectSeries","_reject","reject","rejectSeries","_detect","main_callback","result","detect","detectSeries","any","every","all","sortBy","criteria","left","right","auto","tasks","remainingTasks","listeners","addListener","unshift","taskComplete","theCallback","task","taskCallback","safeResults","rkey","requires","abs","ready","listener","removeListener","retry","times","attempts","wrappedTask","wrappedCallback","wrappedResults","retryAttempt","finalAttempt","seriesCallback","series","data","waterfall","wrapIterator","next","_parallel","parallel","parallelLimit","makeCallback","_concat","cb","y","concatSeries","whilst","doWhilst","until","doUntil","worker","concurrency","_insert","q","pos","drain","saturated","workers","empty","kill","shift","idle","pause","resume","priorityQueue","_compareTasks","priority","sequence","compare","beg","end","mid","_binarySearch","cargo","payload","working","drained","ts","ds","_console_fn","dir","memoize","hasher","queues","memoized","pop","unmemoized","unmemoize","counter","timesSeries","seq","fns","that","newargs","nextargs","compose","_applyEach","go","applyEach","applyEachSeries","forever","constructor","userId","displayName","isLocalStats","_userId","setDisplayName","_isLocalStats","setDominantSpeaker","totalDominantSpeakerTime","_dominantSpeakerStart","_hasLeft","_facialExpressions","happy","neutral","surprised","angry","fearful","disgusted","sad","getUserId","getDisplayName","newName","isDominantSpeaker","isNowDominantSpeaker","now","timeElapsed","getTotalDominantSpeakerTime","total","hasLeft","markAsHasLeft","getFacialExpressions","setFacialExpressions","facialExpressions","addFacialExpression","facialExpression","duration","getTokenAuthUrl","urlPattern","roomName","roleUpgrade","EventEmitterForwarder","src","dest","emit","forward","srcEvent","handlers","oldOnErrorHandler","onerror","oldOnUnhandledRejection","onunhandledrejection","handler","event","reason","GlobalOnErrorHandler","addHandler","callErrorHandler","errHandler","callUnhandledRejectionHandler","randomInt","min","max","randomElement","RandomUtil","randomHexDigit","randomHexString","ret","randomAlphanumStr","currentExecutingScript","require","ScriptUtil","loadScript","prepend","relativeURL","loadCallback","errorCallback","d","document","tagName","script","createElement","referenceNode","getElementsByTagName","scriptEl","scriptSrc","baseScriptSrc","lastIndexOf","onload","parentNode","insertBefore","appendChild","integerHash","string","char","hash","charCodeAt","pow","generateUsername","CAMERA","DESKTOP","DESKTOP_HIGH_FPS","NONE","ENVIRONMENT","USER","H264","OPUS","ULPFEC","VP8","VP9","INACTIVE","RECVONLY","SENDONLY","SENDRECV","CREATE_ANSWER_FAILED","CREATE_OFFER_FAILED","DATA_CHANNEL_OPEN","ENDPOINT_CONN_STATUS_CHANGED","DOMINANT_SPEAKER_CHANGED","LASTN_ENDPOINT_CHANGED","FORWARDED_SOURCES_CHANGED","PERMISSIONS_CHANGED","SENDER_VIDEO_CONSTRAINTS_CHANGED","LASTN_VALUE_CHANGED","LOCAL_TRACK_SSRC_UPDATED","LOCAL_TRACK_MAX_ENABLED_RESOLUTION_CHANGED","TRACK_ATTACHED","REMOTE_TRACK_ADDED","REMOTE_TRACK_MUTE","REMOTE_TRACK_REMOVED","REMOTE_TRACK_UNMUTE","SET_LOCAL_DESCRIPTION_FAILED","SET_REMOTE_DESCRIPTION_FAILED","AUDIO_OUTPUT_DEVICE_CHANGED","DEVICE_LIST_CHANGED","DEVICE_LIST_WILL_CHANGE","DEVICE_LIST_AVAILABLE","ENDPOINT_MESSAGE_RECEIVED","ENDPOINT_STATS_RECEIVED","LOCAL_UFRAG_CHANGED","REMOTE_UFRAG_CHANGED","width","height","IDENTITY_UPDATED","LOCAL_JID","ADD_ICE_CANDIDATE_FAILED","AUDIO_MUTED_BY_FOCUS","VIDEO_MUTED_BY_FOCUS","AUTHENTICATION_REQUIRED","BRIDGE_DOWN","CALL_ACCEPTED","CALL_INCOMING","CALL_ENDED","CHAT_ERROR_RECEIVED","SETTINGS_ERROR_RECEIVED","CONFERENCE_PROPERTIES_CHANGED","CONNECTION_ESTABLISHED","CONNECTION_FAILED","CONNECTION_INTERRUPTED","CONNECTION_RESTORED","CONNECTION_ICE_FAILED","CONNECTION_RESTARTED","CONNECTION_STATUS_CHANGED","DISPLAY_NAME_CHANGED","EMUC_ROOM_ADDED","EMUC_ROOM_REMOVED","ETHERPAD","FOCUS_DISCONNECTED","FOCUS_LEFT","GRACEFUL_SHUTDOWN","ICE_RESTARTING","ICE_RESTART_SUCCESS","KICKED","LOCAL_ROLE_CHANGED","MEETING_ID_SET","MESSAGE_RECEIVED","INVITE_MESSAGE_RECEIVED","PRIVATE_MESSAGE_RECEIVED","MUC_MEMBER_BOT_TYPE_CHANGED","MUC_DESTROYED","MUC_JOIN_IN_PROGRESS","MUC_JOINED","MUC_MEMBER_JOINED","MUC_MEMBER_LEFT","MUC_LOBBY_MEMBER_JOINED","MUC_LOBBY_MEMBER_UPDATED","MUC_LOBBY_MEMBER_LEFT","MUC_DENIED_ACCESS","MUC_LEFT","MUC_ROLE_CHANGED","MUC_LOCK_CHANGED","MUC_MEMBERS_ONLY_CHANGED","PARTICIPANT_AUDIO_MUTED","PARTICIPANT_VIDEO_MUTED","PARTICIPANT_VIDEO_TYPE_CHANGED","PARTICIPANT_FEATURES_CHANGED","PASSWORD_REQUIRED","PHONE_NUMBER_CHANGED","PRESENCE_RECEIVED","PRESENCE_STATUS","PROMPT_FOR_LOGIN","READY_TO_JOIN","RECORDER_STATE_CHANGED","REMOTE_STATS","RENEGOTIATION_FAILED","RESERVATION_ERROR","ROOM_CONNECT_ERROR","ROOM_CONNECT_NOT_ALLOWED_ERROR","ROOM_JOIN_ERROR","ROOM_CONNECT_MEMBERS_ONLY_ERROR","ROOM_MAX_USERS_ERROR","SENDING_CHAT_MESSAGE","SENDING_PRIVATE_CHAT_MESSAGE","SESSION_ACCEPT","SESSION_ACCEPT_ERROR","SESSION_ACCEPT_TIMEOUT","SOURCE_ADD","SOURCE_ADD_ERROR","SOURCE_REMOVE","SOURCE_REMOVE_ERROR","SPEAKER_STATS_RECEIVED","CONFERENCE_TIMESTAMP_RECEIVED","AV_MODERATION_APPROVED","AV_MODERATION_REJECTED","AV_MODERATION_RECEIVED","AV_MODERATION_CHANGED","AV_MODERATION_PARTICIPANT_APPROVED","AV_MODERATION_PARTICIPANT_REJECTED","BREAKOUT_ROOMS_MOVE_TO_ROOM","BREAKOUT_ROOMS_EVENT","BREAKOUT_ROOMS_UPDATED","START_MUTED_FROM_FOCUS","SUBJECT_CHANGED","SUSPEND_DETECTED","TRANSCRIPTION_STATUS_CHANGED","TRANSPORT_INFO","VIDEO_SIP_GW_AVAILABILITY_CHANGED","VIDEO_SIP_GW_SESSION_STATE_CHANGED","ICE_CONNECTION_STATE_CHANGED","JSON_MESSAGE_RECEIVED","byteLength","b64","lens","getLens","validLen","placeHoldersLen","toByteArray","tmp","Arr","_byteLength","curByte","revLookup","fromByteArray","uint8","extraBytes","maxChunkLength","len2","encodeChunk","lookup","Uint8Array","code","num","output","defineProperty","enumerable","get","Symbol","toStringTag","__esModule","create","default","getFirstMatch","getSecondMatch","matchAndReturnConst","getWindowsVersionName","getMacOSVersionName","getAndroidVersionName","getVersionPrecision","compareVersions","getBrowserAlias","BROWSER_ALIASES_MAP","getBrowserTypeByAlias","BROWSER_MAP","ENGINE_MAP","OS_MAP","PLATFORMS_MAP","Bada","BlackBerry","Chrome","Chromium","Epiphany","Firefox","Focus","Generic","Googlebot","Maxthon","Opera","PhantomJS","Puffin","QupZilla","QQ","QQLite","Safari","Sailfish","SeaMonkey","Sleipnir","Swing","Tizen","Vivaldi","WeChat","Roku","amazon_silk","android","bada","blackberry","chrome","chromium","epiphany","firefox","focus","generic","googlebot","google_search","ie","k_meleon","maxthon","edge","mz","naver","opera","opera_coast","phantomjs","puffin","qupzilla","qq","qqlite","safari","sailfish","samsung_internet","seamonkey","sleipnir","swing","tizen","uc","vivaldi","webos","wechat","yandex","tablet","mobile","desktop","tv","WindowsPhone","Windows","MacOS","iOS","Android","WebOS","Linux","ChromeOS","PlayStation4","EdgeHTML","Blink","Trident","Presto","Gecko","WebKit","configurable","writable","getParser","getResult","_ua","parsedResult","getUA","parseBrowser","browser","describe","getBrowser","getBrowserName","toLowerCase","getBrowserVersion","getOS","os","parseOS","getOSName","getOSVersion","getPlatform","platform","parsePlatform","getPlatformType","getEngine","engine","parseEngine","getEngineName","assign","satisfies","isOS","isPlatform","f","isBrowser","compareVersion","isEngine","is","search","versionName","vendor","model","scriptReadyRegex","fullPageUrl","href","pageUrl","scripts","supportsScriptReadyState","isNotOpera","hasNativeCurrentScriptAccessor","stackTraceLimit","Infinity","hasStackBeforeThrowing","hasStackAfterThrowing","getScriptUrlFromStack","skipStackDepth","matches","url","ignoreMessage","round","_nearestExecutingScript","eligibleScripts","readyState","getScriptFromUrl","hasAttribute","getSoleInlineScript","currentScript","thrownErr","near","far","origin","ReflectOwnKeys","R","Reflect","ReflectApply","target","receiver","ownKeys","getOwnPropertySymbols","getOwnPropertyNames","NumberIsNaN","isNaN","EventEmitter","init","once","emitter","Promise","resolve","errorListener","resolver","eventTargetAgnosticAddListener","flags","on","addErrorHandlerIfEventEmitter","_events","_eventsCount","_maxListeners","defaultMaxListeners","checkListener","TypeError","_getMaxListeners","_addListener","events","existing","warning","newListener","warned","w","warn","onceWrapper","fired","wrapFn","_onceWrap","state","wrapped","_listeners","unwrap","evlistener","unwrapListeners","arrayClone","listenerCount","copy","addEventListener","wrapListener","removeEventListener","set","RangeError","getPrototypeOf","setMaxListeners","getMaxListeners","doError","er","message","context","prependListener","prependOnceListener","list","position","originalListener","spliceOne","off","removeAllListeners","rawListeners","eventNames","WINDOW","JS_MD5_NO_WINDOW","WEB_WORKER","NODE_JS","JS_MD5_NO_NODE_JS","versions","node","g","COMMON_JS","JS_MD5_NO_COMMON_JS","AMD","ARRAY_BUFFER","JS_MD5_NO_ARRAY_BUFFER","ArrayBuffer","HEX_CHARS","EXTRA","SHIFT","OUTPUT_TYPES","BASE64_ENCODE_CHAR","blocks","buffer8","buffer","Uint32Array","JS_MD5_NO_ARRAY_BUFFER_IS_VIEW","isView","createOutputMethod","outputType","Md5","update","createMethod","method","nodeWrap","crypto","eval","Buffer","nodeMethod","createHash","digest","sharedMemory","h0","h1","h2","h3","bytes","hBytes","finalized","hashed","first","notString","lastByteIndex","finalize","bc","da","hex","arrayBuffer","base64","v1","v2","v3","base64Str","md5","HASH_UNDEFINED","MAX_SAFE_INTEGER","argsTag","boolTag","dateTag","funcTag","genTag","mapTag","numberTag","objectTag","promiseTag","regexpTag","setTag","stringTag","symbolTag","weakMapTag","arrayBufferTag","dataViewTag","float32Tag","float64Tag","int8Tag","int16Tag","int32Tag","uint8Tag","uint8ClampedTag","uint16Tag","uint32Tag","reFlags","reIsHostCtor","reIsUint","cloneableTags","freeGlobal","freeSelf","freeExports","nodeType","freeModule","moduleExports","addMapEntry","pair","addSetEntry","add","arrayReduce","iteratee","accumulator","initAccum","isHostObject","mapToArray","size","overArg","func","setToArray","uid","arrayProto","funcProto","objectProto","coreJsData","maskSrcKey","exec","IE_PROTO","funcToString","objectToString","reIsNative","getPrototype","objectCreate","propertyIsEnumerable","nativeGetSymbols","nativeIsBuffer","isBuffer","nativeKeys","DataView","getNative","Map","Set","WeakMap","nativeCreate","dataViewCtorString","toSource","mapCtorString","promiseCtorString","setCtorString","weakMapCtorString","symbolProto","symbolValueOf","valueOf","Hash","entries","clear","entry","ListCache","MapCache","Stack","__data__","assignValue","object","objValue","eq","assocIndexOf","baseClone","isDeep","isFull","customizer","isObject","isArr","input","initCloneArray","copyArray","tag","getTag","isFunc","cloneBuffer","isPrototype","proto","initCloneObject","copyObject","getSymbols","copySymbols","baseAssign","cloneFunc","symbol","Ctor","cloneArrayBuffer","dataView","byteOffset","cloneDataView","typedArray","cloneTypedArray","cloneMap","regexp","lastIndex","cloneRegExp","cloneSet","initCloneByTag","stacked","props","keysFunc","symbolsFunc","offset","arrayPush","baseGetAllKeys","getAllKeys","arrayEach","subValue","newValue","getMapData","getValue","isFunction","baseIsNative","has","cache","pairs","LARGE_ARRAY_SIZE","isIndex","other","ctorString","isArrayLike","isLength","inherited","isObjectLike","isArrayLikeObject","isArguments","baseTimes","skipIndexes","arrayLikeKeys","baseKeys","reTrim","reIsBadHex","reIsBinary","reIsOctal","freeParseInt","nativeMax","nativeMin","toNumber","isSymbol","isBinary","wait","lastArgs","lastThis","maxWait","timerId","lastCallTime","lastInvokeTime","leading","maxing","trailing","invokeFunc","time","thisArg","leadingEdge","timerExpired","shouldInvoke","timeSinceLastCall","trailingEdge","remainingWait","debounced","isInvoking","cancel","arrayTag","errorTag","typedArrayTags","freeProcess","nodeUtil","binding","nodeIsTypedArray","isTypedArray","arraySome","predicate","nativeObjectToString","symToStringTag","SetCache","baseGetTag","isOwn","unmasked","getRawTag","baseIsArguments","baseIsEqual","bitmask","equalFunc","objIsArr","othIsArr","objTag","othTag","objIsObj","othIsObj","isSameTag","equalArrays","convert","isPartial","equalByTag","objIsWrapped","othIsWrapped","objUnwrapped","othUnwrapped","objProps","objLength","skipCtor","othValue","compared","objCtor","othCtor","equalObjects","baseIsEqualDeep","arrLength","othLength","seen","arrValue","othIndex","isMasked","resIndex","arrayFilter","baseUnary","isArg","isBuff","isType","SDPUtils","localCName","generateIdentifier","splitLines","blob","trim","splitSections","part","getDescription","sections","getMediaSections","matchPrefix","prefix","parseCandidate","candidate","foundation","protocol","relatedAddress","relatedPort","tcpType","ufrag","usernameFragment","writeCandidate","toUpperCase","parseIceOptions","parseRtpMap","parsed","payloadType","clockRate","channels","numChannels","writeRtpMap","codec","pt","preferredPayloadType","parseExtmap","uri","writeExtmap","headerExtension","preferredId","parseFmtp","kv","writeFmtp","parameters","params","param","parseRtcpFb","parameter","writeRtcpFb","lines","rtcpFeedback","fb","parseSsrcMedia","sp","colon","parseSsrcGroup","getMid","mediaSection","parseFingerprint","algorithm","getDtlsParameters","sessionpart","role","fingerprints","writeDtlsParameters","setupType","fp","parseCryptoLine","cryptoSuite","keyParams","sessionParams","writeCryptoLine","writeCryptoKeyParams","parseCryptoKeyParams","keyMethod","keySalt","lifeTime","mkiValue","mkiLength","getCryptoParameters","getIceParameters","pwd","password","writeIceParameters","iceLite","parseRtpParameters","description","codecs","headerExtensions","fecMechanisms","rtcp","mline","rtpmapline","fmtps","writeRtpDescription","kind","caps","maxptime","extension","parseRtpEncodingParameters","encodingParameters","hasRed","hasUlpfec","secondarySsrc","flows","apt","encParam","codecPayloadType","rtx","fec","mechanism","bandwidth","maxBitrate","parseRtcpParameters","rtcpParameters","remoteSsrc","rsize","reducedSize","compound","mux","writeRtcpParameters","parseMsid","spec","track","planB","msidParts","parseSctpDescription","parseMLine","maxSizeLine","sctpPort","fmt","sctpMapLines","writeSctpDescription","sctp","generateSessionId","writeSessionBoilerplate","sessId","sessVer","sessUser","sessionId","getDirection","getKind","isRejected","parseOLine","username","sessionVersion","netType","addressType","isValidSDP","charAt","JitsiConferenceErrors","CHAT_ERROR","SETTINGS_ERROR","CONFERENCE_DESTROYED","CONFERENCE_MAX_USERS","CONNECTION_ERROR","CONFERENCE_RESTARTED","NOT_ALLOWED_ERROR","MEMBERS_ONLY_ERROR","CONFERENCE_ACCESS_DENIED","ICE_FAILED","INCOMPATIBLE_SERVER_VERSIONS","OFFER_ANSWER_FAILED","PASSWORD_NOT_SUPPORTED","VIDEOBRIDGE_NOT_AVAILABLE","JitsiConferenceEvents","AUDIO_INPUT_STATE_CHANGE","AUDIO_UNMUTE_PERMISSIONS_CHANGED","AUTH_STATUS_CHANGED","BEFORE_STATISTICS_DISPOSED","CONFERENCE_ERROR","CONFERENCE_FAILED","CONFERENCE_JOIN_IN_PROGRESS","CONFERENCE_JOINED","CONFERENCE_LEFT","CONFERENCE_UNIQUE_ID_SET","DATA_CHANNEL_OPENED","CONFERENCE_CREATED_TIMESTAMP","DTMF_SUPPORT_CHANGED","JVB121_STATUS","PARTICIPANT_KICKED","LAST_N_ENDPOINTS_CHANGED","LOCK_STATE_CHANGED","SERVER_REGION_CHANGED","_MEDIA_SESSION_STARTED","_MEDIA_SESSION_ACTIVE_CHANGED","MEMBERS_ONLY_CHANGED","NO_AUDIO_INPUT","NOISY_MIC","NON_PARTICIPANT_MESSAGE_RECEIVED","PARTICIPANT_CONN_STATUS_CHANGED","PARTCIPANT_FEATURES_CHANGED","PARTICIPANT_PROPERTY_CHANGED","P2P_STATUS","PROPERTIES_CHANGED","START_MUTED_POLICY_CHANGED","STARTED_MUTED","TALK_WHILE_MUTED","TRACK_ADDED","TRACK_AUDIO_LEVEL_CHANGED","TRACK_MUTE_CHANGED","TRACK_REMOVED","TRACK_UNMUTE_REJECTED","USER_JOINED","USER_LEFT","USER_ROLE_CHANGED","USER_STATUS_CHANGED","VIDEO_UNMUTE_PERMISSIONS_CHANGED","BOT_TYPE_CHANGED","LOBBY_USER_JOINED","LOBBY_USER_UPDATED","LOBBY_USER_LEFT","FACIAL_EXPRESSION_ADDED","CALLSTATS_SCRIPT_URL","JitsiTrackErrors","CONSTRAINT_FAILED","ELECTRON_DESKTOP_PICKER_ERROR","ELECTRON_DESKTOP_PICKER_NOT_FOUND","GENERAL","NOT_FOUND","PERMISSION_DENIED","SCREENSHARING_GENERIC_ERROR","SCREENSHARING_USER_CANCELED","TIMEOUT","TRACK_IS_DISPOSED","TRACK_NO_STREAM_FOUND","UNSUPPORTED_RESOLUTION","TRACK_ERROR_TO_MESSAGE_MAP","JitsiTrackError","devices","gum","constraints","constraintName","constraint","video","failedConstraintName","mandatory","minWidth","minHeight","getResolutionFromFailedConstraint","TYPE_OPERATIONAL","TYPE_PAGE","TYPE_TRACK","CONNECTION_DISCONNECTED","ICE_DURATION","createConferenceEvent","attributes","createConnectionStageReachedEvent","stage","actionSubject","createGetUserMediaEvent","createJingleEvent","createP2PEvent","createRemotelyMutedEvent","mediaType","createRtpStatsEvent","Events","AUDIO_LEVEL","BEFORE_DISPOSED","BYTE_SENT_STATS","CONNECTION_STATS","LONG_TASKS_STATS","CHROME","OPERA","FIREFOX","INTERNET_EXPLORER","SAFARI","NWJS","ELECTRON","REACT_NATIVE","UNKNOWN","bowserNameToJitsiName","_detectElectron","userAgent","navigator","JitsiMeetElectron","_detectNWJS","_detectReactNative","product","DummyLocalStorage","_storage","getItem","keyName","setItem","keyValue","removeItem","serialize","jitsiLocalStorage","super","localStorage","_localStorageDisabled","ignore","isLocalStorageDisabled","dontEmitChangedEvent","localStorageContent","browserInfo","_bowser","Bowser","detectedBrowserInfo","bowser","detectors","_detectChromiumBased","_name","_version","getName","isChrome","isOpera","isFirefox","isIExplorer","isSafari","isNWJS","isElectron","isReactNative","getVersion","_checkCondition","checkTree","isVersionGreaterThan","isVersionLessThan","isVersionEqualTo","doesVideoMuteByStreamRemove","isChromiumBased","isWebKitBased","isIosBrowser","maxTouchPoints","Boolean","mediaDevices","getUserMedia","RTCRtpTransceiver","isTwa","matchMedia","isSupported","_getSafariVersion","_getChromiumBasedVersion","isSupportedAndroidBrowser","isSupportedIOSBrowser","_getIOSVersion","isUserInteractionRequiredForUnmute","supportsVideoMuteOnConnInterrupted","supportsBandwidthStatistics","supportsCodecPreferences","RTCRtpReceiver","getCapabilities","supportsDeviceChangeEvent","ondevicechange","supportsLocalCandidateRttStatistics","supportsPerformanceObserver","PerformanceObserver","supportedEntryTypes","supportsReceiverStats","supportsRTTStatistics","supportsVP9","usesSdpMungingForSimulcast","usesAdapter","usesRidsForSimulcast","supportsGetDisplayMedia","getDisplayMedia","supportsEncodedTransform","RTCRtpScriptTransform","supportsInsertableStreams","RTCRtpSender","createEncodedStreams","ReadableStream","postMessage","supportsAudioRed","mimeType","supportsUnifiedPlan","supportsVADDetection","supportsRTX","ua","reset","disposed","analyticsHandlers","permanentProperties","conferenceName","addPermanentProperties","dispose","setAnalyticsHandlers","_setUserProperties","_sendEvent","setUserProperties","properties","setConferenceName","sendEvent","eventName","_verifyRequiredFields","objectType","containerType","containerId","objectId","_maybeCacheEvent","wrtcFuncNames","reportType","_fabrics","CallStats","backend","backendInitialized","atLeastOneFabric","defaultInstance","callStatsInstance","fabrics","hasFabric","debug","_addNewFabric","_emptyReportQueue","csInstance","defaultConfID","confID","defaultPC","peerconnection","report","reportsQueue","errorData","_reportError","pc","eventData","sendFabricEvent","associateMstWithUserID","callStatsId","usageLabel","cs","_error","reportError","theBackend","tryCatchMethods","originalMethod","theArguments","debugMethods","originalReportError","exception","CallStatsBackend","callstats","_traceAndCatchBackendCalls","userID","aliasName","userName","callStatsID","callStatsSecret","configParams","applicationName","applicationVersion","siteID","initialize","_initCallback","getWiFiStatsMethod","attachWifiStatsHandler","then","addresses","catch","devicesData","_reportEvent","conferenceID","overall","comment","sendUserFeedback","status","mute","tpc","remoteUserID","fabricAttributes","remoteEndpointType","isP2P","endpointType","peer","server","addNewFabric","fabricUsage","multiplex","_addNewFabricCallback","success","associateStreamWithVideoTag","isLocal","streamEndpointId","sendDominantSpeakerEvent","sendTerminateEvent","fabricEvent","fabricTerminated","delete","sendIceConnectionFailedEvent","sendCreateOfferFailed","sendCreateAnswerFailed","sendResumeOrHoldEvent","isResume","sendScreenSharingEvent","sendSetLocalDescFailed","sendSetRemoteDescFailed","sendAddIceCandidateFailed","AudioContext","webkitAudioContext","LocalStatsCollector","interval","intervalId","intervalMilis","audioLevel","safeCounterIncrement","number","nextValue","calculateAverage","valueArray","filterPositiveValues","suspend","isLocalStatsSupported","analyser","createAnalyser","smoothingTimeConstant","fftSize","createMediaStreamSource","connect","setInterval","frequencyBinCount","getByteTimeDomainData","samples","maxVolume","parseFloat","toFixed","timeDomainDataToAudioLevel","newLevel","lastLevel","diff","animateLevel","clearInterval","RunningAverage","average","addNext","getAverage","PerformanceObserverStats","statsInterval","eventEmitter","longTasks","maxDuration","performanceStatsInterval","stats","getLongTasksStats","avgRatePerMinute","maxDurationMs","startObserver","longTaskEventHandler","getEntries","observer","observe","buffered","startTime","longTasksIntervalId","_lastTimeStamp","StatisticsEvents","stopObserver","disconnect","AUDIO","PRESENTER","VIDEO","calculatePacketLoss","lostPackets","totalPackets","SsrcStats","loss","bitrate","download","upload","resolution","framerate","ConferenceStats","packetLoss","StatsCollector","audioLevelsInterval","baselineAudioLevelsReport","currentAudioLevelsReport","currentStatsReport","previousStatsReport","audioLevelReportHistory","audioLevelsIntervalId","conferenceStats","audioLevelsIntervalMilis","speakerList","statsIntervalId","statsIntervalMilis","ssrc2stats","setLoss","setResolution","addBitrate","resetBitrate","setFramerate","setCodec","setSpeakerList","startAudioLevelStats","audioLevels","getAudioLevels","getStats","processAudioLevelReport","processStats","processStatsReport","_processAndEmitReport","bitrateDownload","bitrateUpload","resolutions","framerates","audioCodec","videoCodec","audioBitrateDownload","audioBitrateUpload","videoBitrateDownload","videoBitrateUpload","ssrcStats","isDownloadStream","packetsTotal","packetsLost","getTrackBySSRC","isAudioTrack","participantId","getParticipantId","userResolutions","userFramerates","codecDesc","userCodecs","audio","avgAudioLevels","localAvgAudioLevels","avgAudioLevel","sum","currentValue","getNonNegativeValue","_calculateBitrate","before","fieldName","bytesNow","bytesBefore","bytesProcessed","timeMs","bitrateKbps","byteSentStats","nominated","availableIncomingBitrate","availableOutgoingBitrate","remoteUsedCandidate","remoteCandidateId","localUsedCandidate","localCandidateId","localip","conferenceStatsTransport","p2p","localCandidateType","candidateType","remoteCandidateType","networkType","rtt","currentRoundTripTime","packetsNow","packetsBefore","packetsDiff","packetsLostNow","packetsLostBefore","packetsLostDiff","frameHeight","frameWidth","frameRate","framesPerSecond","bytesSent","codecId","codecShortType","MediaType","remoteSource","localVideoTracks","getLocalTracks","getLocalSSRC","framesSent","numberOfActiveStreams","getActiveSimulcastStreams","trackIdentifier","getSsrcByTrackId","_instances","isCallstatsLoaded","_initCallStatsBackend","isBackendInitialized","initBackend","Statistics","xmpp","rtpStatsMap","callStatsIntegrationEnabled","enableCallStats","disableThirdPartyRequests","callStatsApplicationLogsDisabled","customScriptUrl","loadCallStatsAPI","callsStatsInstances","instances","audioLevelsEnabled","disableAudioLevels","pcStatsInterval","longTasksStatsInterval","analytics","startRemoteStats","stopRemoteStats","rtpStats","RTPStats","localStats","startLocalStats","LocalStats","addAudioLevelListener","removeAudioLevelListener","addBeforeDisposedListener","removeBeforeDisposedListener","addConnectionStatsListener","removeConnectionStatsListener","addByteSentStatsListener","removeByteSentStatsListener","addLongTasksStatsListener","attachLongTasksStats","conference","performanceObserverStats","removeLongTasksStatsListener","from","callStats","stopCallStats","tpcId","_stopRemoteStats","stopLocalStats","startCallStats","newInstance","_getAllCallStatsInstances","csInstances","statistics","isCallstatsEnabled","sendConnectionResumeOrHoldEvent","instance","sendMuteEvent","muted","roomJid","sendActiveDeviceListEvent","globalSet","sendGetUserMediaFailed","formatJitsiTrackErrorForCallStats","sendLog","globalSubSet","csPerStats","sendApplicationLog","sendFeedback","rating","reportGlobalError","sendAnalyticsAndLog","eventToLog","sendAnalytics","JitsiConferenceEventManager","xmppListeners","p2pJingleSession","jvbJingleSession","isMuted","getType","setupChatRoomListeners","chatRoom","room","chatRoomForwarder","XMPPEvents","jingleSession","rtc","closeBridgeChannel","features","participant","getParticipantById","Strophe","setFeatures","offerIq","_setBridgeChannel","actor","mutedByFocusActor","setAudioMute","isMutedByFocus","mutedVideoByFocusActor","setVideoMute","isVideoMutedByFocus","_onMucJoined","isJvbConnectionInterrupted","connectionTimes","bridgeDown","createBridgeDownEvent","_onConferenceRestarted","connection","emuc","doLeave","roomjid","_onIceConnectionFailed","createFocusLeftEvent","jid","resource","getStatus","setTerminator","setInitiator","setParticipantPropertyListener","setProperty","onMemberKicked","onSuspendDetected","onMemberJoined","_onMemberBotTypeChanged","onMemberLeft","onDisplayNameChanged","onLocalRoleChanged","isModerator","recorderSession","logObject","getError","onUserRoleChanged","AuthenticationEvents","authEnabled","authIdentity","txt","myJid","_status","addPresenceListener","myUserId","startAudioMuted","startVideoMuted","updated","startMutedPolicy","setupRTCListeners","RTCEvents","onRemoteTrackAdded","onRemoteTrackRemoved","dominant","previous","lastDominantSpeaker","performance","isVideoTrack","videoType","VideoType","removeXMPPListeners","setupXMPPListeners","_addConferenceXMPPListener","onIncomingCall","onCallAccepted","onTransportInfo","onCallEnded","audioMuted","videoMuted","ignoreStartMuted","createdTimestamp","actorJid","actorParticipant","getParticipants","getJid","enabled","setupStatisticsListeners","setAudioLevel","startSilent","onByteSentStatsReceived","LOCAL_TRACK_STOPPED","TRACK_AUDIO_OUTPUT_CHANGED","TRACK_VIDEOTYPE_CHANGED","NO_DATA_FROM_SOURCE","TRACK_STREAMING_STATUS_CHANGED","ParticipantConnectionStatus","ACTIVE","INTERRUPTED","RESTORING","ParticipantConnectionStatusHandler","isConnectionActiveByJvb","isInLastN","isRestoringTimedout","isVideoMuted","isVideoTrackFrozen","trackTimers","connStatusFromJvb","outOfLastNTimeout","p2pRtcMuteTimeout","rtcMuteTimeout","rtcMutedTimestamp","enteredLastNTimestamp","restoringTimers","connectionStatusMap","_getVideoFrozenTimeout","isP2PActive","_onEndpointConnStatusChanged","onEndpointConnStatusChanged","_onP2PStatus","refreshConnectionStatusForAll","_onUserLeft","onUserLeft","_onTrackRtcMuted","onTrackRtcMuted","_onTrackRtcUnmuted","onTrackRtcUnmuted","_onRemoteTrackAdded","_onRemoteTrackRemoved","_onSignallingMuteChanged","onSignallingMuteChanged","_onTrackVideoTypeChanged","onTrackVideoTypeChanged","_onLastNChanged","_onLastNValueChanged","participantIds","clearRtcMutedTimestamp","endpointId","isActive","figureOutConnectionStatus","_changeConnectionStatus","newStatus","getConnectionStatus","getId","_setConnectionStatus","remoteTrack","JitsiTrackEvents","hasAnyVideoRTCMuted","hasAnyVideoTrackWebRTCMuted","timeout","participants","inP2PMode","isRestoringTimedOut","_isRestoringTimedout","audioOnlyMode","getLastN","isConnActiveByJvb","newState","_getNewStateForP2PMode","_getNewStateForJvbMode","_clearRestoringTimer","oldConnectionStatus","connectionStatus","nowMs","maybeSendParticipantConnectionStatusEvent","startedMs","videoTracks","getTracksByMediaType","participantConnectionStatus","createParticipantConnectionStatusEvent","leavingLastN","enteringLastN","rTimer","JitsiParticipant","hidden","statsID","identity","isReplacing","isReplaced","_jid","_id","_conference","_displayName","_supportsDTMF","_tracks","_role","_hidden","_statsID","_connectionStatus","_properties","_identity","_isReplacing","_isReplaced","_features","getConference","getProperty","getTracks","jitsiTrack","isWebRTCTrackMuted","oldValue","getStatsID","isHidden","isHiddenFromRecorder","user","isAudioMuted","_isMediaTypeMuted","getRole","setRole","newRole","setIsReplacing","newIsReplacing","setIsReplaced","newIsReplaced","supportsDTMF","getFeatures","hasFeature","feature","newFeatures","getBotType","_botType","setBotType","newBotType","JitsiConnectionEvents","WRONG_STATE","DISPLAY_NAME_REQUIRED","JitsiConnectionErrors","CONNECTION_DROPPED_ERROR","OTHER_ERROR","SERVER_ERROR","Deferred","promise","clearRejectTimeout","_timeout","setRejectTimeout","ms","Listenable","kJitsiE2EE","E2EEcontext","sharedKey","baseUrl","ljm","querySelector","idx","workerUrl","workerBlob","Blob","URL","createObjectURL","_worker","Worker","operation","cleanup","cleanupAll","handleReceiver","receiverStreams","readableStream","readable","writableStream","handleSender","sender","senderStreams","setKey","keyIndex","KeyHandler","e2eeCtx","E2EEContext","_enabling","_onMediaSessionStarted","_onLocalTrackAdded","_setupReceiverE2EEForTrack","_trackMuteChanged","isEnabled","_setEnabled","setLocalParticipantProperty","_restartMediaSessions","setEncryptionKey","getMediaSessions","_setupSenderE2EEForTrack","localTracks","findReceiverForTrack","findSenderForTrack","ExternallyManagedKeyHandler","keyInfo","encryptionKey","v4","OLM_MESSAGE_TYPE","OLM_MESSAGE_TYPES","kOlmData","OlmAdapterEvents","OLM_ID_KEY_READY","PARTICIPANT_E2EE_CHANNEL_READY","PARTICIPANT_KEY_UPDATED","OlmAdapter","_conf","_init","_key","_keyIndex","_reqs","_sessionInitialization","_bootstrapOlm","_onEndpointMessageReceived","_onConferenceLeft","_onParticipantLeft","_onParticipantPropertyChanged","promises","localParticipantId","FEATURE_E2EE","_sendSessionInit","allSettled","Olm","pId","olmData","_getParticipantOlmData","uuid","uuidv4","JITSI_MEET_MUC_TYPE","olm","ciphertext","_encryptKeyInfo","_sendMessage","updateCurrentKey","clearParticipantSession","free","clearAllParticipantsSessions","_olmAccount","Account","idKeys","identity_keys","_idKey","curve25519","get_library_version","_onIdKeyReady","idKey","_onParticipantE2EEChannelReady","base64js","encrypt","_sendError","Session","create_outbound","otKey","ack","pendingSessionUuid","create_inbound","body","remove_one_time_keys","decrypt","json","safeJsonParse","lastKey","isEqual","isE2EEEnabled","sendMessage","generate_one_time_keys","otKeys","one_time_keys","mark_keys_as_published","ManagedKeyHandler","_conferenceJoined","_olmAdapter","_rotateKey","debounce","_rotateKeyImpl","_ratchetKey","_ratchetKeyImpl","_onParticipantKeyUpdated","_onParticipantJoined","initSessions","_generateKey","updateKey","material","keyBytes","subtle","importKey","newKey","textEncoder","TextEncoder","deriveBits","salt","encode","ratchet","getRandomValues","E2EEncryption","e2ee","_externallyManaged","externallyManagedKey","_keyHandler","testing","disableE2EE","enableEncodedTransformSupport","setEnabled","IDENTITY_PROPERTIES","IDENTITY_PROPERTIES_FOR_COMPARE","compareIdentities","res","parseDiscoInfo","identities","$","_","getAttribute","category","Caps","disco","rooms","externalFeatures","_addChatRoom","_removeChatRoom","addFeature","submit","external","_generateVersion","_updateRoomWithExternalFeatures","removeFeature","sendPresence","removeFromPresence","children","addOrReplaceInPresence","getFeaturesAndIdentities","_getDiscoInfo","response","_fixChatRoomPresenceMap","xmlns","ver","_notifyVersionChanged","sortedIdentities","accumulatedValue","sortedFeatures","b64_sha1","generateSha","_identities","NETWORK_INFO_EVENT","_current","isOnline","updateNetworkInfo","ResumeTask","stropheConnection","_stropheConn","_resumeRetryN","_retryDelay","retryDelay","schedule","_cancelResume","_networkOnlineListener","NetworkInfo","_scheduleResume","_resumeTimeout","minDelay","base","getJitterDelay","_resumeConnection","streamManagement","resumeToken","getResumeToken","service","pattern","oldToken","LastRequestTracker","_lastSuccess","_lastFailedMessage","startTracking","xmppConnection","originalRawInput","rawInput","rawMessage","includes","connected","getLastFailedMessage","getTimeSinceLastSuccess","getConnectionPluginDefinition","ConnectionPluginListenable","PingConnectionPlugin","ConnectionPlugin","getTimeSinceLastServerResponse","onPingThresholdExceeded","pingOptions","failedPings","_onPingThresholdExceeded","_getTimeSinceLastServerResponse","pingInterval","pingTimeout","pingThreshold","threshold","pingTimestampsToKeep","pingExecIntervals","ping","_addPingExecutionTimestamp","iq","$iq","to","sendIQ2","startInterval","remoteJid","_lastServerCheck","errmsg","stopInterval","getTime","getPingSuspendTime","pingIntervals","maxInterval","previousTS","currentInterval","XmppConnection","CONN_STATUS_CHANGED","CONN_SHARD_CHANGED","Status","enableWebsocketResume","websocketKeepAlive","websocketKeepAliveUrl","serviceUrl","shard","xmppPing","_options","_usesWebsocket","startsWith","maxRetries","_rawInputTracker","LastSuccessTracker","_resumeTask","_deferredIQs","addConnectionPlugin","_onPingErrorThresholdExceeded","_oneSuccessfulConnect","websocket","_proto","socket","isUsingWebSocket","WebSocket","OPEN","disconnecting","domain","lastResponseHeaders","pingDomain","_maybeStartWSKeepAlive","plugin","attach","sid","rid","_stropheConnectionCb","pass","targetCallback","blockCallback","_maybeEnableStreamResume","_keepAliveAndCheckShard","_processDeferredIQs","_tryResumingConnection","_wsKeepAlive","_clearDeferredIQs","deferred","closeWebsocket","_closeSocket","_onClose","enable","intervalWithJitter","fetch","responseShard","headers","timeLeft","sendIQ","send","stanza","elem","errback","sendUnavailableBeacon","sendBeacon","_changeConnectStatus","_buildBody","attrs","pres","$pres","cnode","tree","_abortAllRequests","_doDisconnect","JitsiTranscriptionStatus","ON","OFF","AVModeration","_xmpp","_mainRoom","_moderationEnabledByType","_whitelistAudio","_whitelistVideo","_onMessage","avModerationComponentAddress","$msg","up","approve","jidToWhitelist","jidToBlacklist","removed","approved","whitelists","newWhitelists","oldList","newList","FEATURE_KEY","BREAKOUT_ROOM_ACTIONS","ADD","REMOVE","MOVE_TO_ROOM","BREAKOUT_ROOM_EVENTS","BreakoutRooms","_handleMessages","_rooms","createBreakoutRoom","subject","removeBreakoutRoom","breakoutRoomJid","sendParticipantToRoom","participantJid","getComponentAddress","breakoutRoomsComponentAddress","_setIsBreakoutRoom","isBreakoutRoom","_isBreakoutRoom","_setMainRoomJid","_mainRoomJid","getMainRoomJid","EMAIL_COMMAND","Lobby","mainRoom","maybeJoinLobbyRoom","_maybeJoinLobbyRoom","lobbyRoomJid","lobbySupported","setMembersOnly","disable","lobbyRoom","membersOnlyEnabled","sendPrivateMessage","getLocalId","myroomjid","addMessageListener","removeMessageHandler","leave","setLobbyRoomJid","joined","email","customDomain","createRoom","disableDiscoInfo","disableFocus","enableLobby","nick","isHiddenDomain","botType","members","getBreakoutRooms","avatar","clean","isSelfPresence","invitePassword","denyAccess","kick","approveAccess","mainRoomJid","memberRoomJid","msgToSend","_callStatsUserName","_machineId","externalStorage","callStatsUserName","UsernameGenerator","generateCallStatsUserName","machineId","amDid","jitsiMeetId","_p8","generateJitsiMeetId","createExpBackoffTimer","step","Moderator","Settings","xmppService","getNextTimeout","getNextErrorTimeout","externalAuthEnabled","sipGatewayEnabled","attachEvent","isExternalAuthEnabled","isSipGatewayEnabled","onMucMemberLeft","setFocusUserJid","focusJid","focusUserJid","getFocusUserJid","getFocusComponent","focusComponent","hosts","createConferenceIq","machineUID","disableRtx","audioPacketDelay","startBitrate","minBitrate","rtcstatsEnabled","callstatsDisabled","parseSessionId","resultIq","attr","parseConfigOptions","authenticationEnabled","allocateConferenceFocus","_allocateConferenceFocusSuccess","_allocateConferenceFocusError","invalidSession","reservationErr","errorCode","errorTextNode","errorMsg","anonymousdomain","waitMs","retrySec","authenticate","errorIq","prop","getLoginUrl","urlCallback","failureCallback","_getLoginUrl","popup","urlCb","failureCb","decodeURIComponent","getPopupLoginUrl","logout","logoutUrl","packet2JSON","xmlElement","nodes","child","json2packet","packet","filterNodeFromPresenceJSON","nodeName","MEMBERS_AFFILIATIONS","ChatRoom","XMPP","replaceParticipant","presMap","presHandlers","_removeConnListeners","inProgressEmitted","focusMucJid","noBridgeAvailable","moderator","lobby","avModeration","breakoutRooms","initPresenceMap","lastPresences","phoneNumber","phonePin","participantPropertyListener","locked","transcriptionStatus","xns","statsId","presenceUpdateTime","onConnStatusChanged","fromJoin","billingId","presenceSyncTime","discoRoomInfo","getInfo","meetingIdValEl","setMeetingId","membersOnly","lobbyRoomField","isBreakoutField","breakoutMainRoomField","meetingId","createNonAnonymousRoom","getForm","form","formSubmit","CONNECTED","onPresence","member","statusEl","textContent","hasStatusUpdate","hasVersionUpdate","xElement","getElementsByTagNameNS","mucUserItem","isReplaceParticipant","affiliation","isFocus","hiddenDomain","fromHiddenDomain","presence","xEl","remove","extractIdentityInformation","userInfo","tags","hiddenFromRecorderFeatureEnabled","groupInfo","_extractFeatures","_initFocus","memberOfThis","displayJids","restartByTerminateSupported","supportsRestartByTerminate","att","phone","pin","processNode","var","focusFeatures","tagHandlers","elementName","setSubject","onParticipantLeft","skipEvents","onPresenceUnavailable","destroySelect","reasonSelect","isKick","membersKeys","actorSelect","actorNick","onMessage","settingsErrorMsg","subjectText","stamp","dateParts","invite","passwordSelect","jsonMessage","parsedJson","tryParseJSONAndVerify","onPresenceError","lobbyRoomNode","lobbyRoomOldNode","setAffiliation","grantIQ","kickIQ","lockRoom","onSuccess","onError","onNotSupported","formsubmit","formToSubmit","addToPresence","matchingNodes","getFromPresence","removePresenceListener","handlerIdx","mucJid","getMemberRole","peerJid","addAudioInfoToPresence","audioMutedTagName","addVideoInfoToPresence","videoMutedTagName","getMediaPresenceInfo","mutedNode","codecTypeNode","videoTypeNode","codecType","getLastPresence","mucNick","isSIPCallingSupported","dial","rayo","hangup","getLobby","getAVModeration","getPhoneNumber","getPhonePin","getMeetingId","muteParticipant","iqToFocus","onMute","onMuteVideo","onMucLeft","doReject","MucConnectionPlugin","isRoomCreated","_sourceNameSignaling","sourceNameSignaling","_sendMultipleVideoStreams","sendMultipleVideoStreams","isMultiStreamSupportEnabled","isSourceNameSignalingEnabled","_createSourceExtension","owner","sourceCompactJson","$build","FeatureFlags","_createSsrcGroupExtension","ssrcGroupCompactJson","_getOrCreateRtpDescription","jingle","expandSourcesFromJson","jsonMessageXml","audioRtpDescription","videoRtpDescription","ownerSources","videoSources","videoSsrcGroups","audioSources","audioSsrcGroups","ScreenObtainer","obtainStream","_createObtainStreamMethod","onFailure","JitsiMeetNW","obtainDesktopStream","jitsiError","obtainScreenOnElectron","obtainScreenFromGetDisplayMediaRN","obtainScreenFromGetDisplayMedia","_getAudioConstraints","audioQuality","stereo","autoGainControl","channelCount","echoCancellation","noiseSuppression","JitsiMeetScreenObtainer","openDesktopPicker","desktopSharingFrameRate","desktopSharingSources","streamId","streamType","screenShareAudio","audioConstraints","optionalConstraints","optional","chromeMediaSource","chromeMediaSourceId","minFrameRate","maxFrameRate","maxWidth","screen","maxHeight","sourceId","sourceType","cursor","errorDetails","errorName","errorStack","setDesktopSharingFrameRate","maxFps","SDPUtil","filterSpecialChars","iceparams","mediadesc","sessiondesc","findLine","parseICEUfrag","parseICEPwd","buildICEUfrag","frag","buildICEPwd","parseMID","parseMSIDAttribute","ssrcLines","msidLine","buildMLine","parseRTPMap","clockrate","parseSCTPMap","parseSCTPPort","buildRTPMap","parseCrypto","fingerprint","parseICECandidate","elems","network","buildICECandidate","cand","hasOwnAttribute","parseSSRC","parseSourceNameLine","sourceNameLine","ssrcSdpLine","parseRTCPFB","haystack","needle","findLines","needles","candidateToJingle","candidateFromJingle","parsePrimaryVideoSsrc","videoMLine","generateSsrc","ssrcLine","parseGroupSsrcs","ssrcGroup","getMedia","getUfrag","ufragLines","preferCodec","codecName","matchingPayloadTypes","payloadTypes","payloadIndex","stripCodec","highProfile","h264Pts","removePts","stripH264HighCodec","CodecMimeType","rtxApts","rtxPts","keepPts","MediaDirection","rtcpFb","SDP","mediaI","raw","arrayEquals","array1","array2","equals","SDPDiffer","mySDP","otherSDP","failICE","removeTcpCandidates","removeUdpCandidates","addMlineForNewLocalSource","clonedeep","groups","mids","getMediaSsrcMap","mediaSSRCs","mediaindex","linessrc","containsSSRC","medias","toJingle","thecreator","assrcline","creator","amidline","rtpmap","afmtpline","fmtpParameters","rtcpFbToJingle","availableSsrc","ssrcParameters","sourceName","ridLines","rids","ridLine","ridInfo","extmapLines","extmap","senders","transportToJingle","sctpport","sctpmap","sctpAttrs","streams","setupLine","setup","iceParameters","payloadtype","feedback","rtcpFbFromJingle","feedbackElementTrrInt","fromJingle","contents","jingle2media","setAttribute","__","hdrExt","userSources","nonUserSources","isUserSource","sourceStr","getNewMedia","myMedias","othersMedias","newMedia","othersMediaIdx","myMedia","othersMedia","otherSsrcGroup","matched","mySsrcGroup","modify","sdpMediaSsrcs","modified","ssrcNum","mediaSsrc","AsyncQueue","_queue","_processQueueTasks","_stopped","finishedCallback","shutdown","PENDING","ENDED","JingleSession","localJid","mediaConstraints","pcConfig","isInitiator","usedrip","dripContainer","_signalingLayer","initiatorJid","responderJid","signalingLayer","JingleSessionState","doInitialize","addIceCandidates","getState","addSources","removeSources","terminate","failure","acceptOffer","_getInitiatorJid","MediaSessionEvents","IQ_TIMEOUT","JingleSessionPC","jingleContents","videoContents","maxFrameHeightSel","_bridgeSessionId","_cachedOldLocalSdp","_cachedNewLocalSdp","_iceCheckingStartedTimestamp","_gatheringStartedTimestamp","localRecvMaxFrameHeight","_localVideoActive","_remoteVideoActive","_gatheringReported","lasticecandidate","closed","remoteRecvMaxFrameHeight","modificationQueue","wasConnected","establishmentDuration","_xmppListeners","onXmppStatusChanged","_removeSenderVideoConstraintsChangeListener","_assertNotEnded","isReconnect","wasstable","webrtcIceUdpDisable","webrtcIceTcpDisable","pcOptions","gatherStats","maxstats","capScreenshareBitrate","enableInsertableStreams","videoQuality","forceTurnRelay","enableUnifiedOnChrome","disableSimulcast","abtestSuspendVideo","_abtestSuspendVideoEnabled","preferH264","disableH264","preferredCodec","createPeerConnection","onicecandidate","ev","phase","initiator","sendIceCandidate","onsignalingstatechange","signalingState","connectionState","oniceconnectionstatechange","isStable","iceConnectionState","reconnect","usesTerminateForRestart","enableIceRestart","iceStarted","onconnectionstatechange","icestate","onnegotiationneeded","remoteDescription","workFunction","oldSdp","localDescription","_renegotiate","configureSenderVideoEncodings","newSdp","notifyMySSRCUpdate","getRemoteRecvMaxFrameHeight","localSDP","ice","sdpMLineIndex","jcand","errorMesssage","sendIceCandidates","cands","sdpMid","fingerprintLine","required","newJingleErrorHandler","sendIceFailedNotification","sessionInfo","iceCandidates","rtcCandidate","RTCIceCandidate","iceCandidate","addIceCandidate","outerHTML","readSsrcInfo","ssrcElement","setSSRCOwner","setTrackSourceName","i3","ssrcInfoElement","jidOrEndpointId","generateRecvonlySsrc","getConfiguredVideoCodec","jingleOffer","setOfferAnswerCycle","sendSessionAccept","addTracks","localTrack","addTrack","createOffer","offerSdp","setLocalDescription","sendSessionInitiate","setAnswer","jingleAnswer","remoteSdp","_responderRenegotiate","jingleOfferAnswerIq","newRemoteSdp","_processNewJingleOfferIq","oldLocalSdp","bridgeSessionId","sendContentModify","newLocalSdp","setVideoCodecs","preferred","disabled","current","replaceTransport","jingleOfferElem","enableForcedReload","sendTransportAccept","originalOffer","clone","newFingerprint","accept","responder","maxFrameHeight","sessionModify","setReceiverVideoConstraint","transportAccept","medialines","sendTransportReject","transportReject","setSenderVideoConstraint","audioActive","audioTransferActive","videoActive","videoTransferActive","setMediaTransferActive","jitsiLocalTrack","getLocalVideoTracks","getSourceName","getLocalVideoTrack","setSenderVideoConstraints","sendSessionTerminate","sessionTerminate","reasonDescription","restart","requestRestart","onTerminated","reasonCondition","reasonText","close","_parseSsrcInfoFromSourceAdd","sourceAddElem","currentRemoteSdp","addSsrcInfo","i1","i2","addRemoteStream","_addOrRemoveRemoteStream","removeRemoteStream","removeRemoteStreamsOnLeave","finishCallback","removeSsrcInfo","getRemoteSourceInfoByParticipant","_processRemoteRemoveSource","newLocalSDP","isAdd","logPrefix","errMsg","addOrRemoveSsrcInfo","_parseSsrcInfoFromSourceRemove","_processRemoteAddSource","findIndex","desiredDirection","getDesiredMediaDirection","optionalRemoteSdp","_initiatorRenegotiate","setRemoteDescription","createAnswer","answer","offer","replaceTrack","oldTrack","newTrack","clearRecvonlySsrc","shouldRenegotiate","sourceRemoveElem","_verifyNoSSRCChanged","operationName","oldSDP","currentLocalSDP","sdpDiff","addedMedia","removedMedia","addTrackAsUnmute","_addRemoveTrackAsMuteUnmute","removeTrackAsMute","isMute","oldLocalSDP","removeTrackMute","addTrackUnmute","logAudioStr","logVideoStr","isSessionActive","audioActiveChanged","setAudioTransferActive","pcVideoActiveChanged","setVideoTransferActive","modifyContents","newVideoSenders","parseVideoSenders","newMaxFrameHeight","parseMaxFrameHeight","_modifyRemoteVideoActive","remoteVideoSenders","isRemoteVideoActive","newSDP","getSignaledSourceInfo","sdpDiffer","mediaIndex","signaledSsrcs","removedAnySSRCs","ctx","sourceInfo","request","errResponse","errorElSel","errorReasonSel","errorMsgSel","getIceConnectionState","getConnectionState","abTesting","enableSuspendVideoTest","_parseIceCandidates","parseCandidates","candidateAttrs","JingleConnectionPlugin","iceConfig","sessions","jvbIceConfig","jvb","p2pIceConfig","offerToReceiveAudio","offerToReceiveVideo","onJingle","fromJid","sess","jsonMessages","audioVideoSsrcs","logMessage","endpoint","startMuted","iceUfrag","icePwd","dtlsFingerprint","successTime","newP2PJingleSession","me","getStunAndTurnCredentials","v2Res","onReceiveStunAndTurnCredentials","v1Res","iceservers","dict","urls","credential","temp","useTurnUdp","iceServers","getLog","updateLog","StropheLogger","logIncoming","rawOutput","logOutgoing","RAYO_XMLNS","RayoConnectionPlugin","onRayo","roomPass","req","callResource","lastErrorStatus","resetLastErrorStatusRegExpr","lastErrorStatusRegExpr","FAILURE_REGEX","DEFAULT_STUN_SERVERS","FEATURE_JIGASI","token","disconnectInProgress","authenticatedUser","trace","errStatusCapture","createConnection","bosh","deploymentInfo","details","shard_changed","suspend_time","time_since_last_success","_initStrophePlugins","initFeaturesList","disableBeforeUnloadHandlers","enableOpusRed","enableRemb","enableTcc","enableLipSync","getConnection","connectionHandler","credentials","statusStr","_maybeSendDeploymentInfoStat","_sysMessageHandler","deleteHandler","sendDiscoInfo","_resetState","sendDeploymentInfo","_processDiscoInfoIdentities","anonymousConnectionFailed","connectionFailed","lastErrorMsg","wasIntentionalDisconnect","_getConnectionFailedReasonDetails","lastFailedRawMessage","_parseConnectionFailedMessage","speakerStatsComponentAddress","conferenceDurationComponentAddress","processLobbyFeatures","fr","endsWith","region","_onPrivateMessage","_connect","_addSysHandler","_onSystemMessage","foundIceServers","attaching","configDomain","hostname","onCreateResource","getRoomJid","mucNickname","muc","getJingleLog","getXmppLog","getSessions","disconnectListener","_cleanupXmppConnection","evType","sync","p2pStunServers","stunServers","iceTransportPolicy","headersArr","header","sendFacialExpressionEvent","expression","jsonString","users","created_timestamp","acceptedStatuses","aprops","authenticateAndUpgradeRole","rejectPromise","onLoginSuccessful","roomPassword","canceled","authenticationError","connectionError","CodecSelection","disabledCodec","_getCodecMimeType","jvbCodec","p2pCodec","jvbPreferredCodec","_isCodecSupported","p2pPreferredCodec","_selectPreferredCodec","mediaSession","currentCodec","selectedCodec","enforcePreferredCodec","remoteParticipants","remote","peerMediaInfo","getPeerMediaInfo","peerCodec","getPreferredCodec","BridgeChannel","wsUrl","_channel","_eventEmitter","_mode","_areRetriesEnabled","_closedFromClient","datachannel","createDataChannel","_handleChannel","_wsUrl","_initWebSocket","ws","_startConnectionRetries","timeoutS","reload","isOpen","_retryTimeout","_stopConnectionRetries","_retryWebSocketConnection","closeEvent","createBridgeChannelClosedEvent","mode","sendEndpointStatsMessage","_send","colibriClass","msgPayload","sendSetLastNMessage","lastN","sendSelectedEndpointsMessage","endpointIds","selectedEndpoints","sendReceiverVideoConstraintMessage","maxFrameHeightPixels","sendNewReceiverVideoConstraintsMessage","sendVideoTypeMessage","sendSourceVideoTypeMessage","channel","onopen","onmessage","dominantSpeakerEndpoint","previousSpeakers","active","lastNEndpoints","forwardedSources","videoConstraints","onclose","jsonObject","DEFAULT_CONSTRAINTS","ideal","audioOutputDeviceId","audioOutputChanged","disableAP","disableAEC","disableNS","disableAGC","featureDetectionAudioEl","isAudioOutputDeviceChangeAvailable","setSinkId","availableDevicesPollTimer","availableDevices","emptyFuncton","updateGrantedPermissions","um","audioTracksReceived","getAudioTracks","videoTracksReceived","getVideoTracks","grantedPermissions","sendDeviceListToAnalytics","deviceList","audioInputDeviceCount","audioOutputDeviceCount","videoInputDeviceCount","videoOutputDeviceCount","device","deviceId","groupId","label","updateKnownDevices","pds","newDevices","mediaDeviceInfoToJSON","facing","compareAvailableMediaDevices","rtcUtils","origAttachMediaStream","RTCPeerConnectionType","RTCPeerConnection","attachMediaStream","getStreamID","getTrackID","element","srcObject","isDeviceChangeAvailable","getAudioOutputDevice","ex","pcConstraints","googScreencastMinBitrate","googCpuOveruseDetection","screenObtainer","isDeviceListAvailable","enumerateDevices","_getUserMedia","umDevices","gumTimeout","timeoutExpired","_getDesktopMedia","_getMissingTracks","requestedDevices","missingDevices","audioDeviceRequested","videoDeviceRequested","obtainAudioAndVideoPermissions","otherOptions","mediaStreamsMetaData","maybeRequestDesktopDevice","desktopSharingSourceDevice","matchingDevice","maybeRequestCaptureDevices","requestedCaptureDevices","Resolutions","cameraDeviceId","facingMode","CameraFacingMode","micDeviceId","getConstraints","desktopStream","desktopAudioTracks","desktopAudioStream","MediaStream","desktopVideoTracks","desktopVideoStream","avStream","audioTracks","audioStream","effects","videoStream","stopMediaStream","deviceType","mediaStream","release","isDesktopSharingEnabled","setAudioOutputDevice","getCurrentlyAvailableMediaDevices","arePermissionsGrantedForAvailableDevices","getEventDataForActiveDevice","deviceData","setSuspendVideo","googSuspendBelowMinBitrate","trackHandler2Prop","JitsiTrack","streamInactiveHandler","trackMediaType","containers","_streamInactiveHandler","_setStream","_addMediaStreamInactiveHandler","onended","oninactive","_setHandler","_unregisterHandlers","videoTrack","getVideoType","isLocalAudioTrack","getOriginalStream","getStreamId","getTrack","getTrackLabel","getTrackId","getUsageLabel","_maybeFireTrackAttached","container","_onTrackAttach","RTCUtils","_attachTTFMTracker","detach","_onTrackDetach","isScreenSharing","newAudioLevel","getMSID","trackId","setAudioOutput","JitsiLocalTrack","rtcId","_setEffectInProgress","effect","_startStreamEffect","displaySurface","getSettings","metadata","maxEnabledResolution","_constraints","_prevSetMuted","_facingMode","_trackEnded","_hasSentData","_testDataSent","_realDeviceId","_sourceName","_trackMutedTS","_onDeviceListWillChange","oldRealDeviceId","_setRealDeviceIdFromDeviceList","_onAudioOutputDeviceChanged","_initNoDataFromSourceHandlers","_addStreamToConferenceAsUnmute","_addLocalTrackAsUnmute","_fireNoDataFromSourceEvent","isReceivingData","createNoDataFromSourceEvent","_isNoDataFromSourceEventsEnabled","_queueSetMuted","setMuted","_setMuted","_removeStreamFromConferenceAsMute","successCallback","_removeLocalTrackAsMute","_sendMuteStatus","_setTrackMuteStatus","logMuteInfo","_streamEffect","_stopStreamEffect","stopStream","streamOptions","getDeviceId","getCameraFacingMode","streamsInfo","streamInfo","cont","_sendBridgeVideoTypeMessage","storedMSID","_originalStream","startEffect","stopEffect","_switchCamera","_switchStreamEffect","setEffect","removeTrack","trackSettings","getDuration","isEnded","_stopStreamInProgress","_effectEnabled","setConference","setSourceName","unmute","found","PLAN_B_MIDS","findSimGroup","grp","findFidGroup","addSimGroupSources","sourceGroups","sourceList","findSourcebyId","relatedFidGroup","relatedSsrc","addSourcesToMline","otherSsrc","simGroup2","checkIfMlineForSsrcExists","mlines","Interop","toPlanB","sessionMedia","bLine","bundle","msidSemantic","semantic","resStr","toUnifiedPlan","currentDesc","iceRestart","newDesc","oldDesc","newMLine","oldMLine","checkForIceRestart","newIceUfrag","newIcePwd","newMline","mLineForData","ssrc2group","createSourceGroupMap","bundleOnly","resultSdp","SignalingEvents","PEER_MUTED_CHANGED","PEER_VIDEO_TYPE_CHANGED","getSourceNameForJitsiTrack","trackIdx","getMediaTypeFromSourceName","firstLetterOfMediaTypeIdx","firstLetterOfMediaType","SignalingLayer","getSSRCOwner","getPeerSourceInfo","getTrackSourceName","parsePrimarySSRC","parseSecondarySSRC","_getSSRCCount","MLineWrap","getSSRCAttrValue","ssrcNumber","attrName","ssrcObj","removeSSRC","addSSRCAttribute","findGroup","findGroups","findGroupByPrimarySSRC","primarySSRC","findSSRCByMSID","getSSRCCount","containsAnySSRCGroups","getPrimaryVideoSsrc","fecGroup","getRtxSSRC","getSSRCs","getPrimaryVideoSSRCs","videoSSRCs","ssrcGroupInfo","dumpSSRCGroups","removeGroupsWithSSRC","removeGroupsBySemantics","replaceSSRC","oldSSRC","newSSRC","addSSRCGroup","SdpTransformWrap","rawSDP","parsedSDP","selectMedia","selectedMLines","toRawSDP","LocalSdpMunger","localEndpointId","_addMutedLocalVideoTracksToSDP","transformer","localVideos","isCamera","isInPeerConnection","isMediaStreamInPc","requiredSSRCs","isSimulcastOn","simulcast","sdpConsistency","cachedPrimarySsrc","primaryCname","rtxModifier","modifyRtxSsrcs2","_generateMsidAttribute","pcId","_transformMediaIdentifiers","sourceToMsidMap","streamAndTrackIDs","mediaDirection","generatedMsid","maybeAddMutedLocalVideoTracksToSDP","transformStreamIdentifiers","sessionDesc","audioMLine","_injectSourceNames","updateAssociatedRtxStream","primarySsrcInfo","rtxSsrc","previousRtxSSRC","RtxModifier","correspondingRtxSsrcs","ssrcMapping","modifyRtxSsrcs","sdpStr","sdpTransformer","videoMLines","primaryVideoSsrcs","correspondingRtxSsrc","stripRtx","fidGroups","SdpConsistency","clearVideoSsrcCache","injectRecvOnly","setPrimarySsrc","hasPrimarySsrcCached","makeVideoPrimarySsrcsConsistent","newPrimarySsrc","SdpSimulcast","_ssrcCache","_fillSsrcsFromCache","cachedSsrcs","newSsrcs","_getSsrcAttribute","_generateNewSsrcsForSimulcast","addAssociatedAttributes","_generateSsrc","mungedSsrcs","mungedSsrcGroups","TrackStreamingStatus","TrackStreamingStatusImpl","isInForwardedSources","restoringTimer","streamingStatusMap","trackTimer","outOfForwardedSourcesTimeout","figureOutStreamingStatus","_onForwardedSourcesChanged","onForwardedSourcesChanged","maybeSendTrackStreamingStatusEvent","_changeStreamingStatus","getTrackStreamingStatus","_setTrackStreamingStatus","isVideoRTCMuted","oldStreamingStatus","streamingStatus","trackStreamingStatus","createTrackStreamingStatusEvent","leavingForwardedSources","enteringForwardedSources","_clearEnteredForwardedSourcesTimestamp","_setEnteredForwardedSourcesTimestamp","enteredForwardedSourcesTimestamp","_getEnteredForwardedSourcesTimestamp","_isCurrentTrack","ttfmTrackerAudioAttached","ttfmTrackerVideoAttached","containerEvents","JitsiRemoteTrack","ownerEndpointId","_trackStreamingStatus","_trackStreamingStatusImpl","_enteredForwardedSourcesTimestamp","_addEventListener","_removeEventListener","hasBeenMuted","_bindTrackHandlers","_containerHandlers","_containerEventHandler","_onTrackMute","_onTrackUnmute","_initTrackStreamingStatus","_disposeTrackStreamingStatus","setMute","getSSRC","_setVideoType","_playCallback","getConnectionTimes","gumStart","gumEnd","gumDuration","ttfm","_getStatus","_p2pConnStatusRtcMuteTimeout","_peerConnStatusRtcMuteTimeout","_peerConnStatusOutOfLastNTimeout","HD_BITRATE","SIM_LAYER_RIDS","TPCUtils","bitrateSettings","maxBitratesVideo","standardBitrates","low","standard","high","videoBitrates","encodingBitrates","localStreamEncodingsConfig","scaleResolutionDownBy","_getStreamEncodings","ensureCorrectOrderOfSsrcs","parsedSdp","reorderedSsrcs","findTransceiver","getTransceivers","insertUnifiedPlanSimulcastReceive","simulcast_03","simulcastLine","transceiverInit","sendEncodings","addTransceiver","calculateEncodingsActiveState","localVideoTrack","newHeight","encoding","isSharingLowFpsScreen","calculateEncodingsBitrates","desktopShareBitrate","desktopBitrate","presenterEnabled","transceiver","currentDirection","trackIndex","setEncodings","getParameters","encodings","setParameters","transceivers","updateEncodingsResolution","TraceablePeerConnection","_dtmfSender","_dtmfTonesQueue","remoteTracks","_addedStreams","localSSRCs","localUfrag","remoteUfrag","_dtlsTransport","_peerVideoTypeChanged","_peerMutedChanged","safeConstraints","rtcStatsSFUP2P","tpcUtils","statsinterval","_capScreenshareBitrate","_usesUnifiedPlan","_usesTransceiverCodecPreferences","interop","localSdpMunger","getLocalEndpointId","_senderVideoMaxHeight","_senderMaxHeights","what","onTrack","evt","_remoteTrackAdded","_remoteTrackRemoved","onaddstream","_remoteStreamAdded","onremovestream","_remoteStreamRemoved","ondatachannel","_processStat","stat","statValue","endTime","dumpSDP","isAddOperation","hasLocalSource","hasAnyTracksOfType","mediaTransferActive","_getReceiversByEndpointIds","endpoints","receivers","getRemoteTracks","remoteTrackIds","getReceivers","audioReceivers","getSynchronizationSources","tracks","endpointTracksByMediaType","primarySsrcs","fidLines","getTargetVideoBitrates","findTrackById","RTC","isUserStreamById","onaddtrack","onremovetrack","streamAudioTracks","audioTrack","streamVideoTracks","remoteSDP","mediaLines","mls","trackSsrc","_createRemoteTrack","remoteTracksMap","userTracksByMediaType","existingTrack","isUserStream","toBeRemoved","_removeRemoteTrack","removeRemoteTracks","removedTracks","remoteTracksByMedia","_extractSSRCMap","groupsMap","groupSSRCs","_getSSRC","_injectSsrcGroupForUnifiedSimulcast","getters","audioMedia","changed","videoMedia","enforceSendRecv","_adjustRemoteMediaDirection","_isSharingScreen","_mungeCodecOrder","codecPreference","bitrates","hdBitrate","webrtcStream","_addStream","generateNewStreamSSRCInfo","rtxSsrcMapping","promiseChain","_assertTrackBelongs","webRtcStream","addStream","_removeStream","removeStream","doesBelong","defaultCodec","lowFps","findSenderByKind","getSenders","negotiationNeeded","mediaActive","_ensureSimulcastGroupIsLast","localSdp","videoStartIndex","simStartIndex","otherStartIndex","simEndIndex","simStr","otherEndIndex","sdpHead","simStrTrimmed","sdpTail","_adjustLocalMediaDirection","modifiedDirection","desiredAudioDirection","desiredVideoDirection","hasRemoteSource","_mungeOpus","opusMaxAverageBitrate","mLines","fmtpOpus","fmtpConfig","sdpChanged","maxaveragebitrate","mungedConfig","_initializeDtlsTransport","onstatechange","currentDescription","firstSsrcs","newSsrcLines","filteredLines","ssrcId","cnameLine","replaceDefaultUnifiedPlanMsid","normalizePlanB","videoSender","preference","degradationPreference","encodingsEnabledState","maxBitrates","scaleFactor","layer","sendTones","tones","interToneGap","rtpSender","dtmf","localAudioTrack","createDTMFSender","ontonechange","_onToneChange","toneBuffer","insertDTMF","tone","peerTracks","_removePeerConnection","_createOfferOrAnswer","isOffer","logName","handleSuccess","resolveFn","rejectFn","_processLocalSSRCsMap","handleFailure","eventType","capabilities","setCodecPreferences","oaPromise","_extractPrimarySSRC","sourceIdentifier","newSSRCNum","oldSSRCNum","activeStreams","currNumSsrcs","peerConnectionIdCounter","rtcTrackIdCounter","_createLocalTracks","mediaStreamMetaData","metaData","peerConnections","_lastN","_lastNEndpoints","_forwardedSources","_maxFrameHeight","_selectedEndpoints","_lastNChangeListener","_forwardedSourcesChangeListener","_onDeviceListChanged","_updateAudioOutputForAudioTracks","_videoType","BridgeVideoType","destroy","_channelOpenListener","tracksInfo","initializeBridgeChannel","logError","msgType","_receiverVideoConstraints","oldLastNEndpoints","leavingLastNEndpoints","enteringLastNEndpoints","oldForwardedSources","setNewReceiverVideoConstraints","setVideoType","sendSourceVideoType","selectEndpoints","ids","signaling","encodedInsertableStreams","sdpSemantics","bundlePolicy","newConnection","traceablePeerConnection","addLocalTrack","getForwardedSources","localVideo","getLocalAudioTrack","localAudio","pcRemoteTracks","mutePromises","removeLocalTrack","elSelector","sendChannelMessage","setLastN","remoteAudioTracks","LOCAL_STATS_UPDATED","REMOTE_STATS_UPDATED","kSimulcastFormats","layers","targetRN","ConnectionQuality","_localStats","connectionQuality","jvbRTT","_lastConnectionQualityUpdate","_remoteStats","_timeIceConnected","_timeVideoUnmuted","ConferenceEvents","_updateLocalConnectionQuality","ConnectionQualityEvents","_broadcastLocalStats","_updateRemoteStats","_updateLocalStats","_maybeUpdateUnmuteTime","serverRegion","bridgeCount","_calculateConnectionQuality","resolutionName","quality","activeTPC","getActivePeerConnection","videoQualitySettings","millisSinceStart","simulcastFormat","targetHeight","rampUp","getTarget","maxIncreasePerSecond","prevConnectionQuality","diffSeconds","updateLocalConnectionQuality","isConnectionInterrupted","IceFailedHandling","_actOnIceFailed","explicitlyDisabled","useTerminateForRestart","jvbConnection","jvbConnIceState","_canceled","_iceFailedTimeout","DETECTOR_STATE_CHANGE","VAD_NOISY_DEVICE","VAD_REPORT_PUBLISHED","VAD_SCORE_PUBLISHED","VAD_TALK_WHILE_MUTED","NoAudioSignalDetection","_timeoutTrigger","_hasAudioInput","_audioLevel","_trackAdded","_clearTriggerTimeout","_handleAudioInputStateChange","DetectionEvents","_handleNoAudioInputDetection","_eventFired","_audioTrack","P2PDominantSpeakerDetection","myUserID","createAudioContext","AudioContextImpl","TrackVADEmitter","procNodeSampleRate","vadProcessor","_procNodeSampleRate","_vadProcessor","_localTrack","_bufferResidue","Float32Array","_audioContext","sampleRate","getRequiredPCMFrequency","_vadSampleSize","getSampleLength","_onAudioProcess","_initializeAudioContext","_audioSource","_audioProcessingNode","createScriptProcessor","audioEvent","inData","inputBuffer","getChannelData","completeInData","sampleTimestamp","pcmSample","vadScore","calculateAudioFrameVAD","score","pcmData","_connectAudioGraph","onaudioprocess","destination","_disconnectAudioGraph","_cleanupResources","getDeviceLabel","_destroyed","VADAudioAnalyser","createVADProcessor","_createVADProcessor","_vadEmitter","_isVADEmitterRunning","_detectionServices","_vadInitTracker","_processVADScore","_trackRemoved","addVADDetectionService","vadService","detector","_stopVADEmitter","_startVADEmitter","processVADScore","_changeDetectorsMuteState","changeMuteState","vadEmitter","VADNoiseDetection","_processing","_scoreArray","_audioLvlArray","_active","_calculateNoisyScore","scoreAvg","audioLevelAvg","_setActiveState","_recordValues","avgAudioLvl","_processTimeout","posAudioLevels","VADTalkMutedDetection","_calculateVADScore","E2ePingEvents","E2E_RTT_CHANGED","E2E_PING_REQUEST","E2E_PING_RESPONSE","ParticipantWrapper","e2eping","requests","lastRequestId","clearIntervals","sendRequest","handleResponse","maybeSendAnalytics","isDataChannelOpen","pingIntervalMs","analyticsInterval","analyticsIntervalMs","requestId","requestMessage","timeSent","createE2eRttEvent","E2ePing","participantJoined","participantLeft","messageReceived","dataChannelOpened","participantWrapper","handleRequest","Jvb121EventGenerator","_jvb121","evaluateStatus","oldStatus","getParticipantCount","ReceiverVideoConstraints","_defaultConstraints","defaultConstraints","onStageEndpoints","updateLastN","updateReceiveResolution","updateReceiverVideoConstraints","updateSelectedEndpoints","ReceiveVideoController","_rtc","startLastN","channelLastN","useNewBandwidthAllocationStrategy","remoteEndpointIds","oldConstraints","newConstraints","setPreferredReceiveMaxFrameHeight","setReceiverConstraints","isEndpointsFormat","isSourcesFormat","p2pSession","SendVideoController","_preferredSendMaxFrameHeight","_sourceSenderConstraints","_configureConstraintsForLocalSources","_onSenderConstraintsReceived","_propagateSendMaxFrameHeight","getActiveMediaSession","_senderVideoConstraints","idealHeight","sendMaxFrameHeight","_selectSendMaxFrameHeight","activeMediaSession","setPreferredSendMaxFrameHeight","getFocusRecordingUpdate","jibriStatus","recordingMode","sessionID","getHiddenDomainUpdate","liveStreamViewURLContainer","liveStreamViewURL","modeContainer","sessionIDContainer","getSessionIdFromIq","jibri","getSessionId","sessionIdContainer","isFromFocus","JibriSession","_connection","_setSessionID","setStatus","getID","_sessionID","getInitiator","_initiator","getLiveStreamViewURL","_liveStreamViewURL","getTerminator","_terminator","getMode","setError","setLiveStreamViewURL","appData","broadcastId","_createIQ","recordingXMLUtils","_setErrorFromIq","AudioOutputProblemDetector","_localAudioLevelCache","_reportedParticipants","_audioProblemCandidates","_numberOfRemoteAudioLevelsReceived","_onLocalAudioLevelsReport","_onRemoteAudioLevelReceived","_clearUserData","numberOfReports","localAudioLevels","remoteAudioLevels","localAudioLevelsString","createAudioOutputProblemEvent","AverageStatReport","calculate","appendReport","ConnectionAvgStats","avgRtpStatsReporter","_n","_sampleIdx","_avgRTT","_avgRemoteRTTMap","_avgRtpStatsReporter","_avgEnd2EndRTT","_onConnectionStats","_calculateAvgStats","_onRemoteStatsUpdated","_processRemoteStats","batchReport","jvbEnd2EndRTT","jvbStatsMonitor","avgRemoteRTT","_calculateAvgRemoteRTT","avgLocalRTT","_resetAvgStats","remoteAvg","avg","validData","rttAvg","AvgRTPStatsReporter","_avgAudioBitrateUp","_avgAudioBitrateDown","_avgVideoBitrateUp","_avgVideoBitrateDown","_avgBandwidthUp","_avgBandwidthDown","_avgPacketLossTotal","_avgPacketLossUp","_avgPacketLossDown","_avgRemoteFPS","_avgRemoteScreenFPS","_avgLocalFPS","_avgLocalScreenFPS","_avgRemoteCameraPixels","_avgRemoteScreenPixels","_avgLocalCameraPixels","_avgLocalScreenPixels","_avgCQ","_cachedTransportStats","_onLocalStatsUpdated","_maybeSendTransportAnalyticsEvent","_onP2PStatusChanged","p2pStatsMonitor","_onJvb121StatusChanged","_resetAvgJvbStats","confSize","_calculateAvgVideoFps","_calculateAvgVideoPixels","peerResolutions","peerPixelsSum","peerCount","myID","peerID","videosResolution","peerAvgPixels","_calculatePeerAvgVideoPixels","videos","peerSsrcCount","peerSsrcPixels","peerFpsSum","videosFps","peerAvgFPS","_calculatePeerAvgVideoFps","peerSsrcFps","transportStats","SpeakerStatsCollector","dominantSpeakerId","SpeakerStats","_onDominantSpeaker","_onUserJoin","_onUserLeave","_onDisplayNameChange","_onFacialExpressionAdd","_updateStats","oldDominantSpeaker","newDominantSpeaker","savedUser","newStats","speakerStatsToUpdate","newParticipant","RecordingResult","wordArray","TrackRecorder","recorder","AUDIO_WEBM","AUDIO_OGG","startRecorder","trackRecorder","stopRecorder","determineCorrectFileType","MediaRecorder","isTypeSupported","AudioRecorder","jitsiConference","recorders","fileType","isRecording","instantiateTrackRecorder","updateNames","originalStream","ondataavailable","dataEvent","recorderToRemove","style","click","revokeObjectURL","getRecordingResults","getFileType","Word","word","begin","getWord","getBeginTime","getEndTime","SphinxService","recordingResult","verify","formatResponse","audioBlob","sphinxURL","toReturn","getURL","audioFileBlob","XMLHttpRequest","onreadystatechange","DONE","responseText","open","setRequestHeader","audioRecorder","objects","filler","BEFORE_STATE","RECORDING_STATE","TRANSCRIBING_STATE","FINISHED_STATE","Transcriber","transcriptionService","transcription","lineLength","blobCallBack","transcriber","getUTCMilliseconds","wordObject","maybeMerge","hasPopulatedArrays","twoDimensionalArray","callBack","merge","arrays","potentialWords","pushWordToSortedArray","lowestWordArray","wordToAdd","updateTranscription","foundSmaller","wordToCompare","getTranscription","ComponentsVersions","addCommandListener","processVersions","mucResource","getComponentVersion","componentName","STATUS_AVAILABLE","STATUS_UNDEFINED","STATUS_BUSY","STATE_ON","STATE_OFF","STATE_PENDING","STATE_RETRYING","STATE_FAILED","ERROR_NO_CONNECTION","ERROR_SESSION_EXISTS","STATE_CHANGED","JitsiVideoSIPGWSession","sipAddress","VideoSIPGWConstants","_sendJibriIQ","setState","failureReason","oldState","addStateListener","removeStateListener","sipaddress","displayname","nodeTree","VideoSIPGW","sessionStateChangeListener","sessionStateChanged","handleJibriSIPState","Constants","failure_reason","createVideoSIPGWSession","SOURCE_INFO_PRESENCE_ELEMENT","SignalingLayerImpl","ssrcOwners","_localSourceState","_remoteSourceState","_sourceNames","_addLocalSourceInfoToPresence","_doesEndpointSendNewSourceInfo","setChatRoom","oldChatRoom","_audioMuteHandler","_videoMuteHandler","_videoTypeHandler","_sourceInfoHandler","_memberLeftHandler","_bindChatRoomEventHandlers","emitAudioMutedEvent","emitVideoMutedEvent","emitVideoTypeEvent","sourceInfoJSON","emitEventsFromHere","endpointSourceState","newMutedState","oldSourceState","newVideoType","newSourceNames","_findEndpointSourceInfoForMediaType","remoteSourceState","legacyGetPeerMediaInfo","lastPresence","mediaInfo","endpointMediaSource","existingOwner","setTrackMuteStatus","setTrackVideoType","existingName","JitsiConference","eventManager","componentsVersions","dtmfManager","somebodySupportsDTMF","wasStopped","avgRtpStatsN","_audioOutputProblemDetector","speakerStatsCollector","deferredStartP2PTask","delay","backToP2PDelay","isP2PConnectionInterrupted","videoSIPGWHandler","recordingManager","_sessions","_chatRoom","getSession","_handleFocusPresence","_handleJibriPresence","startRecording","_addSession","_emitSessionUpdate","stopRecording","_createSession","_conferenceJoinAnalyticsEventSent","isE2EESupported","_e2eEncryption","_audioSenderLimitReached","_videoSenderLimitReached","JitsiConnection","appID","errType","errorType","errorMessage","createConnectionFailedEvent","ANALYTICS_CONNECTION_DISCONNECTED","JitsiMediaDevicesEvents","resourceCreator","isAuthenticatedUser","re","codecSettings","codecSelection","_statsCurrentId","statisticsId","_onIceConnectionInterrupted","_onIceConnectionRestored","_onIceConnectionEstablished","_updateProperties","_sendConferenceJoinAnalyticsEvent","_removeLocalSourceOnReject","_updateRoomPresence","_registerRtcListeners","receiveVideoController","sendVideoController","callStatsThreshold","statisticsDisplayName","callStatsCustomScriptUrl","callStatsConfigParams","enableTalkWhileMuted","_audioAnalyser","vadTalkMutedDetection","enableNoisyMicDetection","vadNoiseDetection","enableNoAudioDetection","_noAudioSignalDetection","hasAudioSignal","jvb121Status","p2pDominantSpeakerDetection","userRegion","transcriptionLanguage","_maybeSetSITimeout","isJoined","isP2PEnabled","isP2PTestModeEnabled","p2pTestMode","onLocalTrackRemoved","_sendConferenceLeftAnalyticsEvent","_delayedIceFailed","_maybeClearSITimeout","leaveError","localtrack","_desktopSharingFrameRate","isAuthEnabled","isLoggedIn","getAuthLogin","getExternalAuthUrl","urlForPopup","getPerformanceStats","longTasksStats","eventId","command","removeCommandListener","sendTextMessage","sendPrivateTextMessage","sendCommand","sendCommandOnce","removeCommand","nickKey","getTranscriber","localAudioTracks","getTranscriptionStatus","addTrackPromises","_setupNewTrack","_fireMuteChangeEvent","_fireAudioLevelChangeEvent","activeTpc","actorId","_getInitialLocalTracks","trackType","isStartAudioMuted","isStartVideoMuted","muteHandler","audioLevelHandler","oldVideoType","oldTrackBelongsToConference","_doReplaceTrack","replaceTrackPromises","_setNewVideoType","videoTypeChanged","videoTypeTagName","trackVideoType","legacyTypeChanged","presenceChanged","audioMuteChanged","videoMuteChanged","addAsUnmutePromises","removeAsMutePromises","lock","unlock","selectParticipant","selectParticipants","isInteger","isVideoActive","countHidden","grantOwner","revokeOwner","isMyself","isMembersOnly","kickParticipant","_sessionInitiateTimeout","muteMediaType","fullJid","_updateFeatures","_maybeStartOrStopP2P","updateDTMFSupport","botParticipant","mediaSessions","tracksToBeRemoved","kickedParticipantId","kickedParticipant","transportInfo","removedTrack","_onIncomingCallP2P","rejectReason","_shouldBeInP2PMode","_rejectIncomingCall","_acceptP2PIncomingCall","_acceptJvbIncomingCall","_suspendMediaTransferForJvbConnection","webSocket","stopOptions","forceJvb121","p2pFailed","_stopP2PSession","isDTMFSupported","peerConnection","startTranscriber","stopTranscriber","getMeetingUniqueId","setStartMutedPolicy","policy","getStartMutedPolicy","removeLocalParticipantProperty","getLocalParticipantProperty","property","overallFeedback","detailedFeedback","getSsrcByTrack","remoteUserId","_fireIncompatibleVersionsEvent","sendEndpointMessage","broadcastEndpointMessage","sendThroughVideobridge","messageType","messageToSend","remoteID","_addRemoteJVBTracks","_addRemoteTracks","_addRemoteP2PTracks","p2pEstablishmentDuration","jvbEstablishmentDuration","forceJVB121Ratio","establishmentDurationDiff","_setP2PStatus","_removeRemoteJVBTracks","audioLimitReached","videoLimitReached","_maybeClearDeferredStartP2P","_removeRemoteTracks","_removeRemoteP2PTracks","sessionNickname","_resumeMediaTransferForJvbConnection","_startP2PSession","userLeftEvent","peers","shouldBeInP2P","myId","peersId","hasBotPeer","wasP2PEstablished","skip","muteStatusChanged","audioMuteStatusChanged","videoMuteStatusChanged","getP2PConnectionState","startP2PSession","stopP2PSession","getSpeakerStats","sendFacialExpression","perf","toggleE2EE","setMediaEncryptionKey","isLobbySupported","disableLobby","joinLobby","myLobbyUserId","sendLobbyMessage","addLobbyMessageListener","removeLobbyMessageHandler","lobbyDenyAccess","lobbyApproveAccess","isAVModerationSupported","enableAVModeration","disableAVModeration","avModerationApprove","avModerationReject","setToken","initJitsiConference","getLogs","PERMISSION_PROMPT_IS_SHOWN","SLOW_GET_USER_MEDIA","AUDIO_PERMISSION_NAME","VIDEO_PERMISSION_NAME","_permissions","_logOutputDevice","permissions","_handlePermissionsChange","_permissionsApiSupported","query","_parsePermissionState","onchange","supported","permissionStatus","deviceID","isDevicePermissionGranted","isMultipleAudioInputSupported","emitEvent","stopActiveDevices","ACTIONS","ProxyConnectionPC","receiveAudio","receiveVideo","_peerConnection","_onError","_onRemoteStream","_onSendMessage","getPeerJid","processMessage","$jingle","_onSessionAccept","_onSessionInitiate","_onSessionTerminate","_onTransportInfo","_createPeerConnection","connectionStub","pcConfigStub","roomStub","jitsiRemoteTrack","onRemoteStream","onSendMessage","PRECALL_TEST_RESULTS","_initialized","api","_loadScript","appId","appSecret","disablePrecalltest","_initialize","execute","makePrecallTest","AudioMixer","_started","_streamsToMix","_streamMSSArray","addMediaStream","_mixedMSD","createMediaStreamDestination","streamMSS","getAnalyticsAttributesFromOptions","video_requested","JitsiMeetJS","ProxyConnectionService","jitsiConnection","_onFatalError","_convertStringToXML","_selfCloseConnection","xml","xmlDom","DOMParser","parseFromString","isVideo","convertVideoToDesktop","jitsiLocalTracks","createLocalTracks","stringifiedIq","XMLSerializer","serializeToString","onConnectionClosed","constants","recording","BUSY","RESOURCE_CONSTRAINT","UNEXPECTED_REQUEST","SERVICE_UNAVAILABLE","FILE","STREAM","sipVideoGW","detection","errors","errorTypes","logLevels","JitsiMediaDevices","enableAnalyticsLogging","enableWindowOnErrorHandler","getGlobalOnErrorHandler","isWebRtcSupported","addGlobalLogTransport","globalTransport","removeGlobalLogTransport","setGlobalLogOptions","oldfirePermissionPromptIsShownEvent","promiseFulfilled","firePermissionPromptIsShownEvent","fireSlowPromiseEvent","restOptions","mStream","currentlyAvailableMediaDevices","setVideoTrackContentHints","createTrackVADEmitter","localAudioDeviceId","createAudioMixer","getActiveAudioDevice","audioDevices","devicePromiseArray","micDevice","devicePromise","outcomeArray","successfulPromises","rejectedPromises","rejectReasons","deviceLabel","isCollectingLocalStats","lineno","colno","setNetworkInfo","hint","contentHint","precallTest","util","AuthUtil","global$1","_typeof","_toConsumableArray","arr2","_arrayWithoutHoles","iter","_iterableToArray","_nonIterableSpread","safe_add","lsw","str2binl","bin","md5_cmn","cnt","md5_ff","md5_gg","md5_hh","md5_ii","core_md5","olda","oldb","oldc","oldd","MD5","hexdigest","binarray","hex_tab","binl2hex","fromCharCode","binl2str","core_sha1","olde","rol","safe_add$1","sha1_ft","sha1_kt","core_hmac_sha1","bkey","str2binb","ipad","opad","binb2b64","triplet","binb2str","SHA1","b64_hmac_sha1","str_hmac_sha1","str_sha1","utils","out","cookies","cookieName","expires","path","cookieObj","isObj","cookieValue","escape","unescape","cookie","Builder","VERSION","NS","HTTPBIND","BOSH","CLIENT","AUTH","ROSTER","PROFILE","DISCO_INFO","DISCO_ITEMS","MUC","SASL","FRAMING","BIND","SESSION","STANZAS","XHTML_IM","XHTML","css","validTag","validAttribute","validCSS","CONNECTING","CONNFAIL","AUTHENTICATING","AUTHFAIL","DISCONNECTED","DISCONNECTING","ATTACHED","REDIRECT","CONNTIMEOUT","BINDREQUIRED","ErrorCondition","BAD_FORMAT","CONFLICT","MISSING_JID_NODE","NO_AUTH_MECH","UNKNOWN_REASON","LogLevel","FATAL","ElementType","NORMAL","TEXT","CDATA","FRAGMENT","SECONDARY_TIMEOUT","addNamespace","forEachChild","elemName","childNodes","childNode","isTagEqual","_xmlGenerator","_makeGenerator","doc","implementation","createDocument","documentMode","_getIEXmlDom","xmlGenerator","docStrings","ActiveXObject","xmlTextNode","xmlescape","xmlunescape","createTextNode","xmlHtmlNode","html","loadXML","getText","nodeValue","copyElement","_i","createHtml","cssText","cssAttrs","cssName","cssValue","_i2","createDocumentFragment","_i3","_i4","escapeNode","unescapeNode","getNodeFromJid","getDomainFromJid","bare","getBareJidFromJid","getResourceFromJid","_handleError","fatal","sourceURL","fileName","lineNumber","getNamedItem","_requestId","_connectionPlugins","ptype","moreattrs","removeAttribute","impNode","xmlGen","importNode","newElem","h","fragment","innerHTML","xhtml","Handler","ns","matchBare","matchBareFromJid","getNamespace","elNamespace","ignoreNamespaceFragment","namespaceMatch","_this","nsMatch","isMatch","elem_type","run","TimedHandler","period","lastCalled","Connection","_this2","Websocket","Bosh","_sasl_data","do_session","do_bind","timedHandlers","removeTimeds","removeHandlers","addTimeds","addHandlers","protocolErrorHandlers","_idleTimeout","_disconnectTimeout","authenticated","do_authentication","restored","_data","_uniqueId","_sasl_success_handler","_sasl_failure_handler","_sasl_challenge_handler","_onIdle","registerSASLMechanisms","mechanisms","F","_reset","_requests","getUniqueId","suffix","addProtocolErrorHandler","status_code","hold","route","authcid","authzid","servtype","connect_callback","wind","_attach","restore","_sessionCachingSupported","_restore","sessionStorage","xmlInput","xmlOutput","nextValidRid","_queueData","_this3","timeoutHandler","deleteTimedHandler","addTimedHandler","_this4","iqtype","_sendRestart","_this5","thand","handRef","hand","SASLAnonymous","SASLExternal","SASLMD5","SASLOAuthBearer","SASLXOAuth2","SASLPlain","SASLSHA1","registerSASLMechanism","_addSysTimedHandler","_onDisconnectTimeout","_disconnect","condition","statusChanged","_dataRecv","_this6","_reqToData","strip","_emptyQueue","cond","conflict","UNKOWN_REASON","_i5","_hand","_connect_cb","_callback","bodyWrap","mech","_no_auth_received","sortMechanismsByPriority","higher","swap","_attemptSASLAuth","_attemptLegacyAuth","mechanism_found","_sasl_success_cb","_sasl_failure_cb","_sasl_challenge_cb","_sasl_mechanism","onStart","request_auth_exchange","isClientFirst","onChallenge","btoa","challenge","atob","_onLegacyAuthIQResult","_auth2_cb","_this7","serverSignature","streamfeature_handlers","wrapper","_onStreamFeaturesAfterSASL","explicitResourceBinding","_onResourceBindResultIQ","jidNode","_establishSession","_onSessionResultIQ","_this8","_i6","_thand","SASLMechanism","auth_str","test_cnonce","cnonce","nonce","Hi","U","U_old","authMessage","attribMatch","clientKey","serverKey","clientSignature","_quote","realm","host","digest_uri","cred","A1","A2","core","Strophe$1","$build$1","Request","sends","xmlData","origFunc","date","NaN","abort","dead","age","timeDead","xhr","_newXHR","getResponse","responseXML","documentElement","overrideMimeType","_conn","inactivity","keepalive","_cacheSession","_onRequestStateChange","_throttledRequestHandler","typ","_sendTerminate","_callProtocolErrorHandlers","reqStatus","_getRequestStatus","err_callback","HTTP","_hitError","time_elapsed","def","getAllResponseHeaders","valid_request","too_many_retries","_removeRequest","reqIs0","_restartRequest","_processRequest","primary_timeout","secondary_timeout","server_error","content_type","contentType","withCredentials","e2","sendFunc","customHeaders","backoff","Strophe$2","$build$2","new_service","pathname","_buildStream","_check_streamerror","connectstatus","errorString","_onOpen","_connect_cb_wrapper","_handleStreamStart","streamStart","parsedMessage","see_uri","_streamWrap","CLOSED","closeString","rawStanza","firstChild","startString","strophe_js","_items","conn","_onDiscoInfo","_onDiscoItems","addIdentity","lang","var_name","addItem","call_back","items","_buildIQResult","query_attrs","iqresult","logging","autoSendCountOnEveryIncomingStanza","requestResponseInterval","_c","_NS","_isStreamManagementEnabled","_serverProcesssedStanzasCounter","_clientProcessedStanzasCounter","_clientSentStanzasCounter","_originalXMLOutput","_requestHandler","_incomingHandler","_requestResponseIntervalCount","_isSupported","_unacknowledgedStanzas","_acknowledgedStanzaListeners","addAcknowledgedStanzaListener","_resumeToken","_resuming","_originalConnect","_connectArgs","requestAcknowledgement","getOutgoingCounter","getIncomingCounter","_interceptConnectArgs","_originalOnStreamFeaturesAfterSASL","_originalDoDisconnect","_interceptDoDisconnect","_originalDisconnect","_interceptDisconnect","_resumeState","_storedJid","previd","_handleServerRequestHandler","_ackHandler","_handleServerAck","_incomingStanzaHandler","_enabledHandler","_handleEnabled","_resumeFailedHandler","_handleResumeFailed","_resumedHandler","_handleResumed","_increaseSentStanzasCounter","firstElementChild","handledCount","_handleAcknowledgedStanzas","_increaseReceivedStanzasCounter","_answerProcessedStanzas","reportedHandledCount","lastKnownHandledCount","delta","_throwError","byteToHex","buf","bth","_v","_v2","_v3","_v4","_interopRequireDefault","getOutputLength","inputLength8","safeAdd","md5cmn","md5ff","md5gg","md5hh","md5ii","encodeURIComponent","length32","hexTab","md5ToHexEncodedArray","wordsToMd5","length8","bytesToWords","rnds8","msCrypto","ROTL","K","H","N","ceil","M","W","T","_rng","_bytesToUuid","_nodeId","_clockseq","_lastMSecs","_lastNSecs","clockseq","seedBytes","rng","msecs","nsecs","dt","tl","tmh","_md","_default","hashfunc","generateUUID","namespace","stringToBytes","uuidToBytes","DNS","rnds","_sha","logDisabled_","deprecationWarnings_","extractVersion","uastring","wrapPeerConnectionEvent","eventNameToWrap","nativeAddEventListener","nativeEventName","modifiedEvent","handleEvent","_eventMap","nativeRemoveEventListener","unwrappedCb","disableLog","bool","disableWarnings","deprecated","oldMethod","newMethod","val","compactObject","isEmptyObject","walkStats","resultSet","filterStats","outbound","streamStatsType","filteredResult","trackStats","trackStat","shimGetUserMedia","browserDetails","constraintsToChrome_","cc","exact","oldname_","oc","mix","advanced","shimConstraints_","remap","face","getSupportedFacingModeLies","getSupportedConstraints","dev","shimError_","PermissionDeniedError","PermissionDismissedError","InvalidStateError","DevicesNotFoundError","ConstraintNotSatisfiedError","TrackStartError","MediaDeviceFailedDueToShutdown","MediaDeviceKillSwitchOn","TabCaptureError","ScreenCaptureError","DeviceCaptureError","webkitGetUserMedia","origGetUserMedia","DOMException","shimGetDisplayMedia","getSourceId","widthSpecified","heightSpecified","frameRateSpecified","shimMediaStream","webkitMediaStream","shimOnTrack","_ontrack","origSetRemoteDescription","_ontrackpoly","te","Event","dispatchEvent","shimGetSendersWithDtmf","shimSenderWithDtmf","_dtmf","_pc","_senders","origAddTrack","origRemoveTrack","origAddStream","origRemoveStream","origGetSenders","shimGetStats","origGetStats","selector","onSucc","onErr","fixChromeStats_","standardReport","standardStats","localcandidate","remotecandidate","makeMapStats","successCallbackWrapper_","shimSenderReceiverGetStats","origGetReceivers","srcElement","MediaStreamTrack","shimAddTrackRemoveTrackWithNative","getLocalStreams","_shimmedLocalStreams","existingSenders","newSenders","newSender","shimAddTrackRemoveTrack","origGetLocalStreams","nativeStreams","_reverseStreams","_streams","newStream","replaceInternalStreamId","internalId","externalStream","internalStream","replaceExternalStreamId","alreadyExists","oldStream","nativeMethod","methodObj","origSetLocalDescription","origLocalDescription","getOwnPropertyDescriptor","streamid","shimPeerConnection","webkitRTCPeerConnection","fixNegotiationNeeded","getConfiguration","nativeGetUserMedia","nativeGetSettings","applyConstraints","nativeApplyConstraints","preferredMediaSource","mediaSource","RTCTrackEvent","mozRTCPeerConnection","modernStatsTypes","inboundrtp","outboundrtp","candidatepair","nativeGetStats","shimSenderGetStats","shimReceiverGetStats","shimRemoveStream","shimRTCDataChannel","DataChannel","RTCDataChannel","shimAddTransceiver","origAddTransceiver","setParametersPromises","initParameters","shouldPerformCheck","encodingParam","maxFramerate","shimGetParameters","origGetParameters","shimCreateOffer","origCreateOffer","finally","shimCreateAnswer","origCreateAnswer","shimLocalStreamsAPI","_localStreams","_addTrack","shimRemoteStreamsAPI","getRemoteStreams","_remoteStreams","_onaddstream","_onaddstreampoly","shimCallbacksAPI","withCallback","shimConstraints","errcb","shimRTCIceServerUrls","OrigPeerConnection","newIceServers","generateCertificate","shimTrackEventTransceiver","shimCreateOfferLegacy","offerOptions","audioTransceiver","setDirection","videoTransceiver","shimAudioContext","shimRTCIceCandidate","NativeRTCIceCandidate","nativeCandidate","parsedCandidate","augmentedCandidate","toJSON","shimMaxMessageSize","_sctp","sctpInDescription","getRemoteFirefoxVersion","getCanSendMaxMessageSize","remoteIsFirefox","canSendMaxMessageSize","getMaxMessageSize","canSendMMS","remoteMMS","POSITIVE_INFINITY","shimSendThrowTypeError","wrapDcSend","dc","origDataChannelSend","origCreateDataChannel","dataChannel","shimConnectionState","checking","_onconnectionstatechange","origMethod","_connectionstatechangepoly","_lastConnectionState","newEvent","removeExtmapAllowMixed","nativeSRD","shimAddIceCandidateNullOrEmpty","nativeAddIceCandidate","adapter","shimChrome","shimFirefox","shimSafari","mozGetUserMedia","isSecureContext","RTCIceGatherer","commonShim","browserShim","adapterFactory","__webpack_module_cache__","__webpack_require__","moduleId","cachedModule","loaded","__webpack_modules__","amdO","getter","definition","globalThis","nmd","paths","__webpack_exports__"],"sourceRoot":""}