@100mslive/roomkit-react 0.1.3 → 0.1.4-alpha.0
Sign up to get free protection for your applications and to get access to all the features.
- package/dist/{ActiveSpeakerView-KIIG3P3H.css → ActiveSpeakerView-AGL73O6U.css} +2 -2
- package/dist/{PinnedTrackView-IKIDDCHG.css.map → ActiveSpeakerView-AGL73O6U.css.map} +2 -2
- package/dist/{ActiveSpeakerView-CLXH5CT7.js → ActiveSpeakerView-UPFD5RXA.js} +6 -6
- package/dist/{HLSView-2BZD2FSZ.css → HLSView-64OG755F.css} +2 -2
- package/dist/{HLSView-2BZD2FSZ.css.map → HLSView-64OG755F.css.map} +2 -2
- package/dist/{HLSView-RIXB2GY3.js → HLSView-T267ZHOS.js} +5 -5
- package/dist/{PinnedTrackView-ZGNZXO4J.js → PinnedTrackView-O4FHHHOV.js} +6 -6
- package/dist/{PinnedTrackView-IKIDDCHG.css → PinnedTrackView-YWAZ2342.css} +2 -2
- package/dist/{conference-IQN7SXQI.css.map → PinnedTrackView-YWAZ2342.css.map} +2 -2
- package/dist/{VirtualBackground-PPX7DVS3.js → VirtualBackground-BCKXNDTD.js} +3 -3
- package/dist/{chunk-CIV5V5QF.js → chunk-3HMUOAD6.js} +7 -7
- package/dist/chunk-3HMUOAD6.js.map +7 -0
- package/dist/{chunk-GEPWMIT4.js → chunk-6GXDYWD5.js} +2 -2
- package/dist/{chunk-LMSP5ETL.js → chunk-NLZVUXR3.js} +6 -6
- package/dist/{chunk-E7WZYH2U.js → chunk-ORPC2GYB.js} +2 -2
- package/dist/{chunk-G24GH7QG.js → chunk-YE4RPJYG.js} +2 -2
- package/dist/{chunk-G24GH7QG.js.map → chunk-YE4RPJYG.js.map} +1 -1
- package/dist/{conference-IQN7SXQI.css → conference-7KHWJZLG.css} +2 -2
- package/dist/{ActiveSpeakerView-KIIG3P3H.css.map → conference-7KHWJZLG.css.map} +2 -2
- package/dist/{conference-ORQKXGY3.js → conference-ETISNCLN.js} +11 -11
- package/dist/index.cjs.css +1 -1
- package/dist/index.cjs.css.map +2 -2
- package/dist/index.cjs.js +5 -3
- package/dist/index.cjs.js.map +2 -2
- package/dist/index.css +1 -1
- package/dist/index.css.map +2 -2
- package/dist/index.js +4 -4
- package/dist/meta.cjs.json +10 -10
- package/dist/meta.esbuild.json +93 -93
- package/dist/{transcription-ETHBK5TS.js → transcription-JJQ4UAIK.js} +3 -3
- package/package.json +9 -9
- package/dist/chunk-CIV5V5QF.js.map +0 -7
- /package/dist/{ActiveSpeakerView-CLXH5CT7.js.map → ActiveSpeakerView-UPFD5RXA.js.map} +0 -0
- /package/dist/{HLSView-RIXB2GY3.js.map → HLSView-T267ZHOS.js.map} +0 -0
- /package/dist/{PinnedTrackView-ZGNZXO4J.js.map → PinnedTrackView-O4FHHHOV.js.map} +0 -0
- /package/dist/{VirtualBackground-PPX7DVS3.js.map → VirtualBackground-BCKXNDTD.js.map} +0 -0
- /package/dist/{chunk-GEPWMIT4.js.map → chunk-6GXDYWD5.js.map} +0 -0
- /package/dist/{chunk-LMSP5ETL.js.map → chunk-NLZVUXR3.js.map} +0 -0
- /package/dist/{chunk-E7WZYH2U.js.map → chunk-ORPC2GYB.js.map} +0 -0
- /package/dist/{conference-ORQKXGY3.js.map → conference-ETISNCLN.js.map} +0 -0
- /package/dist/{transcription-ETHBK5TS.js.map → transcription-JJQ4UAIK.js.map} +0 -0
@@ -1,7 +0,0 @@
|
|
1
|
-
{
|
2
|
-
"version": 3,
|
3
|
-
"sources": ["../../../node_modules/ua-parser-js/src/ua-parser.js", "../../../node_modules/sdp/sdp.js", "../../../node_modules/sdp-transform/lib/grammar.js", "../../../node_modules/sdp-transform/lib/parser.js", "../../../node_modules/sdp-transform/lib/writer.js", "../../../node_modules/sdp-transform/lib/index.js", "../src/Prebuilt/components/VideoTile.jsx", "../src/Prebuilt/components/peerTileUtils.jsx", "../src/Prebuilt/components/TileMenu.jsx", "../src/Prebuilt/components/AppData/useAppConfig.js", "../src/Prebuilt/components/gridView.jsx", "../src/Prebuilt/components/FirstPersonDisplay.jsx", "../src/Prebuilt/components/Image.jsx", "../src/Prebuilt/components/VideoList.jsx", "../src/Prebuilt/components/Pagination.jsx", "../src/Prebuilt/components/ScreenshareTile.jsx", "../src/Prebuilt/common/useSortedPeers.js", "../src/Prebuilt/common/PeersSorter.js", "../../../node_modules/reselect/es/index.js", "../../hms-video-web/package.json", "../../hms-video-web/src/sdk/models/HMSMessage.ts", "../../hms-video-web/src/sdk/models/HMSRoom.ts", "../../hms-video-web/src/sdk/models/peer/HMSPeer.ts", "../../hms-video-web/src/utils/id-factory.ts", "../../hms-video-web/src/sdk/models/peer/HMSLocalPeer.ts", "../../hms-video-web/src/sdk/models/peer/HMSRemotePeer.ts", "../../hms-video-web/src/sdk/LocalTrackManager.ts", "../../hms-video-web/src/analytics/AnalyticsEvent.ts", "../../hms-video-web/src/utils/support.ts", "../../hms-video-web/src/analytics/domain-analytics.ts", "../../hms-video-web/src/utils/analytics-deviceId.ts", "../../hms-video-web/src/utils/local-storage-polyfill.ts", "../../hms-video-web/src/utils/local-storage.ts", "../../hms-video-web/src/utils/logger.ts", "../../hms-video-web/src/error/ErrorCodes.ts", "../../hms-video-web/src/error/HMSException.ts", "../../hms-video-web/src/signal/jsonrpc/models.ts", "../../hms-video-web/src/error/ErrorFactory.ts", "../../hms-video-web/src/utils/validations.ts", "../../hms-video-web/src/utils/user-agent.ts", "../../hms-video-web/src/analytics/AnalyticsEventFactory.ts", "../../hms-video-web/src/analytics/AnalyticsTimer.ts", "../../hms-video-web/src/error/utils.ts", "../../hms-video-web/src/internal.ts", "../../hms-video-web/src/media/streams/HMSMediaStream.ts", "../../hms-video-web/src/utils/json.ts", "../../hms-video-web/src/media/tracks/HMSTrack.ts", "../../hms-video-web/src/media/tracks/HMSTrackType.ts", "../../hms-video-web/src/media/tracks/HMSAudioTrack.ts", "../../hms-video-web/src/device-manager/DeviceStorage.ts", "../../hms-video-web/src/plugins/audio/HMSAudioPlugin.ts", "../../hms-video-web/src/analytics/MediaPluginsAnalyticsFactory.ts", "../../hms-video-web/src/plugins/audio/AudioPluginsAnalytics.ts", "../../hms-video-web/src/plugins/audio/HMSAudioPluginsManager.ts", "../../hms-video-web/src/utils/track.ts", "../../hms-video-web/src/utils/media.ts", "../../hms-video-web/src/utils/queue.ts", "../../hms-video-web/src/utils/timer-utils.ts", "../../hms-video-web/src/utils/track-audio-level-monitor.ts", "../../hms-video-web/src/interfaces/update-listener.ts", "../../hms-video-web/src/interfaces/simulcast-layers.ts", "../../hms-video-web/src/interfaces/track-settings.ts", "../../hms-video-web/src/interfaces/devices.ts", "../../hms-video-web/src/interfaces/playlist.ts", "../../hms-video-web/src/interfaces/session-store/polls.ts", "../../hms-video-web/src/media/settings/HMSAudioTrackSettings.ts", "../../hms-video-web/src/media/settings/HMSVideoTrackSettings.ts", "../../hms-video-web/src/media/settings/HMSTrackSettings.ts", "../../hms-video-web/src/media/tracks/HMSLocalAudioTrack.ts", "../../hms-video-web/src/media/tracks/HMSRemoteAudioTrack.ts", "../../hms-video-web/src/media/tracks/HMSVideoTrack.ts", "../../hms-video-web/src/media/tracks/VideoElementManager.ts", "../../hms-video-web/src/media/tracks/trackUtils.ts", "../../hms-video-web/src/utils/intersection-observer.ts", "../../hms-video-web/src/utils/resize-observer.ts", "../../hms-video-web/src/plugins/video/HMSVideoPlugin.ts", "../../hms-video-web/src/utils/math.ts", "../../hms-video-web/src/plugins/video/VideoPluginsAnalytics.ts", "../../hms-video-web/src/plugins/video/HMSVideoPluginsManager.ts", "../../hms-video-web/src/media/tracks/HMSLocalVideoTrack.ts", "../../hms-video-web/src/utils/constants.ts", "../../hms-video-web/src/media/tracks/HMSRemoteVideoTrack.ts", "../../hms-video-web/src/media/streams/HMSLocalStream.ts", "../../hms-video-web/src/media/streams/HMSRemoteStream.ts", "../../hms-video-web/src/utils/device-error.ts", "../../hms-video-web/src/rtc-stats/utils.ts", "../../hms-video-web/src/rtc-stats/HMSWebrtcStats.ts", "../../hms-video-web/src/rtc-stats/HMSWebrtcInternals.ts", "../../hms-video-web/src/sdk/NetworkTestManager.ts", "../../hms-video-web/src/sdk/RoleChangeManager.ts", "../../hms-video-web/src/analytics/HTTPAnalyticsTransport.ts", "../../hms-video-web/src/sdk/store/Store.ts", "../../hms-video-web/src/sdk/WakeLockManager.ts", "../../hms-video-web/src/analytics/AnalyticsEventsService.ts", "../../hms-video-web/src/audio-sink-manager/AudioSinkManager.ts", "../../hms-video-web/src/device-manager/DeviceManager.ts", "../../hms-video-web/src/device-manager/AudioOutputManager.ts", "../../hms-video-web/src/events/EventBus.ts", "../../hms-video-web/src/events/HMSInternalEvent.ts", "../../hms-video-web/src/notification-manager/HMSNotifications.ts", "../../hms-video-web/src/notification-manager/managers/ActiveSpeakerManager.ts", "../../hms-video-web/src/notification-manager/managers/BroadcastManager.ts", "../../hms-video-web/src/notification-manager/managers/ConnectionQualityManager.ts", "../../hms-video-web/src/notification-manager/managers/TrackManager.ts", "../../hms-video-web/src/notification-manager/managers/onDemandTrackManager.ts", "../../hms-video-web/src/notification-manager/managers/PeerListManager.ts", "../../hms-video-web/src/utils/date.ts", "../../hms-video-web/src/notification-manager/managers/PeerManager.ts", "../../hms-video-web/src/notification-manager/managers/PolicyChangeManager.ts", "../../hms-video-web/src/notification-manager/managers/PollsManager.ts", "../../hms-video-web/src/notification-manager/managers/RequestManager.ts", "../../hms-video-web/src/notification-manager/managers/RoomUpdateManager.ts", "../../hms-video-web/src/notification-manager/managers/SessionMetadataManager.ts", "../../hms-video-web/src/notification-manager/NotificationManager.ts", "../../hms-video-web/src/playlist-manager/AudioContextManager.ts", "../../hms-video-web/src/utils/typed-event-emitter.ts", "../../hms-video-web/src/playlist-manager/PlaylistAudioManager.ts", "../../hms-video-web/src/playlist-manager/PlaylistVideoManager.ts", "../../hms-video-web/src/playlist-manager/PlaylistManager.ts", "../../hms-video-web/src/session-store/index.ts", "../../hms-video-web/src/session-store/interactivity-center/HMSInteractivityCenter.ts", "../../hms-video-web/src/transport/models/JoinParameters.ts", "../../hms-video-web/src/transport/models/TransportFailureCategory.ts", "../../hms-video-web/src/transport/models/TransportState.ts", "../../hms-video-web/src/utils/promise.ts", "../../hms-video-web/src/transport/RetryScheduler.ts", "../../hms-video-web/src/analytics/publish-stats/index.ts", "../../hms-video-web/src/analytics/signal-transport/LocalStoageEvents.ts", "../../hms-video-web/src/analytics/AnalyticsTransport.ts", "../../hms-video-web/src/analytics/signal-transport/SignalAnalyticsTransport.ts", "../../hms-video-web/src/connection/model.ts", "../../hms-video-web/src/utils/session-description.ts", "../../hms-video-web/src/connection/HMSConnection.ts", "../../hms-video-web/src/connection/publish/publishConnection.ts", "../../hms-video-web/src/connection/subscribe/subscribeConnection.ts", "../../hms-video-web/src/connection/HMSDataChannel.ts", "../../hms-video-web/src/signal/init/index.ts", "../../hms-video-web/src/signal/jsonrpc/index.ts", "../../hms-video-web/src/utils/network-info.ts", "../../hms-video-web/src/transport/index.ts", "../../hms-video-web/src/utils/fetch.ts", "../../hms-video-web/src/utils/jwt.ts", "../../hms-video-web/src/sdk/index.ts", "../../../node_modules/webrtc-adapter/src/js/adapter_core.js", "../../../node_modules/webrtc-adapter/src/js/adapter_factory.js", "../../../node_modules/webrtc-adapter/src/js/utils.js", "../../../node_modules/webrtc-adapter/src/js/chrome/chrome_shim.js", "../../../node_modules/webrtc-adapter/src/js/chrome/getusermedia.js", "../../../node_modules/webrtc-adapter/src/js/chrome/getdisplaymedia.js", "../../../node_modules/webrtc-adapter/src/js/firefox/firefox_shim.js", "../../../node_modules/webrtc-adapter/src/js/firefox/getusermedia.js", "../../../node_modules/webrtc-adapter/src/js/firefox/getdisplaymedia.js", "../../../node_modules/webrtc-adapter/src/js/safari/safari_shim.js", "../../../node_modules/webrtc-adapter/src/js/common_shim.js", "../../hms-video-store/package.json", "../../hms-video-store/src/core/schema/room.ts", "../../hms-video-store/src/core/schema/schema.ts", "../../hms-video-store/src/core/schema/message.ts", "../../hms-video-store/src/core/schema/notification.ts", "../../hms-video-store/src/core/schema/playlist.ts", "../../hms-video-store/src/core/selectors/selectors.ts", "../../hms-video-store/src/core/selectors/selectorUtils.ts", "../../hms-video-store/src/core/selectors/playlistselectors.ts", "../../hms-video-store/src/core/selectors/selectorsByID.ts", "../../hms-video-store/src/core/selectors/common.ts", "../../hms-video-store/src/core/hmsSDKStore/sdkTypes.ts", "../../hms-video-store/src/common/ui-logger.ts", "../../hms-video-store/src/core/selectors/derivedSelectors.ts", "../../hms-video-store/src/core/selectors/selectorsByReference.ts", "../../hms-video-store/src/core/hmsSDKStore/sdkUtils/storeMergeUtils.ts", "../../hms-video-store/src/core/webrtc-stats/webrtc-stats-store.ts", "../../hms-video-store/src/common/storeName.ts", "../../hms-video-store/src/core/hmsSDKStore/HMSReactiveStore.ts", "../../hms-video-store/src/core/hmsSDKStore/HMSNotifications.ts", "../../hms-video-store/src/core/hmsSDKStore/common/mapping.ts", "../../hms-video-store/src/core/hmsSDKStore/HMSSDKActions.ts", "../../hms-video-store/src/core/hmsSDKStore/sdkUtils/sdkUtils.ts", "../../hms-video-store/src/core/hmsSDKStore/adapter.ts", "../../hms-video-store/src/core/hmsSDKStore/HMSInteractivityCenter.ts", "../../hms-video-store/src/core/hmsSDKStore/HMSPlaylist.ts", "../../hms-video-store/src/core/hmsSDKStore/HMSSessionStore.ts", "../../hms-video-store/src/controller/beam/BeamSpeakerLabelsLogger.ts", "../../hms-video-store/src/core/webrtc-stats/HMSStats.ts", "../../hms-video-store/src/core/webrtc-stats/selectors.ts"],
|
4
|
-
"sourcesContent": ["/////////////////////////////////////////////////////////////////////////////////\n/* UAParser.js v1.0.35\n Copyright \u00A9 2012-2021 Faisal Salman <f@faisalman.com>\n MIT License *//*\n Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent data.\n Supports browser & node.js environment. \n Demo : https://faisalman.github.io/ua-parser-js\n Source : https://github.com/faisalman/ua-parser-js */\n/////////////////////////////////////////////////////////////////////////////////\n\n(function (window, undefined) {\n\n 'use strict';\n\n //////////////\n // Constants\n /////////////\n\n\n var LIBVERSION = '1.0.35',\n EMPTY = '',\n UNKNOWN = '?',\n FUNC_TYPE = 'function',\n UNDEF_TYPE = 'undefined',\n OBJ_TYPE = 'object',\n STR_TYPE = 'string',\n MAJOR = 'major',\n MODEL = 'model',\n NAME = 'name',\n TYPE = 'type',\n VENDOR = 'vendor',\n VERSION = 'version',\n ARCHITECTURE= 'architecture',\n CONSOLE = 'console',\n MOBILE = 'mobile',\n TABLET = 'tablet',\n SMARTTV = 'smarttv',\n WEARABLE = 'wearable',\n EMBEDDED = 'embedded',\n UA_MAX_LENGTH = 350;\n\n var AMAZON = 'Amazon',\n APPLE = 'Apple',\n ASUS = 'ASUS',\n BLACKBERRY = 'BlackBerry',\n BROWSER = 'Browser',\n CHROME = 'Chrome',\n EDGE = 'Edge',\n FIREFOX = 'Firefox',\n GOOGLE = 'Google',\n HUAWEI = 'Huawei',\n LG = 'LG',\n MICROSOFT = 'Microsoft',\n MOTOROLA = 'Motorola',\n OPERA = 'Opera',\n SAMSUNG = 'Samsung',\n SHARP = 'Sharp',\n SONY = 'Sony',\n VIERA = 'Viera',\n XIAOMI = 'Xiaomi',\n ZEBRA = 'Zebra',\n FACEBOOK = 'Facebook',\n CHROMIUM_OS = 'Chromium OS',\n MAC_OS = 'Mac OS';\n\n ///////////\n // Helper\n //////////\n\n var extend = function (regexes, extensions) {\n var mergedRegexes = {};\n for (var i in regexes) {\n if (extensions[i] && extensions[i].length % 2 === 0) {\n mergedRegexes[i] = extensions[i].concat(regexes[i]);\n } else {\n mergedRegexes[i] = regexes[i];\n }\n }\n return mergedRegexes;\n },\n enumerize = function (arr) {\n var enums = {};\n for (var i=0; i<arr.length; i++) {\n enums[arr[i].toUpperCase()] = arr[i];\n }\n return enums;\n },\n has = function (str1, str2) {\n return typeof str1 === STR_TYPE ? lowerize(str2).indexOf(lowerize(str1)) !== -1 : false;\n },\n lowerize = function (str) {\n return str.toLowerCase();\n },\n majorize = function (version) {\n return typeof(version) === STR_TYPE ? version.replace(/[^\\d\\.]/g, EMPTY).split('.')[0] : undefined;\n },\n trim = function (str, len) {\n if (typeof(str) === STR_TYPE) {\n str = str.replace(/^\\s\\s*/, EMPTY);\n return typeof(len) === UNDEF_TYPE ? str : str.substring(0, UA_MAX_LENGTH);\n }\n };\n\n ///////////////\n // Map helper\n //////////////\n\n var rgxMapper = function (ua, arrays) {\n\n var i = 0, j, k, p, q, matches, match;\n\n // loop through all regexes maps\n while (i < arrays.length && !matches) {\n\n var regex = arrays[i], // even sequence (0,2,4,..)\n props = arrays[i + 1]; // odd sequence (1,3,5,..)\n j = k = 0;\n\n // try matching uastring with regexes\n while (j < regex.length && !matches) {\n\n if (!regex[j]) { break; }\n matches = regex[j++].exec(ua);\n\n if (!!matches) {\n for (p = 0; p < props.length; p++) {\n match = matches[++k];\n q = props[p];\n // check if given property is actually array\n if (typeof q === OBJ_TYPE && q.length > 0) {\n if (q.length === 2) {\n if (typeof q[1] == FUNC_TYPE) {\n // assign modified match\n this[q[0]] = q[1].call(this, match);\n } else {\n // assign given value, ignore regex match\n this[q[0]] = q[1];\n }\n } else if (q.length === 3) {\n // check whether function or regex\n if (typeof q[1] === FUNC_TYPE && !(q[1].exec && q[1].test)) {\n // call function (usually string mapper)\n this[q[0]] = match ? q[1].call(this, match, q[2]) : undefined;\n } else {\n // sanitize match using given regex\n this[q[0]] = match ? match.replace(q[1], q[2]) : undefined;\n }\n } else if (q.length === 4) {\n this[q[0]] = match ? q[3].call(this, match.replace(q[1], q[2])) : undefined;\n }\n } else {\n this[q] = match ? match : undefined;\n }\n }\n }\n }\n i += 2;\n }\n },\n\n strMapper = function (str, map) {\n\n for (var i in map) {\n // check if current value is array\n if (typeof map[i] === OBJ_TYPE && map[i].length > 0) {\n for (var j = 0; j < map[i].length; j++) {\n if (has(map[i][j], str)) {\n return (i === UNKNOWN) ? undefined : i;\n }\n }\n } else if (has(map[i], str)) {\n return (i === UNKNOWN) ? undefined : i;\n }\n }\n return str;\n };\n\n ///////////////\n // String map\n //////////////\n\n // Safari < 3.0\n var oldSafariMap = {\n '1.0' : '/8',\n '1.2' : '/1',\n '1.3' : '/3',\n '2.0' : '/412',\n '2.0.2' : '/416',\n '2.0.3' : '/417',\n '2.0.4' : '/419',\n '?' : '/'\n },\n windowsVersionMap = {\n 'ME' : '4.90',\n 'NT 3.11' : 'NT3.51',\n 'NT 4.0' : 'NT4.0',\n '2000' : 'NT 5.0',\n 'XP' : ['NT 5.1', 'NT 5.2'],\n 'Vista' : 'NT 6.0',\n '7' : 'NT 6.1',\n '8' : 'NT 6.2',\n '8.1' : 'NT 6.3',\n '10' : ['NT 6.4', 'NT 10.0'],\n 'RT' : 'ARM'\n };\n\n //////////////\n // Regex map\n /////////////\n\n var regexes = {\n\n browser : [[\n\n /\\b(?:crmo|crios)\\/([\\w\\.]+)/i // Chrome for Android/iOS\n ], [VERSION, [NAME, 'Chrome']], [\n /edg(?:e|ios|a)?\\/([\\w\\.]+)/i // Microsoft Edge\n ], [VERSION, [NAME, 'Edge']], [\n\n // Presto based\n /(opera mini)\\/([-\\w\\.]+)/i, // Opera Mini\n /(opera [mobiletab]{3,6})\\b.+version\\/([-\\w\\.]+)/i, // Opera Mobi/Tablet\n /(opera)(?:.+version\\/|[\\/ ]+)([\\w\\.]+)/i // Opera\n ], [NAME, VERSION], [\n /opios[\\/ ]+([\\w\\.]+)/i // Opera mini on iphone >= 8.0\n ], [VERSION, [NAME, OPERA+' Mini']], [\n /\\bopr\\/([\\w\\.]+)/i // Opera Webkit\n ], [VERSION, [NAME, OPERA]], [\n\n // Mixed\n /(kindle)\\/([\\w\\.]+)/i, // Kindle\n /(lunascape|maxthon|netfront|jasmine|blazer)[\\/ ]?([\\w\\.]*)/i, // Lunascape/Maxthon/Netfront/Jasmine/Blazer\n // Trident based\n /(avant |iemobile|slim)(?:browser)?[\\/ ]?([\\w\\.]*)/i, // Avant/IEMobile/SlimBrowser\n /(ba?idubrowser)[\\/ ]?([\\w\\.]+)/i, // Baidu Browser\n /(?:ms|\\()(ie) ([\\w\\.]+)/i, // Internet Explorer\n\n // Webkit/KHTML based // Flock/RockMelt/Midori/Epiphany/Silk/Skyfire/Bolt/Iron/Iridium/PhantomJS/Bowser/QupZilla/Falkon\n /(flock|rockmelt|midori|epiphany|silk|skyfire|bolt|iron|vivaldi|iridium|phantomjs|bowser|quark|qupzilla|falkon|rekonq|puffin|brave|whale(?!.+naver)|qqbrowserlite|qq|duckduckgo)\\/([-\\w\\.]+)/i,\n // Rekonq/Puffin/Brave/Whale/QQBrowserLite/QQ, aka ShouQ\n /(heytap|ovi)browser\\/([\\d\\.]+)/i, // Heytap/Ovi\n /(weibo)__([\\d\\.]+)/i // Weibo\n ], [NAME, VERSION], [\n /(?:\\buc? ?browser|(?:juc.+)ucweb)[\\/ ]?([\\w\\.]+)/i // UCBrowser\n ], [VERSION, [NAME, 'UC'+BROWSER]], [\n /microm.+\\bqbcore\\/([\\w\\.]+)/i, // WeChat Desktop for Windows Built-in Browser\n /\\bqbcore\\/([\\w\\.]+).+microm/i\n ], [VERSION, [NAME, 'WeChat(Win) Desktop']], [\n /micromessenger\\/([\\w\\.]+)/i // WeChat\n ], [VERSION, [NAME, 'WeChat']], [\n /konqueror\\/([\\w\\.]+)/i // Konqueror\n ], [VERSION, [NAME, 'Konqueror']], [\n /trident.+rv[: ]([\\w\\.]{1,9})\\b.+like gecko/i // IE11\n ], [VERSION, [NAME, 'IE']], [\n /ya(?:search)?browser\\/([\\w\\.]+)/i // Yandex\n ], [VERSION, [NAME, 'Yandex']], [\n /(avast|avg)\\/([\\w\\.]+)/i // Avast/AVG Secure Browser\n ], [[NAME, /(.+)/, '$1 Secure '+BROWSER], VERSION], [\n /\\bfocus\\/([\\w\\.]+)/i // Firefox Focus\n ], [VERSION, [NAME, FIREFOX+' Focus']], [\n /\\bopt\\/([\\w\\.]+)/i // Opera Touch\n ], [VERSION, [NAME, OPERA+' Touch']], [\n /coc_coc\\w+\\/([\\w\\.]+)/i // Coc Coc Browser\n ], [VERSION, [NAME, 'Coc Coc']], [\n /dolfin\\/([\\w\\.]+)/i // Dolphin\n ], [VERSION, [NAME, 'Dolphin']], [\n /coast\\/([\\w\\.]+)/i // Opera Coast\n ], [VERSION, [NAME, OPERA+' Coast']], [\n /miuibrowser\\/([\\w\\.]+)/i // MIUI Browser\n ], [VERSION, [NAME, 'MIUI '+BROWSER]], [\n /fxios\\/([-\\w\\.]+)/i // Firefox for iOS\n ], [VERSION, [NAME, FIREFOX]], [\n /\\bqihu|(qi?ho?o?|360)browser/i // 360\n ], [[NAME, '360 '+BROWSER]], [\n /(oculus|samsung|sailfish|huawei)browser\\/([\\w\\.]+)/i\n ], [[NAME, /(.+)/, '$1 '+BROWSER], VERSION], [ // Oculus/Samsung/Sailfish/Huawei Browser\n /(comodo_dragon)\\/([\\w\\.]+)/i // Comodo Dragon\n ], [[NAME, /_/g, ' '], VERSION], [\n /(electron)\\/([\\w\\.]+) safari/i, // Electron-based App\n /(tesla)(?: qtcarbrowser|\\/(20\\d\\d\\.[-\\w\\.]+))/i, // Tesla\n /m?(qqbrowser|baiduboxapp|2345Explorer)[\\/ ]?([\\w\\.]+)/i // QQBrowser/Baidu App/2345 Browser\n ], [NAME, VERSION], [\n /(metasr)[\\/ ]?([\\w\\.]+)/i, // SouGouBrowser\n /(lbbrowser)/i, // LieBao Browser\n /\\[(linkedin)app\\]/i // LinkedIn App for iOS & Android\n ], [NAME], [\n\n // WebView\n /((?:fban\\/fbios|fb_iab\\/fb4a)(?!.+fbav)|;fbav\\/([\\w\\.]+);)/i // Facebook App for iOS & Android\n ], [[NAME, FACEBOOK], VERSION], [\n /(kakao(?:talk|story))[\\/ ]([\\w\\.]+)/i, // Kakao App\n /(naver)\\(.*?(\\d+\\.[\\w\\.]+).*\\)/i, // Naver InApp\n /safari (line)\\/([\\w\\.]+)/i, // Line App for iOS\n /\\b(line)\\/([\\w\\.]+)\\/iab/i, // Line App for Android\n /(chromium|instagram)[\\/ ]([-\\w\\.]+)/i // Chromium/Instagram\n ], [NAME, VERSION], [\n /\\bgsa\\/([\\w\\.]+) .*safari\\//i // Google Search Appliance on iOS\n ], [VERSION, [NAME, 'GSA']], [\n /musical_ly(?:.+app_?version\\/|_)([\\w\\.]+)/i // TikTok\n ], [VERSION, [NAME, 'TikTok']], [\n\n /headlesschrome(?:\\/([\\w\\.]+)| )/i // Chrome Headless\n ], [VERSION, [NAME, CHROME+' Headless']], [\n\n / wv\\).+(chrome)\\/([\\w\\.]+)/i // Chrome WebView\n ], [[NAME, CHROME+' WebView'], VERSION], [\n\n /droid.+ version\\/([\\w\\.]+)\\b.+(?:mobile safari|safari)/i // Android Browser\n ], [VERSION, [NAME, 'Android '+BROWSER]], [\n\n /(chrome|omniweb|arora|[tizenoka]{5} ?browser)\\/v?([\\w\\.]+)/i // Chrome/OmniWeb/Arora/Tizen/Nokia\n ], [NAME, VERSION], [\n\n /version\\/([\\w\\.\\,]+) .*mobile\\/\\w+ (safari)/i // Mobile Safari\n ], [VERSION, [NAME, 'Mobile Safari']], [\n /version\\/([\\w(\\.|\\,)]+) .*(mobile ?safari|safari)/i // Safari & Safari Mobile\n ], [VERSION, NAME], [\n /webkit.+?(mobile ?safari|safari)(\\/[\\w\\.]+)/i // Safari < 3.0\n ], [NAME, [VERSION, strMapper, oldSafariMap]], [\n\n /(webkit|khtml)\\/([\\w\\.]+)/i\n ], [NAME, VERSION], [\n\n // Gecko based\n /(navigator|netscape\\d?)\\/([-\\w\\.]+)/i // Netscape\n ], [[NAME, 'Netscape'], VERSION], [\n /mobile vr; rv:([\\w\\.]+)\\).+firefox/i // Firefox Reality\n ], [VERSION, [NAME, FIREFOX+' Reality']], [\n /ekiohf.+(flow)\\/([\\w\\.]+)/i, // Flow\n /(swiftfox)/i, // Swiftfox\n /(icedragon|iceweasel|camino|chimera|fennec|maemo browser|minimo|conkeror|klar)[\\/ ]?([\\w\\.\\+]+)/i,\n // IceDragon/Iceweasel/Camino/Chimera/Fennec/Maemo/Minimo/Conkeror/Klar\n /(seamonkey|k-meleon|icecat|iceape|firebird|phoenix|palemoon|basilisk|waterfox)\\/([-\\w\\.]+)$/i,\n // Firefox/SeaMonkey/K-Meleon/IceCat/IceApe/Firebird/Phoenix\n /(firefox)\\/([\\w\\.]+)/i, // Other Firefox-based\n /(mozilla)\\/([\\w\\.]+) .+rv\\:.+gecko\\/\\d+/i, // Mozilla\n\n // Other\n /(polaris|lynx|dillo|icab|doris|amaya|w3m|netsurf|sleipnir|obigo|mosaic|(?:go|ice|up)[\\. ]?browser)[-\\/ ]?v?([\\w\\.]+)/i,\n // Polaris/Lynx/Dillo/iCab/Doris/Amaya/w3m/NetSurf/Sleipnir/Obigo/Mosaic/Go/ICE/UP.Browser\n /(links) \\(([\\w\\.]+)/i, // Links\n /panasonic;(viera)/i // Panasonic Viera\n ], [NAME, VERSION], [\n \n /(cobalt)\\/([\\w\\.]+)/i // Cobalt\n ], [NAME, [VERSION, /master.|lts./, \"\"]]\n ],\n\n cpu : [[\n\n /(?:(amd|x(?:(?:86|64)[-_])?|wow|win)64)[;\\)]/i // AMD64 (x64)\n ], [[ARCHITECTURE, 'amd64']], [\n\n /(ia32(?=;))/i // IA32 (quicktime)\n ], [[ARCHITECTURE, lowerize]], [\n\n /((?:i[346]|x)86)[;\\)]/i // IA32 (x86)\n ], [[ARCHITECTURE, 'ia32']], [\n\n /\\b(aarch64|arm(v?8e?l?|_?64))\\b/i // ARM64\n ], [[ARCHITECTURE, 'arm64']], [\n\n /\\b(arm(?:v[67])?ht?n?[fl]p?)\\b/i // ARMHF\n ], [[ARCHITECTURE, 'armhf']], [\n\n // PocketPC mistakenly identified as PowerPC\n /windows (ce|mobile); ppc;/i\n ], [[ARCHITECTURE, 'arm']], [\n\n /((?:ppc|powerpc)(?:64)?)(?: mac|;|\\))/i // PowerPC\n ], [[ARCHITECTURE, /ower/, EMPTY, lowerize]], [\n\n /(sun4\\w)[;\\)]/i // SPARC\n ], [[ARCHITECTURE, 'sparc']], [\n\n /((?:avr32|ia64(?=;))|68k(?=\\))|\\barm(?=v(?:[1-7]|[5-7]1)l?|;|eabi)|(?=atmel )avr|(?:irix|mips|sparc)(?:64)?\\b|pa-risc)/i\n // IA64, 68K, ARM/64, AVR/32, IRIX/64, MIPS/64, SPARC/64, PA-RISC\n ], [[ARCHITECTURE, lowerize]]\n ],\n\n device : [[\n\n //////////////////////////\n // MOBILES & TABLETS\n /////////////////////////\n\n // Samsung\n /\\b(sch-i[89]0\\d|shw-m380s|sm-[ptx]\\w{2,4}|gt-[pn]\\d{2,4}|sgh-t8[56]9|nexus 10)/i\n ], [MODEL, [VENDOR, SAMSUNG], [TYPE, TABLET]], [\n /\\b((?:s[cgp]h|gt|sm)-\\w+|sc[g-]?[\\d]+a?|galaxy nexus)/i,\n /samsung[- ]([-\\w]+)/i,\n /sec-(sgh\\w+)/i\n ], [MODEL, [VENDOR, SAMSUNG], [TYPE, MOBILE]], [\n\n // Apple\n /(?:\\/|\\()(ip(?:hone|od)[\\w, ]*)(?:\\/|;)/i // iPod/iPhone\n ], [MODEL, [VENDOR, APPLE], [TYPE, MOBILE]], [\n /\\((ipad);[-\\w\\),; ]+apple/i, // iPad\n /applecoremedia\\/[\\w\\.]+ \\((ipad)/i,\n /\\b(ipad)\\d\\d?,\\d\\d?[;\\]].+ios/i\n ], [MODEL, [VENDOR, APPLE], [TYPE, TABLET]], [\n /(macintosh);/i\n ], [MODEL, [VENDOR, APPLE]], [\n\n // Sharp\n /\\b(sh-?[altvz]?\\d\\d[a-ekm]?)/i\n ], [MODEL, [VENDOR, SHARP], [TYPE, MOBILE]], [\n\n // Huawei\n /\\b((?:ag[rs][23]?|bah2?|sht?|btv)-a?[lw]\\d{2})\\b(?!.+d\\/s)/i\n ], [MODEL, [VENDOR, HUAWEI], [TYPE, TABLET]], [\n /(?:huawei|honor)([-\\w ]+)[;\\)]/i,\n /\\b(nexus 6p|\\w{2,4}e?-[atu]?[ln][\\dx][012359c][adn]?)\\b(?!.+d\\/s)/i\n ], [MODEL, [VENDOR, HUAWEI], [TYPE, MOBILE]], [\n\n // Xiaomi\n /\\b(poco[\\w ]+)(?: bui|\\))/i, // Xiaomi POCO\n /\\b; (\\w+) build\\/hm\\1/i, // Xiaomi Hongmi 'numeric' models\n /\\b(hm[-_ ]?note?[_ ]?(?:\\d\\w)?) bui/i, // Xiaomi Hongmi\n /\\b(redmi[\\-_ ]?(?:note|k)?[\\w_ ]+)(?: bui|\\))/i, // Xiaomi Redmi\n /\\b(mi[-_ ]?(?:a\\d|one|one[_ ]plus|note lte|max|cc)?[_ ]?(?:\\d?\\w?)[_ ]?(?:plus|se|lite)?)(?: bui|\\))/i // Xiaomi Mi\n ], [[MODEL, /_/g, ' '], [VENDOR, XIAOMI], [TYPE, MOBILE]], [\n /\\b(mi[-_ ]?(?:pad)(?:[\\w_ ]+))(?: bui|\\))/i // Mi Pad tablets\n ],[[MODEL, /_/g, ' '], [VENDOR, XIAOMI], [TYPE, TABLET]], [\n\n // OPPO\n /; (\\w+) bui.+ oppo/i,\n /\\b(cph[12]\\d{3}|p(?:af|c[al]|d\\w|e[ar])[mt]\\d0|x9007|a101op)\\b/i\n ], [MODEL, [VENDOR, 'OPPO'], [TYPE, MOBILE]], [\n\n // Vivo\n /vivo (\\w+)(?: bui|\\))/i,\n /\\b(v[12]\\d{3}\\w?[at])(?: bui|;)/i\n ], [MODEL, [VENDOR, 'Vivo'], [TYPE, MOBILE]], [\n\n // Realme\n /\\b(rmx[12]\\d{3})(?: bui|;|\\))/i\n ], [MODEL, [VENDOR, 'Realme'], [TYPE, MOBILE]], [\n\n // Motorola\n /\\b(milestone|droid(?:[2-4x]| (?:bionic|x2|pro|razr))?:?( 4g)?)\\b[\\w ]+build\\//i,\n /\\bmot(?:orola)?[- ](\\w*)/i,\n /((?:moto[\\w\\(\\) ]+|xt\\d{3,4}|nexus 6)(?= bui|\\)))/i\n ], [MODEL, [VENDOR, MOTOROLA], [TYPE, MOBILE]], [\n /\\b(mz60\\d|xoom[2 ]{0,2}) build\\//i\n ], [MODEL, [VENDOR, MOTOROLA], [TYPE, TABLET]], [\n\n // LG\n /((?=lg)?[vl]k\\-?\\d{3}) bui| 3\\.[-\\w; ]{10}lg?-([06cv9]{3,4})/i\n ], [MODEL, [VENDOR, LG], [TYPE, TABLET]], [\n /(lm(?:-?f100[nv]?|-[\\w\\.]+)(?= bui|\\))|nexus [45])/i,\n /\\blg[-e;\\/ ]+((?!browser|netcast|android tv)\\w+)/i,\n /\\blg-?([\\d\\w]+) bui/i\n ], [MODEL, [VENDOR, LG], [TYPE, MOBILE]], [\n\n // Lenovo\n /(ideatab[-\\w ]+)/i,\n /lenovo ?(s[56]000[-\\w]+|tab(?:[\\w ]+)|yt[-\\d\\w]{6}|tb[-\\d\\w]{6})/i\n ], [MODEL, [VENDOR, 'Lenovo'], [TYPE, TABLET]], [\n\n // Nokia\n /(?:maemo|nokia).*(n900|lumia \\d+)/i,\n /nokia[-_ ]?([-\\w\\.]*)/i\n ], [[MODEL, /_/g, ' '], [VENDOR, 'Nokia'], [TYPE, MOBILE]], [\n\n // Google\n /(pixel c)\\b/i // Google Pixel C\n ], [MODEL, [VENDOR, GOOGLE], [TYPE, TABLET]], [\n /droid.+; (pixel[\\daxl ]{0,6})(?: bui|\\))/i // Google Pixel\n ], [MODEL, [VENDOR, GOOGLE], [TYPE, MOBILE]], [\n\n // Sony\n /droid.+ (a?\\d[0-2]{2}so|[c-g]\\d{4}|so[-gl]\\w+|xq-a\\w[4-7][12])(?= bui|\\).+chrome\\/(?![1-6]{0,1}\\d\\.))/i\n ], [MODEL, [VENDOR, SONY], [TYPE, MOBILE]], [\n /sony tablet [ps]/i,\n /\\b(?:sony)?sgp\\w+(?: bui|\\))/i\n ], [[MODEL, 'Xperia Tablet'], [VENDOR, SONY], [TYPE, TABLET]], [\n\n // OnePlus\n / (kb2005|in20[12]5|be20[12][59])\\b/i,\n /(?:one)?(?:plus)? (a\\d0\\d\\d)(?: b|\\))/i\n ], [MODEL, [VENDOR, 'OnePlus'], [TYPE, MOBILE]], [\n\n // Amazon\n /(alexa)webm/i,\n /(kf[a-z]{2}wi|aeo[c-r]{2})( bui|\\))/i, // Kindle Fire without Silk / Echo Show\n /(kf[a-z]+)( bui|\\)).+silk\\//i // Kindle Fire HD\n ], [MODEL, [VENDOR, AMAZON], [TYPE, TABLET]], [\n /((?:sd|kf)[0349hijorstuw]+)( bui|\\)).+silk\\//i // Fire Phone\n ], [[MODEL, /(.+)/g, 'Fire Phone $1'], [VENDOR, AMAZON], [TYPE, MOBILE]], [\n\n // BlackBerry\n /(playbook);[-\\w\\),; ]+(rim)/i // BlackBerry PlayBook\n ], [MODEL, VENDOR, [TYPE, TABLET]], [\n /\\b((?:bb[a-f]|st[hv])100-\\d)/i,\n /\\(bb10; (\\w+)/i // BlackBerry 10\n ], [MODEL, [VENDOR, BLACKBERRY], [TYPE, MOBILE]], [\n\n // Asus\n /(?:\\b|asus_)(transfo[prime ]{4,10} \\w+|eeepc|slider \\w+|nexus 7|padfone|p00[cj])/i\n ], [MODEL, [VENDOR, ASUS], [TYPE, TABLET]], [\n / (z[bes]6[027][012][km][ls]|zenfone \\d\\w?)\\b/i\n ], [MODEL, [VENDOR, ASUS], [TYPE, MOBILE]], [\n\n // HTC\n /(nexus 9)/i // HTC Nexus 9\n ], [MODEL, [VENDOR, 'HTC'], [TYPE, TABLET]], [\n /(htc)[-;_ ]{1,2}([\\w ]+(?=\\)| bui)|\\w+)/i, // HTC\n\n // ZTE\n /(zte)[- ]([\\w ]+?)(?: bui|\\/|\\))/i,\n /(alcatel|geeksphone|nexian|panasonic(?!(?:;|\\.))|sony(?!-bra))[-_ ]?([-\\w]*)/i // Alcatel/GeeksPhone/Nexian/Panasonic/Sony\n ], [VENDOR, [MODEL, /_/g, ' '], [TYPE, MOBILE]], [\n\n // Acer\n /droid.+; ([ab][1-7]-?[0178a]\\d\\d?)/i\n ], [MODEL, [VENDOR, 'Acer'], [TYPE, TABLET]], [\n\n // Meizu\n /droid.+; (m[1-5] note) bui/i,\n /\\bmz-([-\\w]{2,})/i\n ], [MODEL, [VENDOR, 'Meizu'], [TYPE, MOBILE]], [\n\n // MIXED\n /(blackberry|benq|palm(?=\\-)|sonyericsson|acer|asus|dell|meizu|motorola|polytron)[-_ ]?([-\\w]*)/i,\n // BlackBerry/BenQ/Palm/Sony-Ericsson/Acer/Asus/Dell/Meizu/Motorola/Polytron\n /(hp) ([\\w ]+\\w)/i, // HP iPAQ\n /(asus)-?(\\w+)/i, // Asus\n /(microsoft); (lumia[\\w ]+)/i, // Microsoft Lumia\n /(lenovo)[-_ ]?([-\\w]+)/i, // Lenovo\n /(jolla)/i, // Jolla\n /(oppo) ?([\\w ]+) bui/i // OPPO\n ], [VENDOR, MODEL, [TYPE, MOBILE]], [\n\n /(kobo)\\s(ereader|touch)/i, // Kobo\n /(archos) (gamepad2?)/i, // Archos\n /(hp).+(touchpad(?!.+tablet)|tablet)/i, // HP TouchPad\n /(kindle)\\/([\\w\\.]+)/i, // Kindle\n /(nook)[\\w ]+build\\/(\\w+)/i, // Nook\n /(dell) (strea[kpr\\d ]*[\\dko])/i, // Dell Streak\n /(le[- ]+pan)[- ]+(\\w{1,9}) bui/i, // Le Pan Tablets\n /(trinity)[- ]*(t\\d{3}) bui/i, // Trinity Tablets\n /(gigaset)[- ]+(q\\w{1,9}) bui/i, // Gigaset Tablets\n /(vodafone) ([\\w ]+)(?:\\)| bui)/i // Vodafone\n ], [VENDOR, MODEL, [TYPE, TABLET]], [\n\n /(surface duo)/i // Surface Duo\n ], [MODEL, [VENDOR, MICROSOFT], [TYPE, TABLET]], [\n /droid [\\d\\.]+; (fp\\du?)(?: b|\\))/i // Fairphone\n ], [MODEL, [VENDOR, 'Fairphone'], [TYPE, MOBILE]], [\n /(u304aa)/i // AT&T\n ], [MODEL, [VENDOR, 'AT&T'], [TYPE, MOBILE]], [\n /\\bsie-(\\w*)/i // Siemens\n ], [MODEL, [VENDOR, 'Siemens'], [TYPE, MOBILE]], [\n /\\b(rct\\w+) b/i // RCA Tablets\n ], [MODEL, [VENDOR, 'RCA'], [TYPE, TABLET]], [\n /\\b(venue[\\d ]{2,7}) b/i // Dell Venue Tablets\n ], [MODEL, [VENDOR, 'Dell'], [TYPE, TABLET]], [\n /\\b(q(?:mv|ta)\\w+) b/i // Verizon Tablet\n ], [MODEL, [VENDOR, 'Verizon'], [TYPE, TABLET]], [\n /\\b(?:barnes[& ]+noble |bn[rt])([\\w\\+ ]*) b/i // Barnes & Noble Tablet\n ], [MODEL, [VENDOR, 'Barnes & Noble'], [TYPE, TABLET]], [\n /\\b(tm\\d{3}\\w+) b/i\n ], [MODEL, [VENDOR, 'NuVision'], [TYPE, TABLET]], [\n /\\b(k88) b/i // ZTE K Series Tablet\n ], [MODEL, [VENDOR, 'ZTE'], [TYPE, TABLET]], [\n /\\b(nx\\d{3}j) b/i // ZTE Nubia\n ], [MODEL, [VENDOR, 'ZTE'], [TYPE, MOBILE]], [\n /\\b(gen\\d{3}) b.+49h/i // Swiss GEN Mobile\n ], [MODEL, [VENDOR, 'Swiss'], [TYPE, MOBILE]], [\n /\\b(zur\\d{3}) b/i // Swiss ZUR Tablet\n ], [MODEL, [VENDOR, 'Swiss'], [TYPE, TABLET]], [\n /\\b((zeki)?tb.*\\b) b/i // Zeki Tablets\n ], [MODEL, [VENDOR, 'Zeki'], [TYPE, TABLET]], [\n /\\b([yr]\\d{2}) b/i,\n /\\b(dragon[- ]+touch |dt)(\\w{5}) b/i // Dragon Touch Tablet\n ], [[VENDOR, 'Dragon Touch'], MODEL, [TYPE, TABLET]], [\n /\\b(ns-?\\w{0,9}) b/i // Insignia Tablets\n ], [MODEL, [VENDOR, 'Insignia'], [TYPE, TABLET]], [\n /\\b((nxa|next)-?\\w{0,9}) b/i // NextBook Tablets\n ], [MODEL, [VENDOR, 'NextBook'], [TYPE, TABLET]], [\n /\\b(xtreme\\_)?(v(1[045]|2[015]|[3469]0|7[05])) b/i // Voice Xtreme Phones\n ], [[VENDOR, 'Voice'], MODEL, [TYPE, MOBILE]], [\n /\\b(lvtel\\-)?(v1[12]) b/i // LvTel Phones\n ], [[VENDOR, 'LvTel'], MODEL, [TYPE, MOBILE]], [\n /\\b(ph-1) /i // Essential PH-1\n ], [MODEL, [VENDOR, 'Essential'], [TYPE, MOBILE]], [\n /\\b(v(100md|700na|7011|917g).*\\b) b/i // Envizen Tablets\n ], [MODEL, [VENDOR, 'Envizen'], [TYPE, TABLET]], [\n /\\b(trio[-\\w\\. ]+) b/i // MachSpeed Tablets\n ], [MODEL, [VENDOR, 'MachSpeed'], [TYPE, TABLET]], [\n /\\btu_(1491) b/i // Rotor Tablets\n ], [MODEL, [VENDOR, 'Rotor'], [TYPE, TABLET]], [\n /(shield[\\w ]+) b/i // Nvidia Shield Tablets\n ], [MODEL, [VENDOR, 'Nvidia'], [TYPE, TABLET]], [\n /(sprint) (\\w+)/i // Sprint Phones\n ], [VENDOR, MODEL, [TYPE, MOBILE]], [\n /(kin\\.[onetw]{3})/i // Microsoft Kin\n ], [[MODEL, /\\./g, ' '], [VENDOR, MICROSOFT], [TYPE, MOBILE]], [\n /droid.+; (cc6666?|et5[16]|mc[239][23]x?|vc8[03]x?)\\)/i // Zebra\n ], [MODEL, [VENDOR, ZEBRA], [TYPE, TABLET]], [\n /droid.+; (ec30|ps20|tc[2-8]\\d[kx])\\)/i\n ], [MODEL, [VENDOR, ZEBRA], [TYPE, MOBILE]], [\n\n ///////////////////\n // SMARTTVS\n ///////////////////\n\n /smart-tv.+(samsung)/i // Samsung\n ], [VENDOR, [TYPE, SMARTTV]], [\n /hbbtv.+maple;(\\d+)/i\n ], [[MODEL, /^/, 'SmartTV'], [VENDOR, SAMSUNG], [TYPE, SMARTTV]], [\n /(nux; netcast.+smarttv|lg (netcast\\.tv-201\\d|android tv))/i // LG SmartTV\n ], [[VENDOR, LG], [TYPE, SMARTTV]], [\n /(apple) ?tv/i // Apple TV\n ], [VENDOR, [MODEL, APPLE+' TV'], [TYPE, SMARTTV]], [\n /crkey/i // Google Chromecast\n ], [[MODEL, CHROME+'cast'], [VENDOR, GOOGLE], [TYPE, SMARTTV]], [\n /droid.+aft(\\w)( bui|\\))/i // Fire TV\n ], [MODEL, [VENDOR, AMAZON], [TYPE, SMARTTV]], [\n /\\(dtv[\\);].+(aquos)/i,\n /(aquos-tv[\\w ]+)\\)/i // Sharp\n ], [MODEL, [VENDOR, SHARP], [TYPE, SMARTTV]],[\n /(bravia[\\w ]+)( bui|\\))/i // Sony\n ], [MODEL, [VENDOR, SONY], [TYPE, SMARTTV]], [\n /(mitv-\\w{5}) bui/i // Xiaomi\n ], [MODEL, [VENDOR, XIAOMI], [TYPE, SMARTTV]], [\n /Hbbtv.*(technisat) (.*);/i // TechniSAT\n ], [VENDOR, MODEL, [TYPE, SMARTTV]], [\n /\\b(roku)[\\dx]*[\\)\\/]((?:dvp-)?[\\d\\.]*)/i, // Roku\n /hbbtv\\/\\d+\\.\\d+\\.\\d+ +\\([\\w\\+ ]*; *([\\w\\d][^;]*);([^;]*)/i // HbbTV devices\n ], [[VENDOR, trim], [MODEL, trim], [TYPE, SMARTTV]], [\n /\\b(android tv|smart[- ]?tv|opera tv|tv; rv:)\\b/i // SmartTV from Unidentified Vendors\n ], [[TYPE, SMARTTV]], [\n\n ///////////////////\n // CONSOLES\n ///////////////////\n\n /(ouya)/i, // Ouya\n /(nintendo) ([wids3utch]+)/i // Nintendo\n ], [VENDOR, MODEL, [TYPE, CONSOLE]], [\n /droid.+; (shield) bui/i // Nvidia\n ], [MODEL, [VENDOR, 'Nvidia'], [TYPE, CONSOLE]], [\n /(playstation [345portablevi]+)/i // Playstation\n ], [MODEL, [VENDOR, SONY], [TYPE, CONSOLE]], [\n /\\b(xbox(?: one)?(?!; xbox))[\\); ]/i // Microsoft Xbox\n ], [MODEL, [VENDOR, MICROSOFT], [TYPE, CONSOLE]], [\n\n ///////////////////\n // WEARABLES\n ///////////////////\n\n /((pebble))app/i // Pebble\n ], [VENDOR, MODEL, [TYPE, WEARABLE]], [\n /(watch)(?: ?os[,\\/]|\\d,\\d\\/)[\\d\\.]+/i // Apple Watch\n ], [MODEL, [VENDOR, APPLE], [TYPE, WEARABLE]], [\n /droid.+; (glass) \\d/i // Google Glass\n ], [MODEL, [VENDOR, GOOGLE], [TYPE, WEARABLE]], [\n /droid.+; (wt63?0{2,3})\\)/i\n ], [MODEL, [VENDOR, ZEBRA], [TYPE, WEARABLE]], [\n /(quest( 2| pro)?)/i // Oculus Quest\n ], [MODEL, [VENDOR, FACEBOOK], [TYPE, WEARABLE]], [\n\n ///////////////////\n // EMBEDDED\n ///////////////////\n\n /(tesla)(?: qtcarbrowser|\\/[-\\w\\.]+)/i // Tesla\n ], [VENDOR, [TYPE, EMBEDDED]], [\n /(aeobc)\\b/i // Echo Dot\n ], [MODEL, [VENDOR, AMAZON], [TYPE, EMBEDDED]], [\n\n ////////////////////\n // MIXED (GENERIC)\n ///////////////////\n\n /droid .+?; ([^;]+?)(?: bui|\\) applew).+? mobile safari/i // Android Phones from Unidentified Vendors\n ], [MODEL, [TYPE, MOBILE]], [\n /droid .+?; ([^;]+?)(?: bui|\\) applew).+?(?! mobile) safari/i // Android Tablets from Unidentified Vendors\n ], [MODEL, [TYPE, TABLET]], [\n /\\b((tablet|tab)[;\\/]|focus\\/\\d(?!.+mobile))/i // Unidentifiable Tablet\n ], [[TYPE, TABLET]], [\n /(phone|mobile(?:[;\\/]| [ \\w\\/\\.]*safari)|pda(?=.+windows ce))/i // Unidentifiable Mobile\n ], [[TYPE, MOBILE]], [\n /(android[-\\w\\. ]{0,9});.+buil/i // Generic Android Device\n ], [MODEL, [VENDOR, 'Generic']]\n ],\n\n engine : [[\n\n /windows.+ edge\\/([\\w\\.]+)/i // EdgeHTML\n ], [VERSION, [NAME, EDGE+'HTML']], [\n\n /webkit\\/537\\.36.+chrome\\/(?!27)([\\w\\.]+)/i // Blink\n ], [VERSION, [NAME, 'Blink']], [\n\n /(presto)\\/([\\w\\.]+)/i, // Presto\n /(webkit|trident|netfront|netsurf|amaya|lynx|w3m|goanna)\\/([\\w\\.]+)/i, // WebKit/Trident/NetFront/NetSurf/Amaya/Lynx/w3m/Goanna\n /ekioh(flow)\\/([\\w\\.]+)/i, // Flow\n /(khtml|tasman|links)[\\/ ]\\(?([\\w\\.]+)/i, // KHTML/Tasman/Links\n /(icab)[\\/ ]([23]\\.[\\d\\.]+)/i, // iCab\n /\\b(libweb)/i\n ], [NAME, VERSION], [\n\n /rv\\:([\\w\\.]{1,9})\\b.+(gecko)/i // Gecko\n ], [VERSION, NAME]\n ],\n\n os : [[\n\n // Windows\n /microsoft (windows) (vista|xp)/i // Windows (iTunes)\n ], [NAME, VERSION], [\n /(windows) nt 6\\.2; (arm)/i, // Windows RT\n /(windows (?:phone(?: os)?|mobile))[\\/ ]?([\\d\\.\\w ]*)/i, // Windows Phone\n /(windows)[\\/ ]?([ntce\\d\\. ]+\\w)(?!.+xbox)/i\n ], [NAME, [VERSION, strMapper, windowsVersionMap]], [\n /(win(?=3|9|n)|win 9x )([nt\\d\\.]+)/i\n ], [[NAME, 'Windows'], [VERSION, strMapper, windowsVersionMap]], [\n\n // iOS/macOS\n /ip[honead]{2,4}\\b(?:.*os ([\\w]+) like mac|; opera)/i, // iOS\n /ios;fbsv\\/([\\d\\.]+)/i,\n /cfnetwork\\/.+darwin/i\n ], [[VERSION, /_/g, '.'], [NAME, 'iOS']], [\n /(mac os x) ?([\\w\\. ]*)/i,\n /(macintosh|mac_powerpc\\b)(?!.+haiku)/i // Mac OS\n ], [[NAME, MAC_OS], [VERSION, /_/g, '.']], [\n\n // Mobile OSes\n /droid ([\\w\\.]+)\\b.+(android[- ]x86|harmonyos)/i // Android-x86/HarmonyOS\n ], [VERSION, NAME], [ // Android/WebOS/QNX/Bada/RIM/Maemo/MeeGo/Sailfish OS\n /(android|webos|qnx|bada|rim tablet os|maemo|meego|sailfish)[-\\/ ]?([\\w\\.]*)/i,\n /(blackberry)\\w*\\/([\\w\\.]*)/i, // Blackberry\n /(tizen|kaios)[\\/ ]([\\w\\.]+)/i, // Tizen/KaiOS\n /\\((series40);/i // Series 40\n ], [NAME, VERSION], [\n /\\(bb(10);/i // BlackBerry 10\n ], [VERSION, [NAME, BLACKBERRY]], [\n /(?:symbian ?os|symbos|s60(?=;)|series60)[-\\/ ]?([\\w\\.]*)/i // Symbian\n ], [VERSION, [NAME, 'Symbian']], [\n /mozilla\\/[\\d\\.]+ \\((?:mobile|tablet|tv|mobile; [\\w ]+); rv:.+ gecko\\/([\\w\\.]+)/i // Firefox OS\n ], [VERSION, [NAME, FIREFOX+' OS']], [\n /web0s;.+rt(tv)/i,\n /\\b(?:hp)?wos(?:browser)?\\/([\\w\\.]+)/i // WebOS\n ], [VERSION, [NAME, 'webOS']], [\n /watch(?: ?os[,\\/]|\\d,\\d\\/)([\\d\\.]+)/i // watchOS\n ], [VERSION, [NAME, 'watchOS']], [\n\n // Google Chromecast\n /crkey\\/([\\d\\.]+)/i // Google Chromecast\n ], [VERSION, [NAME, CHROME+'cast']], [\n /(cros) [\\w]+(?:\\)| ([\\w\\.]+)\\b)/i // Chromium OS\n ], [[NAME, CHROMIUM_OS], VERSION],[\n\n // Smart TVs\n /panasonic;(viera)/i, // Panasonic Viera\n /(netrange)mmh/i, // Netrange\n /(nettv)\\/(\\d+\\.[\\w\\.]+)/i, // NetTV\n\n // Console\n /(nintendo|playstation) ([wids345portablevuch]+)/i, // Nintendo/Playstation\n /(xbox); +xbox ([^\\);]+)/i, // Microsoft Xbox (360, One, X, S, Series X, Series S)\n\n // Other\n /\\b(joli|palm)\\b ?(?:os)?\\/?([\\w\\.]*)/i, // Joli/Palm\n /(mint)[\\/\\(\\) ]?(\\w*)/i, // Mint\n /(mageia|vectorlinux)[; ]/i, // Mageia/VectorLinux\n /([kxln]?ubuntu|debian|suse|opensuse|gentoo|arch(?= linux)|slackware|fedora|mandriva|centos|pclinuxos|red ?hat|zenwalk|linpus|raspbian|plan 9|minix|risc os|contiki|deepin|manjaro|elementary os|sabayon|linspire)(?: gnu\\/linux)?(?: enterprise)?(?:[- ]linux)?(?:-gnu)?[-\\/ ]?(?!chrom|package)([-\\w\\.]*)/i,\n // Ubuntu/Debian/SUSE/Gentoo/Arch/Slackware/Fedora/Mandriva/CentOS/PCLinuxOS/RedHat/Zenwalk/Linpus/Raspbian/Plan9/Minix/RISCOS/Contiki/Deepin/Manjaro/elementary/Sabayon/Linspire\n /(hurd|linux) ?([\\w\\.]*)/i, // Hurd/Linux\n /(gnu) ?([\\w\\.]*)/i, // GNU\n /\\b([-frentopcghs]{0,5}bsd|dragonfly)[\\/ ]?(?!amd|[ix346]{1,2}86)([\\w\\.]*)/i, // FreeBSD/NetBSD/OpenBSD/PC-BSD/GhostBSD/DragonFly\n /(haiku) (\\w+)/i // Haiku\n ], [NAME, VERSION], [\n /(sunos) ?([\\w\\.\\d]*)/i // Solaris\n ], [[NAME, 'Solaris'], VERSION], [\n /((?:open)?solaris)[-\\/ ]?([\\w\\.]*)/i, // Solaris\n /(aix) ((\\d)(?=\\.|\\)| )[\\w\\.])*/i, // AIX\n /\\b(beos|os\\/2|amigaos|morphos|openvms|fuchsia|hp-ux|serenityos)/i, // BeOS/OS2/AmigaOS/MorphOS/OpenVMS/Fuchsia/HP-UX/SerenityOS\n /(unix) ?([\\w\\.]*)/i // UNIX\n ], [NAME, VERSION]\n ]\n };\n\n /////////////////\n // Constructor\n ////////////////\n\n var UAParser = function (ua, extensions) {\n\n if (typeof ua === OBJ_TYPE) {\n extensions = ua;\n ua = undefined;\n }\n\n if (!(this instanceof UAParser)) {\n return new UAParser(ua, extensions).getResult();\n }\n\n var _navigator = (typeof window !== UNDEF_TYPE && window.navigator) ? window.navigator : undefined;\n var _ua = ua || ((_navigator && _navigator.userAgent) ? _navigator.userAgent : EMPTY);\n var _uach = (_navigator && _navigator.userAgentData) ? _navigator.userAgentData : undefined;\n var _rgxmap = extensions ? extend(regexes, extensions) : regexes;\n var _isSelfNav = _navigator && _navigator.userAgent == _ua;\n\n this.getBrowser = function () {\n var _browser = {};\n _browser[NAME] = undefined;\n _browser[VERSION] = undefined;\n rgxMapper.call(_browser, _ua, _rgxmap.browser);\n _browser[MAJOR] = majorize(_browser[VERSION]);\n // Brave-specific detection\n if (_isSelfNav && _navigator && _navigator.brave && typeof _navigator.brave.isBrave == FUNC_TYPE) {\n _browser[NAME] = 'Brave';\n }\n return _browser;\n };\n this.getCPU = function () {\n var _cpu = {};\n _cpu[ARCHITECTURE] = undefined;\n rgxMapper.call(_cpu, _ua, _rgxmap.cpu);\n return _cpu;\n };\n this.getDevice = function () {\n var _device = {};\n _device[VENDOR] = undefined;\n _device[MODEL] = undefined;\n _device[TYPE] = undefined;\n rgxMapper.call(_device, _ua, _rgxmap.device);\n if (_isSelfNav && !_device[TYPE] && _uach && _uach.mobile) {\n _device[TYPE] = MOBILE;\n }\n // iPadOS-specific detection: identified as Mac, but has some iOS-only properties\n if (_isSelfNav && _device[MODEL] == 'Macintosh' && _navigator && typeof _navigator.standalone !== UNDEF_TYPE && _navigator.maxTouchPoints && _navigator.maxTouchPoints > 2) {\n _device[MODEL] = 'iPad';\n _device[TYPE] = TABLET;\n }\n return _device;\n };\n this.getEngine = function () {\n var _engine = {};\n _engine[NAME] = undefined;\n _engine[VERSION] = undefined;\n rgxMapper.call(_engine, _ua, _rgxmap.engine);\n return _engine;\n };\n this.getOS = function () {\n var _os = {};\n _os[NAME] = undefined;\n _os[VERSION] = undefined;\n rgxMapper.call(_os, _ua, _rgxmap.os);\n if (_isSelfNav && !_os[NAME] && _uach && _uach.platform != 'Unknown') {\n _os[NAME] = _uach.platform \n .replace(/chrome os/i, CHROMIUM_OS)\n .replace(/macos/i, MAC_OS); // backward compatibility\n }\n return _os;\n };\n this.getResult = function () {\n return {\n ua : this.getUA(),\n browser : this.getBrowser(),\n engine : this.getEngine(),\n os : this.getOS(),\n device : this.getDevice(),\n cpu : this.getCPU()\n };\n };\n this.getUA = function () {\n return _ua;\n };\n this.setUA = function (ua) {\n _ua = (typeof ua === STR_TYPE && ua.length > UA_MAX_LENGTH) ? trim(ua, UA_MAX_LENGTH) : ua;\n return this;\n };\n this.setUA(_ua);\n return this;\n };\n\n UAParser.VERSION = LIBVERSION;\n UAParser.BROWSER = enumerize([NAME, VERSION, MAJOR]);\n UAParser.CPU = enumerize([ARCHITECTURE]);\n UAParser.DEVICE = enumerize([MODEL, VENDOR, TYPE, CONSOLE, MOBILE, SMARTTV, TABLET, WEARABLE, EMBEDDED]);\n UAParser.ENGINE = UAParser.OS = enumerize([NAME, VERSION]);\n\n ///////////\n // Export\n //////////\n\n // check js environment\n if (typeof(exports) !== UNDEF_TYPE) {\n // nodejs env\n if (typeof module !== UNDEF_TYPE && module.exports) {\n exports = module.exports = UAParser;\n }\n exports.UAParser = UAParser;\n } else {\n // requirejs env (optional)\n if (typeof(define) === FUNC_TYPE && define.amd) {\n define(function () {\n return UAParser;\n });\n } else if (typeof window !== UNDEF_TYPE) {\n // browser env\n window.UAParser = UAParser;\n }\n }\n\n // jQuery/Zepto specific (optional)\n // Note:\n // In AMD env the global scope should be kept clean, but jQuery is an exception.\n // jQuery always exports to global scope, unless jQuery.noConflict(true) is used,\n // and we should catch that.\n var $ = typeof window !== UNDEF_TYPE && (window.jQuery || window.Zepto);\n if ($ && !$.ua) {\n var parser = new UAParser();\n $.ua = parser.getResult();\n $.ua.get = function () {\n return parser.getUA();\n };\n $.ua.set = function (ua) {\n parser.setUA(ua);\n var result = parser.getResult();\n for (var prop in result) {\n $.ua[prop] = result[prop];\n }\n };\n }\n\n})(typeof window === 'object' ? window : this);\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).substring(2, 12);\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.substring(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.substring(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.substring(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 attributes: parts.slice(2).join(' '),\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 +\n (headerExtension.attributes ? ' ' + headerExtension.attributes : '') +\n '\\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.substring(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.substring(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.substring(7, sp), 10),\n };\n const colon = line.indexOf(':', sp);\n if (colon > -1) {\n parts.attribute = line.substring(sp + 1, colon);\n parts.value = line.substring(colon + 1);\n } else {\n parts.attribute = line.substring(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.substring(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.substring(6);\n }\n};\n\n// Parses a fingerprint line for DTLS-SRTP.\nSDPUtils.parseFingerprint = function(line) {\n const parts = line.substring(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.substring(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.substring(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.substring(12),\n password: pwd.substring(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 description.profile = mline[2];\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:<pt> 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 const wildcardRtcpFb = SDPUtils.matchPrefix(mediaSection, 'a=rtcp-fb:* ')\n .map(SDPUtils.parseRtcpFb);\n description.codecs.forEach(codec => {\n wildcardRtcpFb.forEach(fb=> {\n const duplicate = codec.rtcpFeedback.find(existingFeedback => {\n return existingFeedback.type === fb.type &&\n existingFeedback.parameter === fb.parameter;\n });\n if (!duplicate) {\n codec.rtcpFeedback.push(fb);\n }\n });\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 += ' ' + (caps.profile || '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.substring(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].substring(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].substring(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\u0456bute.\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].substring(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].substring(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].substring(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 .substring(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, 22);\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].substring(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].substring(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].substring(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.substring(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", "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 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", "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", "import React, { Fragment, useCallback, useMemo, useState } from 'react';\nimport {\n selectAudioTrackByPeerID,\n selectIsPeerAudioEnabled,\n selectLocalPeerID,\n selectPeerMetadata,\n selectPeerNameByID,\n selectVideoTrackByID,\n selectVideoTrackByPeerID,\n useHMSStore,\n} from '@100mslive/react-sdk';\nimport { BrbIcon, HandRaiseFilledIcon, MicOffIcon } from '@100mslive/react-icons';\nimport TileConnection from './Connection/TileConnection';\nimport { useBorderAudioLevel } from '../../AudioLevel';\nimport { Avatar } from '../../Avatar';\nimport { VideoTileStats } from '../../Stats';\nimport { Video } from '../../Video';\nimport { StyledVideoTile } from '../../VideoTile';\nimport { getVideoTileLabel } from './peerTileUtils';\nimport TileMenu from './TileMenu';\nimport { useAppConfig } from './AppData/useAppConfig';\nimport { useIsHeadless, useUISettings } from './AppData/useUISettings';\nimport { UI_SETTINGS } from '../common/constants';\n\nconst Tile = ({ peerId, trackId, width, height, objectFit = 'cover', rootCSS = {}, containerCSS = {} }) => {\n const trackSelector = trackId ? selectVideoTrackByID(trackId) : selectVideoTrackByPeerID(peerId);\n const track = useHMSStore(trackSelector);\n const peerName = useHMSStore(selectPeerNameByID(peerId));\n const audioTrack = useHMSStore(selectAudioTrackByPeerID(peerId));\n const localPeerID = useHMSStore(selectLocalPeerID);\n const isAudioOnly = useUISettings(UI_SETTINGS.isAudioOnly);\n const mirrorLocalVideo = useUISettings(UI_SETTINGS.mirrorLocalVideo);\n const showStatsOnTiles = useUISettings(UI_SETTINGS.showStatsOnTiles);\n const isHeadless = useIsHeadless();\n const isAudioMuted = !useHMSStore(selectIsPeerAudioEnabled(peerId));\n const isVideoMuted = !track?.enabled;\n const [isMouseHovered, setIsMouseHovered] = useState(false);\n const borderAudioRef = useBorderAudioLevel(audioTrack?.id);\n const isVideoDegraded = track?.degraded;\n const isLocal = localPeerID === peerId;\n const label = getVideoTileLabel({\n peerName,\n track,\n isLocal,\n });\n const onHoverHandler = useCallback(event => {\n setIsMouseHovered(event.type === 'mouseenter');\n }, []);\n const headlessConfig = useAppConfig('headlessConfig');\n const hideLabel = isHeadless && headlessConfig?.hideTileName;\n const isTileBigEnoughToShowStats = height >= 180 && width >= 180;\n const avatarSize = useMemo(() => {\n if (!width || !height) {\n return undefined;\n }\n if (width <= 150 || height <= 150) {\n return 'small';\n } else if (width <= 300 || height <= 300) {\n return 'medium';\n }\n return 'large';\n }, [width, height]);\n\n return (\n <StyledVideoTile.Root\n css={{\n width,\n height,\n padding: getPadding({\n isHeadless,\n tileOffset: headlessConfig?.tileOffset,\n hideAudioLevel: headlessConfig?.hideAudioLevel,\n }),\n ...rootCSS,\n }}\n data-testid={`participant_tile_${peerName}`}\n >\n {peerName !== undefined ? (\n <StyledVideoTile.Container\n onMouseEnter={onHoverHandler}\n onMouseLeave={onHoverHandler}\n ref={isHeadless && headlessConfig?.hideAudioLevel ? undefined : borderAudioRef}\n noRadius={isHeadless && Number(headlessConfig?.tileOffset) === 0}\n css={containerCSS}\n >\n {showStatsOnTiles && isTileBigEnoughToShowStats ? (\n <VideoTileStats audioTrackID={audioTrack?.id} videoTrackID={track?.id} peerID={peerId} isLocal={isLocal} />\n ) : null}\n\n {track ? (\n <Video\n trackId={track?.id}\n attach={isLocal ? undefined : !isAudioOnly}\n mirror={\n mirrorLocalVideo &&\n peerId === localPeerID &&\n track?.source === 'regular' &&\n track?.facingMode !== 'environment'\n }\n degraded={isVideoDegraded}\n noRadius={isHeadless && Number(headlessConfig?.tileOffset) === 0}\n data-testid=\"participant_video_tile\"\n css={{\n objectFit,\n }}\n />\n ) : null}\n {isVideoMuted || isVideoDegraded || (!isLocal && isAudioOnly) ? (\n <StyledVideoTile.AvatarContainer>\n <Avatar name={peerName || ''} data-testid=\"participant_avatar_icon\" size={avatarSize} />\n </StyledVideoTile.AvatarContainer>\n ) : null}\n\n {showAudioMuted({\n hideTileAudioMute: headlessConfig?.hideTileAudioMute,\n isHeadless,\n isAudioMuted,\n }) ? (\n <StyledVideoTile.AudioIndicator\n data-testid=\"participant_audio_mute_icon\"\n size={width && height && (width < 180 || height < 180) ? 'small' : 'medium'}\n >\n <MicOffIcon />\n </StyledVideoTile.AudioIndicator>\n ) : null}\n {isMouseHovered && !isHeadless ? (\n <TileMenu peerID={peerId} audioTrackID={audioTrack?.id} videoTrackID={track?.id} />\n ) : null}\n <PeerMetadata peerId={peerId} />\n <TileConnection hideLabel={hideLabel} name={label} isTile peerId={peerId} width={width} />\n </StyledVideoTile.Container>\n ) : null}\n </StyledVideoTile.Root>\n );\n};\n\nconst metaStyles = { top: '$4', left: '$4' };\n\nconst PeerMetadata = ({ peerId }) => {\n const metaData = useHMSStore(selectPeerMetadata(peerId));\n const isHandRaised = metaData?.isHandRaised || false;\n const isBRB = metaData?.isBRBOn || false;\n\n return (\n <Fragment>\n {isHandRaised ? (\n <StyledVideoTile.AttributeBox css={metaStyles} data-testid=\"raiseHand_icon_onTile\">\n <HandRaiseFilledIcon width={40} height={40} />\n </StyledVideoTile.AttributeBox>\n ) : null}\n {isBRB ? (\n <StyledVideoTile.AttributeBox css={metaStyles} data-testid=\"brb_icon_onTile\">\n <BrbIcon width={40} height={40} />\n </StyledVideoTile.AttributeBox>\n ) : null}\n </Fragment>\n );\n};\n\nconst VideoTile = React.memo(Tile);\n\nconst showAudioMuted = ({ hideTileAudioMute, isHeadless, isAudioMuted }) => {\n if (!isHeadless) {\n return isAudioMuted;\n }\n return isAudioMuted && !hideTileAudioMute;\n};\n\nconst getPadding = ({ isHeadless, tileOffset, hideAudioLevel }) => {\n if (!isHeadless || isNaN(Number(tileOffset))) {\n return undefined;\n }\n // Adding extra padding of 3px to ensure that the audio border is visible properly between tiles when tileOffset is 0.\n return Number(tileOffset) === 0 ? (hideAudioLevel ? 0 : 3) : undefined;\n};\n\nexport default VideoTile;\n", "const PEER_NAME_PLACEHOLDER = 'peerName';\nconst labelMap = new Map([\n [[true, 'screen'].toString(), 'Your Screen'],\n [[true, 'regular'].toString(), `You (${PEER_NAME_PLACEHOLDER})`],\n [[false, 'screen'].toString(), `${PEER_NAME_PLACEHOLDER}'s Screen`],\n [[false, 'regular'].toString(), PEER_NAME_PLACEHOLDER],\n [[true, undefined].toString(), `You (${PEER_NAME_PLACEHOLDER})`],\n [[false, undefined].toString(), `${PEER_NAME_PLACEHOLDER}`],\n]);\n\nexport const getVideoTileLabel = ({ peerName, isLocal, track }) => {\n const isPeerPresent = peerName !== undefined;\n if (!isPeerPresent || !track) {\n // for peers with only audio track\n return isPeerPresent ? labelMap.get([isLocal, undefined].toString()).replace(PEER_NAME_PLACEHOLDER, peerName) : '';\n }\n const isLocallyMuted = track.volume === 0;\n // Map [isLocal, videoSource] to the label to be displayed.\n let label = labelMap.get([isLocal, track.source].toString());\n if (label) {\n label = label.replace(PEER_NAME_PLACEHOLDER, peerName);\n } else {\n label = `${peerName} ${track.source}`;\n }\n label = `${label}${track.degraded ? '(Degraded)' : ''}`;\n return `${label}${isLocallyMuted ? ' (Muted for you)' : ''}`;\n};\n", "import React, { Fragment, useState } from 'react';\nimport {\n selectLocalPeerID,\n selectPermissions,\n selectSessionStore,\n selectTemplateAppData,\n selectTrackByID,\n selectVideoTrackByPeerID,\n useCustomEvent,\n useHMSActions,\n useHMSStore,\n useRemoteAVToggle,\n} from '@100mslive/react-sdk';\nimport {\n HorizontalMenuIcon,\n MicOffIcon,\n MicOnIcon,\n PinIcon,\n RemoveUserIcon,\n ShareScreenIcon,\n SpeakerIcon,\n StarIcon,\n VideoOffIcon,\n VideoOnIcon,\n} from '@100mslive/react-icons';\nimport { ToastManager } from './Toast/ToastManager';\nimport { Box, Flex } from '../../Layout';\nimport { Slider } from '../../Slider';\nimport { Text } from '../../Text';\nimport { StyledMenuTile } from '../../TileMenu';\nimport { useSetAppDataByKey } from './AppData/useUISettings';\nimport { useDropdownList } from './hooks/useDropdownList';\nimport { useDropdownSelection } from './hooks/useDropdownSelection';\nimport { useIsFeatureEnabled } from './hooks/useFeatures';\nimport { APP_DATA, FEATURE_LIST, REMOTE_STOP_SCREENSHARE_TYPE, SESSION_STORE_KEY } from '../common/constants';\n\nconst isSameTile = ({ trackId, videoTrackID, audioTrackID }) =>\n trackId && ((videoTrackID && videoTrackID === trackId) || (audioTrackID && audioTrackID === trackId));\n\nconst SpotlightActions = ({ peerId }) => {\n const hmsActions = useHMSActions();\n const spotlightPeerId = useHMSStore(selectSessionStore(SESSION_STORE_KEY.SPOTLIGHT));\n const isTileSpotlighted = spotlightPeerId === peerId;\n\n const setSpotlightPeerId = peer =>\n hmsActions.sessionStore\n .set(SESSION_STORE_KEY.SPOTLIGHT, peer)\n .catch(err => ToastManager.addToast({ title: err.description }));\n\n return (\n <StyledMenuTile.ItemButton onClick={() => (isTileSpotlighted ? setSpotlightPeerId() : setSpotlightPeerId(peerId))}>\n <StarIcon />\n <span>{isTileSpotlighted ? 'Remove from Spotlight' : 'Spotlight Tile for everyone'}</span>\n </StyledMenuTile.ItemButton>\n );\n};\n\nconst PinActions = ({ audioTrackID, videoTrackID }) => {\n const [pinnedTrackId, setPinnedTrackId] = useSetAppDataByKey(APP_DATA.pinnedTrackId);\n\n const isTilePinned = isSameTile({\n trackId: pinnedTrackId,\n videoTrackID,\n audioTrackID,\n });\n\n return (\n <>\n <StyledMenuTile.ItemButton\n onClick={() => (isTilePinned ? setPinnedTrackId() : setPinnedTrackId(videoTrackID || audioTrackID))}\n >\n <PinIcon />\n <span>{isTilePinned ? 'Unpin' : 'Pin'} Tile for myself</span>\n </StyledMenuTile.ItemButton>\n </>\n );\n};\n\n/**\n * Taking peerID as peer won't necesarilly have tracks\n */\nconst TileMenu = ({ audioTrackID, videoTrackID, peerID, isScreenshare = false }) => {\n const [open, setOpen] = useState(false);\n const actions = useHMSActions();\n const localPeerID = useHMSStore(selectLocalPeerID);\n const isLocal = localPeerID === peerID;\n const { removeOthers, changeRole } = useHMSStore(selectPermissions);\n const { isAudioEnabled, isVideoEnabled, setVolume, toggleAudio, toggleVideo, volume } = useRemoteAVToggle(\n audioTrackID,\n videoTrackID,\n );\n const showSpotlight = changeRole;\n const { sendEvent } = useCustomEvent({\n type: REMOTE_STOP_SCREENSHARE_TYPE,\n });\n\n const isPrimaryVideoTrack = useHMSStore(selectVideoTrackByPeerID(peerID))?.id === videoTrackID;\n const uiMode = useHMSStore(selectTemplateAppData).uiMode;\n const isInset = uiMode === 'inset';\n\n const isPinEnabled = useIsFeatureEnabled(FEATURE_LIST.PIN_TILE);\n const showPinAction = isPinEnabled && (audioTrackID || (videoTrackID && isPrimaryVideoTrack)) && !isInset;\n\n const track = useHMSStore(selectTrackByID(videoTrackID));\n const hideSimulcastLayers = !track?.layerDefinitions?.length || track.degraded || !track.enabled;\n\n useDropdownList({ open, name: 'TileMenu' });\n\n if (!(removeOthers || toggleAudio || toggleVideo || setVolume || showPinAction) && hideSimulcastLayers) {\n return null;\n }\n\n if (isInset && isLocal) {\n return null;\n }\n\n return (\n <StyledMenuTile.Root open={open} onOpenChange={setOpen}>\n <StyledMenuTile.Trigger data-testid=\"participant_menu_btn\" onClick={e => e.stopPropagation()}>\n <HorizontalMenuIcon />\n </StyledMenuTile.Trigger>\n <StyledMenuTile.Content side=\"top\" align=\"end\">\n {isLocal ? (\n showPinAction && (\n <>\n <PinActions audioTrackID={audioTrackID} videoTrackID={videoTrackID} />\n {showSpotlight && <SpotlightActions peerId={peerID} />}\n </>\n )\n ) : (\n <>\n {toggleVideo ? (\n <StyledMenuTile.ItemButton\n onClick={toggleVideo}\n data-testid={isVideoEnabled ? 'mute_video_participant_btn' : 'unmute_video_participant_btn'}\n >\n {isVideoEnabled ? <VideoOnIcon /> : <VideoOffIcon />}\n <span>{isVideoEnabled ? 'Mute' : 'Request Unmute'}</span>\n </StyledMenuTile.ItemButton>\n ) : null}\n {toggleAudio ? (\n <StyledMenuTile.ItemButton\n onClick={toggleAudio}\n data-testid={isVideoEnabled ? 'mute_audio_participant_btn' : 'unmute_audio_participant_btn'}\n >\n {isAudioEnabled ? <MicOnIcon /> : <MicOffIcon />}\n <span>{isAudioEnabled ? 'Mute' : 'Request Unmute'}</span>\n </StyledMenuTile.ItemButton>\n ) : null}\n {audioTrackID ? (\n <StyledMenuTile.VolumeItem data-testid=\"participant_volume_slider\">\n <Flex align=\"center\" gap={1}>\n <SpeakerIcon />\n <Box as=\"span\" css={{ ml: '$4' }}>\n Volume ({volume})\n </Box>\n </Flex>\n <Slider css={{ my: '0.5rem' }} step={5} value={[volume]} onValueChange={e => setVolume(e[0])} />\n </StyledMenuTile.VolumeItem>\n ) : null}\n {showPinAction && (\n <>\n <PinActions audioTrackID={audioTrackID} videoTrackID={videoTrackID} />\n {showSpotlight && <SpotlightActions peerId={peerID} />}\n </>\n )}\n <SimulcastLayers trackId={videoTrackID} />\n {removeOthers ? (\n <StyledMenuTile.RemoveItem\n onClick={async () => {\n try {\n await actions.removePeer(peerID, '');\n } catch (error) {\n // TODO: Toast here\n }\n }}\n data-testid=\"remove_participant_btn\"\n >\n <RemoveUserIcon />\n <span>Remove Participant</span>\n </StyledMenuTile.RemoveItem>\n ) : null}\n\n {removeOthers && isScreenshare ? (\n <StyledMenuTile.RemoveItem onClick={() => sendEvent({})}>\n <ShareScreenIcon />\n <span>Stop Screenshare</span>\n </StyledMenuTile.RemoveItem>\n ) : null}\n </>\n )}\n </StyledMenuTile.Content>\n </StyledMenuTile.Root>\n );\n};\n\nconst SimulcastLayers = ({ trackId }) => {\n const track = useHMSStore(selectTrackByID(trackId));\n const actions = useHMSActions();\n const bg = useDropdownSelection();\n if (!track?.layerDefinitions?.length || track.degraded || !track.enabled) {\n return null;\n }\n const currentLayer = track.layerDefinitions.find(layer => layer.layer === track.layer);\n return (\n <Fragment>\n <StyledMenuTile.ItemButton css={{ color: '$on_surface_medium', cursor: 'default' }}>\n Select maximum resolution\n </StyledMenuTile.ItemButton>\n {track.layerDefinitions.map(layer => {\n return (\n <StyledMenuTile.ItemButton\n key={layer.layer}\n onClick={async () => {\n await actions.setPreferredLayer(trackId, layer.layer);\n }}\n css={{\n justifyContent: 'space-between',\n bg: track.preferredLayer === layer.layer ? bg : undefined,\n '&:hover': {\n bg: track.preferredLayer === layer.layer ? bg : undefined,\n },\n }}\n >\n <Text\n as=\"span\"\n css={{\n textTransform: 'capitalize',\n mr: '$2',\n fontWeight: track.preferredLayer === layer.layer ? '$semiBold' : '$regular',\n }}\n >\n {layer.layer}\n </Text>\n <Text as=\"span\" variant=\"xs\" css={{ color: '$on_surface_medium' }}>\n {layer.resolution.width}x{layer.resolution.height}\n </Text>\n </StyledMenuTile.ItemButton>\n );\n })}\n <StyledMenuTile.ItemButton>\n <Text as=\"span\" variant=\"xs\" css={{ color: '$on_surface_medium' }}>\n Currently streaming:\n <Text\n as=\"span\"\n variant=\"xs\"\n css={{\n fontWeight: '$semiBold',\n textTransform: 'capitalize',\n color: '$on_surface_medium',\n ml: '$2',\n }}\n >\n {currentLayer ? (\n <>\n {track.layer} ({currentLayer.resolution.width}x{currentLayer.resolution.height})\n </>\n ) : (\n '-'\n )}\n </Text>\n </Text>\n </StyledMenuTile.ItemButton>\n </Fragment>\n );\n};\n\nexport default TileMenu;\n", "import { selectAppDataByPath, useHMSStore } from '@100mslive/react-sdk';\nimport { APP_DATA } from '../../common/constants';\n\nexport const useAppConfig = (...path) => {\n const appConfig = useHMSStore(selectAppDataByPath(APP_DATA.appConfig, ...path));\n return appConfig;\n};\n", "import React, { Fragment } from 'react';\nimport { useMedia } from 'react-use';\nimport { Box, Flex } from '../../Layout';\nimport { config as cssConfig } from '../../Theme';\nimport { FirstPersonDisplay } from './FirstPersonDisplay';\nimport { Image } from './Image';\nimport VideoList from './VideoList';\nimport { useAppConfig } from './AppData/useAppConfig';\nimport { useIsHeadless } from './AppData/useUISettings';\n\nconst MAX_TILES_FOR_MOBILE = 4;\n\n/**\n * the below variables are for showing webinar etc. related image if required on certain meeting urls\n */\nconst webinarProps = JSON.parse(process.env.REACT_APP_WEBINAR_PROPS || '{}');\nconst eventRoomIDs = webinarProps?.ROOM_IDS || [];\nconst eventsImg = webinarProps?.IMAGE_FILE || ''; // the image to show in center\n// the link to navigate to when user clicks on the image\nconst webinarInfoLink = webinarProps?.LINK_HREF || 'https://100ms.live/';\n\n// The center of the screen shows bigger tiles\nexport const GridCenterView = ({ peers, maxTileCount }) => {\n const mediaQueryLg = cssConfig.media.md;\n const limitMaxTiles = useMedia(mediaQueryLg);\n\n const headlessConfig = useAppConfig('headlessConfig');\n const isHeadless = useIsHeadless();\n return (\n <Fragment>\n <Box\n css={{\n flex: '1 1 0',\n height: '100%',\n mx: isHeadless && Number(headlessConfig?.tileOffset) === 0 ? '0' : '$8',\n '@md': { flex: '2 1 0' },\n }}\n >\n {peers && peers.length > 0 ? (\n <VideoList peers={peers} maxTileCount={limitMaxTiles ? MAX_TILES_FOR_MOBILE : maxTileCount} />\n ) : eventRoomIDs.some(id => window.location.href.includes(id)) ? (\n <Box\n css={{\n display: 'grid',\n placeItems: 'center',\n size: '100%',\n p: '$12',\n }}\n >\n <a href={webinarInfoLink} target=\"_blank\" rel=\"noreferrer\">\n <Image css={{ p: '$4', boxShadow: '$sm' }} alt=\"Event template\" src={eventsImg} />\n </a>\n </Box>\n ) : (\n <FirstPersonDisplay />\n )}\n </Box>\n </Fragment>\n );\n};\n\n// Side pane shows smaller tiles\nexport const GridSidePaneView = ({ peers }) => {\n const headlessConfig = useAppConfig('headlessConfig');\n const isHeadless = useIsHeadless();\n return (\n <Flex\n direction=\"column\"\n css={{\n flex: '0 0 20%',\n mx: isHeadless && Number(headlessConfig?.tileOffset) === 0 ? '0' : '$8',\n '@lg': {\n flex: '0 0 25%',\n },\n '@md': {\n flex: '1 1 0',\n },\n }}\n >\n <Flex css={{ flex: '1 1 0' }} align=\"end\">\n {peers && peers.length > 0 && <VideoList peers={peers} maxColCount={1} />}\n </Flex>\n </Flex>\n );\n};\n", "import React from 'react';\nimport { Box, Flex } from '../../Layout';\nimport { Text } from '../../Text';\nimport PlaceholderBg from '../images/first_person.png';\n\nexport const FirstPersonDisplay = React.memo(() => {\n return (\n <Box\n css={{\n position: 'relative',\n overflow: 'hidden',\n w: '37.5rem',\n maxWidth: '80%',\n h: '100%',\n r: '$3',\n m: '0 auto',\n backgroundImage: `url(${PlaceholderBg})`,\n backgroundSize: 'cover',\n backgroundRepeat: 'no-repeat',\n '@md': {\n w: '100%',\n maxWidth: 'unset',\n },\n }}\n data-testid=\"first_person_img\"\n >\n <Flex\n align=\"center\"\n direction=\"column\"\n css={{\n position: 'absolute',\n w: '100%',\n top: '33.33%',\n left: 0,\n textAlign: 'center',\n }}\n >\n <Text color=\"white\" variant=\"h4\" css={{ '@md': { fontSize: '$md' } }}>\n Welcome!\n </Text>\n <Text color=\"white\" variant=\"h6\" css={{ mt: '$4', '@md': { fontSize: '$sm' } }}>\n You\u2019re the first one here.\n </Text>\n <Text color=\"white\" variant=\"h6\" css={{ mt: '$2', '@md': { fontSize: '$sm' } }}>\n Sit back and relax till the others join.\n </Text>\n </Flex>\n </Box>\n );\n});\n", "import { styled } from '../../Theme';\n\nexport const Image = styled('img', {\n width: '100%',\n height: 'auto',\n r: '$3',\n});\n", "import React, { Fragment, useEffect, useState } from 'react';\nimport { selectLocalPeerID, useHMSStore, useVideoList } from '@100mslive/react-sdk';\nimport { useTheme } from '../../Theme';\nimport { StyledVideoList } from '../../VideoList';\nimport { Pagination } from './Pagination';\nimport ScreenshareTile from './ScreenshareTile';\nimport VideoTile from './VideoTile';\nimport useSortedPeers from '../common/useSortedPeers';\nimport { useAppConfig } from './AppData/useAppConfig';\nimport { useIsHeadless, useUISettings } from './AppData/useUISettings';\nimport { UI_SETTINGS } from '../common/constants';\n\nconst List = ({ maxTileCount, peers, maxColCount, maxRowCount, includeScreenShareForPeer }) => {\n const { aspectRatio } = useTheme();\n const tileOffset = useAppConfig('headlessConfig', 'tileOffset');\n const isHeadless = useIsHeadless();\n const hideLocalVideo = useUISettings(UI_SETTINGS.hideLocalVideo);\n const localPeerId = useHMSStore(selectLocalPeerID);\n let sortedPeers = useSortedPeers({ peers, maxTileCount });\n if (hideLocalVideo && sortedPeers.length > 1) {\n sortedPeers = filterPeerId(sortedPeers, localPeerId);\n }\n const { ref, pagesWithTiles } = useVideoList({\n peers: sortedPeers,\n maxTileCount,\n maxColCount,\n maxRowCount,\n includeScreenShareForPeer,\n aspectRatio,\n offsetY: getOffset({ isHeadless, tileOffset }),\n });\n const [page, setPage] = useState(0);\n useEffect(() => {\n // currentPageIndex should not exceed pages length\n if (page >= pagesWithTiles.length) {\n setPage(0);\n }\n }, [pagesWithTiles.length, page]);\n return (\n <StyledVideoList.Root ref={ref}>\n <StyledVideoList.Container css={{ flexWrap: 'wrap', placeContent: 'center' }}>\n {pagesWithTiles && pagesWithTiles.length > 0\n ? pagesWithTiles[page]?.map(tile => {\n if (tile.width === 0 || tile.height === 0) {\n return null;\n }\n return (\n <Fragment key={tile.track?.id || tile.peer.id}>\n {tile.track?.source === 'screen' ? (\n <ScreenshareTile width={tile.width} height={tile.height} peerId={tile.peer.id} />\n ) : (\n <VideoTile\n width={tile.width}\n height={tile.height}\n peerId={tile.peer?.id}\n trackId={tile.track?.id}\n />\n )}\n </Fragment>\n );\n })\n : null}\n </StyledVideoList.Container>\n {!isHeadless && pagesWithTiles.length > 1 ? (\n <Pagination page={page} setPage={setPage} numPages={pagesWithTiles.length} />\n ) : null}\n </StyledVideoList.Root>\n );\n};\n\nconst VideoList = React.memo(List);\n\n/**\n * returns a new array of peers with the peer with peerId removed,\n * keeps the reference same if peer is not found\n */\nfunction filterPeerId(peers, peerId) {\n const oldPeers = peers; // to keep the reference same if peer is not found\n let foundPeerToFilterOut = false;\n peers = [];\n for (let i = 0; i < oldPeers.length; i++) {\n if (oldPeers[i].id === peerId) {\n foundPeerToFilterOut = true;\n } else {\n peers.push(oldPeers[i]);\n }\n }\n if (!foundPeerToFilterOut) {\n peers = oldPeers;\n }\n return peers;\n}\n\nconst getOffset = ({ tileOffset, isHeadless }) => {\n if (!isHeadless || isNaN(Number(tileOffset))) {\n return 32;\n }\n return Number(tileOffset);\n};\n\nexport default VideoList;\n", "import React from 'react';\nimport { ChevronLeftIcon, ChevronRightIcon } from '@100mslive/react-icons';\nimport { StyledPagination } from '../../Pagination';\n\nexport const Pagination = ({ page, setPage, numPages }) => {\n const disableLeft = page === 0;\n const disableRight = page === numPages - 1;\n const nextPage = () => {\n setPage(Math.min(page + 1, numPages - 1));\n };\n const prevPage = () => {\n setPage(Math.max(page - 1, 0));\n };\n return (\n <StyledPagination.Root>\n <StyledPagination.Chevron disabled={disableLeft} onClick={prevPage}>\n <ChevronLeftIcon width={16} height={16} style={{ cursor: disableLeft ? 'not-allowed' : 'pointer' }} />\n </StyledPagination.Chevron>\n <StyledPagination.Dots>\n {[...Array(numPages)].map((_, i) => (\n <StyledPagination.Dot key={i} active={page === i} onClick={() => setPage(i)} />\n ))}\n </StyledPagination.Dots>\n <StyledPagination.Chevron disabled={disableRight} onClick={nextPage}>\n <ChevronRightIcon width={16} height={16} style={{ cursor: disableRight ? 'not-allowed' : 'pointer' }} />\n </StyledPagination.Chevron>\n </StyledPagination.Root>\n );\n};\n", "import React, { useRef, useState } from 'react';\nimport { useFullscreen } from 'react-use';\nimport screenfull from 'screenfull';\nimport {\n selectLocalPeerID,\n selectPeerByID,\n selectScreenShareAudioByPeerID,\n selectScreenShareByPeerID,\n useHMSStore,\n} from '@100mslive/react-sdk';\nimport { ExpandIcon, ShrinkIcon } from '@100mslive/react-icons';\nimport { VideoTileStats } from '../../Stats';\nimport { Video } from '../../Video';\nimport { StyledVideoTile } from '../../VideoTile';\nimport { getVideoTileLabel } from './peerTileUtils';\nimport TileMenu from './TileMenu';\nimport { useIsHeadless, useUISettings } from './AppData/useUISettings';\nimport { UI_SETTINGS } from '../common/constants';\n\nconst labelStyles = {\n position: 'unset',\n width: '100%',\n textAlign: 'center',\n transform: 'none',\n mt: '$2',\n flexShrink: 0,\n};\n\nconst Tile = ({ peerId, width = '100%', height = '100%' }) => {\n const isLocal = useHMSStore(selectLocalPeerID) === peerId;\n const track = useHMSStore(selectScreenShareByPeerID(peerId));\n const peer = useHMSStore(selectPeerByID(peerId));\n const isAudioOnly = useUISettings(UI_SETTINGS.isAudioOnly);\n const isHeadless = useIsHeadless();\n const [isMouseHovered, setIsMouseHovered] = useState(false);\n const showStatsOnTiles = useUISettings(UI_SETTINGS.showStatsOnTiles);\n const label = getVideoTileLabel({\n peerName: peer.name,\n isLocal: false,\n track,\n });\n const fullscreenRef = useRef(null);\n // fullscreen is for desired state\n const [fullscreen, setFullscreen] = useState(false);\n // isFullscreen is for true state\n const isFullscreen = useFullscreen(fullscreenRef, fullscreen, {\n onClose: () => setFullscreen(false),\n });\n const isFullScreenSupported = screenfull.isEnabled;\n const audioTrack = useHMSStore(selectScreenShareAudioByPeerID(peer?.id));\n return (\n <StyledVideoTile.Root css={{ width, height }} data-testid=\"screenshare_tile\">\n {peer ? (\n <StyledVideoTile.Container\n transparentBg\n ref={fullscreenRef}\n css={{ flexDirection: 'column' }}\n onMouseEnter={() => setIsMouseHovered(true)}\n onMouseLeave={() => {\n setIsMouseHovered(false);\n }}\n >\n {showStatsOnTiles ? (\n <VideoTileStats audioTrackID={audioTrack?.id} videoTrackID={track?.id} peerID={peerId} isLocal={isLocal} />\n ) : null}\n {isFullScreenSupported && !isHeadless ? (\n <StyledVideoTile.FullScreenButton onClick={() => setFullscreen(!fullscreen)}>\n {isFullscreen ? <ShrinkIcon /> : <ExpandIcon />}\n </StyledVideoTile.FullScreenButton>\n ) : null}\n {track ? (\n <Video\n screenShare={true}\n mirror={peer.isLocal && track?.source === 'regular'}\n attach={!isAudioOnly}\n trackId={track.id}\n />\n ) : null}\n <StyledVideoTile.Info css={labelStyles}>{label}</StyledVideoTile.Info>\n {isMouseHovered && !isHeadless && !peer?.isLocal ? (\n <TileMenu isScreenshare peerID={peer?.id} audioTrackID={audioTrack?.id} videoTrackID={track?.id} />\n ) : null}\n </StyledVideoTile.Container>\n ) : null}\n </StyledVideoTile.Root>\n );\n};\n\nconst ScreenshareTile = React.memo(Tile);\n\nexport default ScreenshareTile;\n", "import { useEffect, useRef, useState } from 'react';\nimport { useHMSVanillaStore } from '@100mslive/react-sdk';\nimport PeersSorter from './PeersSorter';\nimport { useActiveSpeakerSorting } from '../components/AppData/useUISettings';\n\nfunction useSortedPeers({ peers, maxTileCount = 9 }) {\n const [sortedPeers, setSortedPeers] = useState([]);\n const store = useHMSVanillaStore();\n const activeSpeakerSorting = useActiveSpeakerSorting();\n const peerSortedRef = useRef(new PeersSorter(store));\n peerSortedRef.current.onUpdate(setSortedPeers);\n\n useEffect(() => {\n const peersSorter = peerSortedRef.current;\n if (peers?.length > 0 && maxTileCount && activeSpeakerSorting) {\n peersSorter.setPeersAndTilesPerPage({\n peers,\n tilesPerPage: maxTileCount,\n });\n } else if (!activeSpeakerSorting) {\n peersSorter.stop();\n }\n }, [maxTileCount, peers, activeSpeakerSorting]);\n\n return activeSpeakerSorting ? sortedPeers : peers;\n}\n\nexport default useSortedPeers;\n", "import { selectDominantSpeaker } from '@100mslive/hms-video-store';\n\nclass PeersSorter {\n listeners = new Set();\n storeUnsubscribe;\n\n constructor(store) {\n this.store = store;\n this.peers = new Map();\n this.lruPeers = new Set();\n this.speaker = undefined;\n }\n\n setPeersAndTilesPerPage = ({ peers, tilesPerPage }) => {\n this.tilesPerPage = tilesPerPage;\n const peerIds = new Set(peers.map(peer => peer.id));\n // remove existing peers which are no longer provided\n this.peers.forEach((_, key) => {\n if (!peerIds.has(key)) {\n this.peers.delete(key);\n }\n });\n this.lruPeers = new Set([...this.lruPeers].filter(peerId => peerIds.has(peerId)));\n peers.forEach(peer => {\n this.peers.set(peer.id, peer);\n if (this.lruPeers.size < tilesPerPage) {\n this.lruPeers.add(peer.id);\n }\n });\n if (!this.storeUnsubscribe) {\n this.storeUnsubscribe = this.store.subscribe(this.onDominantSpeakerChange, selectDominantSpeaker);\n }\n this.moveSpeakerToFront(this.speaker);\n };\n\n onUpdate = cb => {\n this.listeners.add(cb);\n };\n\n stop = () => {\n this.updateListeners();\n this.listeners.clear();\n this.storeUnsubscribe?.();\n };\n\n moveSpeakerToFront = speaker => {\n if (!speaker) {\n this.updateListeners();\n return;\n }\n if (this.lruPeers.has(speaker.id) && this.lruPeers.size <= this.tilesPerPage) {\n this.updateListeners();\n return;\n }\n // delete to insert at beginning\n this.lruPeers.delete(speaker.id);\n let lruPeerArray = Array.from(this.lruPeers);\n while (lruPeerArray.length >= this.tilesPerPage) {\n lruPeerArray.pop();\n }\n this.lruPeers = new Set([speaker.id, ...lruPeerArray]);\n this.updateListeners();\n };\n\n onDominantSpeakerChange = speaker => {\n if (speaker && speaker.id !== this?.speaker?.id) {\n this.speaker = speaker;\n this.moveSpeakerToFront(speaker);\n }\n };\n\n updateListeners = () => {\n const orderedPeers = [];\n this.lruPeers.forEach(key => {\n const peer = this.peers.get(key);\n if (peer) {\n orderedPeers.push(peer);\n }\n });\n this.peers.forEach(peer => {\n if (!this.lruPeers.has(peer.id) && peer) {\n orderedPeers.push(peer);\n }\n });\n this.listeners.forEach(listener => listener?.(orderedPeers));\n };\n}\n\nexport default PeersSorter;\n", "function defaultEqualityCheck(a, b) {\n return a === b;\n}\n\nfunction areArgumentsShallowlyEqual(equalityCheck, prev, next) {\n if (prev === null || next === null || prev.length !== next.length) {\n return false;\n }\n\n // Do this in a for loop (and not a `forEach` or an `every`) so we can determine equality as fast as possible.\n var length = prev.length;\n for (var i = 0; i < length; i++) {\n if (!equalityCheck(prev[i], next[i])) {\n return false;\n }\n }\n\n return true;\n}\n\nexport function defaultMemoize(func) {\n var equalityCheck = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : defaultEqualityCheck;\n\n var lastArgs = null;\n var lastResult = null;\n // we reference arguments instead of spreading them for performance reasons\n return function () {\n if (!areArgumentsShallowlyEqual(equalityCheck, lastArgs, arguments)) {\n // apply arguments instead of spreading for performance.\n lastResult = func.apply(null, arguments);\n }\n\n lastArgs = arguments;\n return lastResult;\n };\n}\n\nfunction getDependencies(funcs) {\n var dependencies = Array.isArray(funcs[0]) ? funcs[0] : funcs;\n\n if (!dependencies.every(function (dep) {\n return typeof dep === 'function';\n })) {\n var dependencyTypes = dependencies.map(function (dep) {\n return typeof dep;\n }).join(', ');\n throw new Error('Selector creators expect all input-selectors to be functions, ' + ('instead received the following types: [' + dependencyTypes + ']'));\n }\n\n return dependencies;\n}\n\nexport function createSelectorCreator(memoize) {\n for (var _len = arguments.length, memoizeOptions = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {\n memoizeOptions[_key - 1] = arguments[_key];\n }\n\n return function () {\n for (var _len2 = arguments.length, funcs = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {\n funcs[_key2] = arguments[_key2];\n }\n\n var recomputations = 0;\n var resultFunc = funcs.pop();\n var dependencies = getDependencies(funcs);\n\n var memoizedResultFunc = memoize.apply(undefined, [function () {\n recomputations++;\n // apply arguments instead of spreading for performance.\n return resultFunc.apply(null, arguments);\n }].concat(memoizeOptions));\n\n // If a selector is called with the exact same arguments we don't need to traverse our dependencies again.\n var selector = memoize(function () {\n var params = [];\n var length = dependencies.length;\n\n for (var i = 0; i < length; i++) {\n // apply arguments instead of spreading and mutate a local list of params for performance.\n params.push(dependencies[i].apply(null, arguments));\n }\n\n // apply arguments instead of spreading for performance.\n return memoizedResultFunc.apply(null, params);\n });\n\n selector.resultFunc = resultFunc;\n selector.dependencies = dependencies;\n selector.recomputations = function () {\n return recomputations;\n };\n selector.resetRecomputations = function () {\n return recomputations = 0;\n };\n return selector;\n };\n}\n\nexport var createSelector = createSelectorCreator(defaultMemoize);\n\nexport function createStructuredSelector(selectors) {\n var selectorCreator = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : createSelector;\n\n if (typeof selectors !== 'object') {\n throw new Error('createStructuredSelector expects first argument to be an object ' + ('where each property is a selector, instead received a ' + typeof selectors));\n }\n var objectKeys = Object.keys(selectors);\n return selectorCreator(objectKeys.map(function (key) {\n return selectors[key];\n }), function () {\n for (var _len3 = arguments.length, values = Array(_len3), _key3 = 0; _key3 < _len3; _key3++) {\n values[_key3] = arguments[_key3];\n }\n\n return values.reduce(function (composition, value, index) {\n composition[objectKeys[index]] = value;\n return composition;\n }, {});\n });\n}", "{\n \"name\": \"@100mslive/hms-video\",\n \"version\": \"0.9.12\",\n \"license\": \"MIT\",\n \"main\": \"dist/index.cjs.js\",\n \"typings\": \"dist/index.d.ts\",\n \"module\": \"dist/index.js\",\n \"files\": [\n \"dist\",\n \"src\"\n ],\n \"engines\": {\n \"node\": \">=10\"\n },\n \"exports\": {\n \".\": {\n \"require\": \"./dist/index.cjs.js\",\n \"import\": \"./dist/index.js\",\n \"default\": \"./dist/index.js\"\n }\n },\n \"scripts\": {\n \"prestart\": \"rm -rf dist && yarn types:build\",\n \"start\": \"concurrently \\\"yarn dev\\\" \\\"yarn types\\\"\",\n \"dev\": \"node ../../scripts/dev\",\n \"build:only\": \"node ../../scripts/build\",\n \"build\": \"yarn build:only && yarn types:build\",\n \"types\": \"tsc -w\",\n \"types:build\": \"tsc -p tsconfig.json\",\n \"test\": \"jest --maxWorkers=1\",\n \"lint\": \"eslint -c ../../.eslintrc .\",\n \"lint:fix\": \"yarn lint --fix\",\n \"prepare\": \"yarn build\",\n \"size\": \"size-limit\",\n \"analyze\": \"size-limit --why\",\n \"format\": \"prettier --write src/**/*.ts\"\n },\n \"author\": \"100ms <tech-common@100ms.live>\",\n \"devDependencies\": {\n \"@types/dom-screen-wake-lock\": \"^1.0.1\",\n \"@types/sdp-transform\": \"^2.4.4\",\n \"@types/ua-parser-js\": \"^0.7.36\",\n \"@types/uuid\": \"^8.3.0\",\n \"jest-canvas-mock\": \"^2.3.1\",\n \"tslib\": \"^2.2.0\"\n },\n \"dependencies\": {\n \"eventemitter2\": \"^6.4.7\",\n \"sdp-transform\": \"^2.14.1\",\n \"ua-parser-js\": \"^1.0.1\",\n \"uuid\": \"^8.3.2\",\n \"webrtc-adapter\": \"^8.0.0\"\n }\n}\n", "import { HMSPeer } from './peer';\nimport { HMSRole } from '../../interfaces';\nimport { HMSMessage } from '../../interfaces/message';\nimport { SendMessage } from '../../notification-manager';\nimport { ISignalParamsProvider } from '../../signal/ISignalSendParamsProvider';\n\nexport default class Message implements HMSMessage, ISignalParamsProvider<SendMessage> {\n sender?: HMSPeer;\n recipientPeer?: HMSPeer;\n recipientRoles?: HMSRole[];\n message: any;\n time: Date;\n type: string;\n id?: string;\n\n constructor({ sender, message, type = 'chat', recipientPeer, recipientRoles, time, id }: HMSMessage) {\n this.sender = sender;\n this.message = message;\n this.type = type;\n this.recipientPeer = recipientPeer;\n this.recipientRoles = recipientRoles;\n this.time = time;\n this.id = id;\n }\n\n toSignalParams() {\n const roles = this.recipientRoles?.map(role => role.name);\n const peer = this.recipientPeer?.peerId;\n const sendParams: SendMessage = {\n info: {\n message: this.message,\n type: this.type,\n },\n };\n if (roles?.length) {\n sendParams.roles = roles;\n }\n if (peer) {\n sendParams.peer_id = peer;\n }\n return sendParams;\n }\n\n toString() {\n return `{\n sender: ${this.sender};\n recipientPeer: ${this.recipientPeer};\n recipientRoles: ${this.recipientRoles?.map(role => role.name)};\n message: ${this.message};\n time: ${this.time};\n type: ${this.type};\n id: ${this.id}\n }`;\n }\n}\n", "import { HMSHLS, HMSRecording, HMSRoom, HMSRTMP } from '../../interfaces/room';\n\nexport default class Room implements HMSRoom {\n id: string;\n joinedAt?: Date | undefined;\n templateId?: string | undefined;\n sessionId?: string;\n startedAt?: Date;\n recording: HMSRecording = { server: { running: false }, browser: { running: false }, hls: { running: false } };\n rtmp: HMSRTMP = { running: false };\n hls: HMSHLS = { running: false, variants: [] };\n name?: string;\n peerCount?: number;\n\n constructor(id: string) {\n this.id = id;\n }\n}\n", "import { HMSPeer as IHMSPeer } from '../../../interfaces/peer';\nimport { HMSRole } from '../../../interfaces/role';\nimport { HMSAudioTrack, HMSTrack, HMSVideoTrack } from '../../../media/tracks';\n\nexport type HMSPeerInit = {\n peerId: string;\n name: string;\n isLocal: boolean;\n customerUserId?: string;\n metadata?: string;\n role?: HMSRole;\n joinedAt?: Date;\n fromRoomState?: boolean;\n};\n\nexport class HMSPeer implements IHMSPeer {\n readonly peerId: string;\n readonly isLocal: boolean;\n name: string;\n customerUserId?: string = '';\n metadata?: string = '';\n audioTrack?: HMSAudioTrack;\n videoTrack?: HMSVideoTrack;\n auxiliaryTracks: HMSTrack[] = [];\n role?: HMSRole;\n joinedAt?: Date;\n\n constructor({ peerId, name, isLocal, customerUserId, metadata, role, joinedAt }: HMSPeerInit) {\n this.name = name;\n this.peerId = peerId;\n this.isLocal = isLocal;\n this.customerUserId = customerUserId;\n this.metadata = metadata;\n this.joinedAt = joinedAt;\n\n if (role) {\n this.role = role;\n }\n }\n\n /**\n * @internal\n */\n updateRole(newRole: HMSRole) {\n this.role = newRole;\n }\n /**\n * @internal\n */\n updateName(newName: string) {\n this.name = newName;\n }\n /**\n * @internal\n */\n updateMetadata(data: string) {\n this.metadata = data;\n }\n\n toString() {\n return `{\n name: ${this.name};\n role: ${this.role?.name};\n peerId: ${this.peerId};\n customerUserId: ${this.customerUserId};\n ${this.audioTrack ? `audioTrack: ${this.audioTrack?.trackId};` : ''}\n ${this.videoTrack ? `videoTrack: ${this.videoTrack?.trackId};` : ''}\n }`;\n }\n}\n", "import { v4 as uuidv4 } from 'uuid';\n\nexport default class HMSIdFactory {\n static makePeerId = () => uuidv4();\n}\n", "import { HMSPeer, HMSPeerInit } from './HMSPeer';\nimport { HMSLocalPeer as IHMSLocalPeer } from '../../../interfaces/peer';\nimport { HMSRole } from '../../../interfaces/role';\nimport { HMSLocalAudioTrack, HMSLocalTrack, HMSLocalVideoTrack } from '../../../media/tracks';\nimport HMSIdFactory from '../../../utils/id-factory';\n\ntype HMSLocalPeerInit = Omit<HMSPeerInit, 'isLocal' | 'peerId'> & { asRole?: HMSRole };\n\nexport class HMSLocalPeer extends HMSPeer implements IHMSLocalPeer {\n isLocal = true;\n declare audioTrack?: HMSLocalAudioTrack;\n declare videoTrack?: HMSLocalVideoTrack;\n auxiliaryTracks: HMSLocalTrack[] = [];\n asRole?: HMSRole;\n\n constructor(peerData: HMSLocalPeerInit) {\n super({ ...peerData, peerId: HMSIdFactory.makePeerId(), isLocal: true });\n this.asRole = peerData.asRole;\n }\n\n isInPreview() {\n return !!this.asRole;\n }\n\n toString(): string {\n return `{\n name: ${this.name};\n role: ${this.role?.name};\n peerId: ${this.peerId};\n customerUserId: ${this.customerUserId};\n ${this.asRole ? `asRole: ${this.asRole.name};` : ''}\n ${this.audioTrack ? `audioTrack: ${this.audioTrack?.trackId};` : ''}\n ${this.videoTrack ? `videoTrack: ${this.videoTrack?.trackId};` : ''}\n }`;\n }\n}\n", "import { HMSPeer, HMSPeerInit } from './HMSPeer';\nimport { HMSRemotePeer as IHMSRemotePeer } from '../../../interfaces/peer';\nimport { HMSRemoteAudioTrack, HMSRemoteTrack, HMSRemoteVideoTrack } from '../../../media/tracks';\n\ntype HMSRemotePeerInit = Omit<HMSPeerInit, 'isLocal'>;\n\nexport class HMSRemotePeer extends HMSPeer implements IHMSRemotePeer {\n isLocal = false;\n declare audioTrack?: HMSRemoteAudioTrack;\n declare videoTrack?: HMSRemoteVideoTrack;\n auxiliaryTracks: HMSRemoteTrack[] = [];\n fromRoomState = false;\n\n constructor(peerData: HMSRemotePeerInit) {\n super({ ...peerData, isLocal: false });\n this.fromRoomState = !!peerData.fromRoomState;\n }\n}\n", "import { v4 as uuid } from 'uuid';\nimport { IStore } from './store';\nimport AnalyticsEventFactory from '../analytics/AnalyticsEventFactory';\nimport { AnalyticsTimer, TimedEvent } from '../analytics/AnalyticsTimer';\nimport { DeviceManager } from '../device-manager';\nimport { ErrorCodes } from '../error/ErrorCodes';\nimport { ErrorFactory } from '../error/ErrorFactory';\nimport { HMSAction } from '../error/HMSAction';\nimport { HMSException } from '../error/HMSException';\nimport { BuildGetMediaError, HMSGetMediaActions } from '../error/utils';\nimport { EventBus } from '../events/EventBus';\nimport { HMSAudioCodec, HMSScreenShareConfig, HMSVideoCodec, ScreenCaptureHandleConfig } from '../interfaces';\nimport InitialSettings from '../interfaces/settings';\nimport { HMSLocalAudioTrack, HMSLocalTrack, HMSLocalVideoTrack, HMSTrackType } from '../internal';\nimport {\n HMSAudioTrackSettings,\n HMSAudioTrackSettingsBuilder,\n HMSTrackSettings,\n HMSTrackSettingsBuilder,\n HMSVideoTrackSettings,\n HMSVideoTrackSettingsBuilder,\n} from '../media/settings';\nimport { HMSLocalStream } from '../media/streams/HMSLocalStream';\nimport { IFetchAVTrackOptions } from '../transport/ITransport';\nimport ITransportObserver from '../transport/ITransportObserver';\nimport HMSLogger from '../utils/logger';\nimport { HMSAudioContextHandler } from '../utils/media';\n\nconst defaultSettings = {\n isAudioMuted: false,\n isVideoMuted: false,\n audioInputDeviceId: 'default',\n audioOutputDeviceId: 'default',\n videoDeviceId: 'default',\n};\n\nlet blankCanvas: HTMLCanvasElement | undefined;\nlet intervalID: ReturnType<typeof setInterval> | undefined;\n\nexport class LocalTrackManager {\n readonly TAG: string = '[LocalTrackManager]';\n private captureHandleIdentifier?: string;\n\n constructor(\n private store: IStore,\n private observer: ITransportObserver,\n private deviceManager: DeviceManager,\n private eventBus: EventBus,\n private analyticsTimer: AnalyticsTimer,\n ) {\n this.setScreenCaptureHandleConfig();\n }\n\n // eslint-disable-next-line complexity\n async getTracksToPublish(initialSettings: InitialSettings = defaultSettings): Promise<HMSLocalTrack[]> {\n const trackSettings = this.getAVTrackSettings(initialSettings);\n if (!trackSettings) {\n return [];\n }\n const canPublishAudio = !!trackSettings.audio;\n const canPublishVideo = !!trackSettings.video;\n let tracksToPublish: Array<HMSLocalTrack> = [];\n const { videoTrack, audioTrack } = await this.updateCurrentLocalTrackSettings(trackSettings);\n const localStream = (videoTrack?.stream || audioTrack?.stream) as HMSLocalStream | undefined;\n // The track gets added to the store only after it is published.\n const isVideoTrackPublished = Boolean(videoTrack && this.store.getTrackById(videoTrack.trackId));\n const isAudioTrackPublished = Boolean(audioTrack && this.store.getTrackById(audioTrack.trackId));\n\n if (isVideoTrackPublished && isAudioTrackPublished) {\n // there is nothing to publish\n return [];\n }\n\n const fetchTrackOptions: IFetchAVTrackOptions = {\n audio: canPublishAudio && !audioTrack && (initialSettings.isAudioMuted ? 'empty' : true),\n video: canPublishVideo && !videoTrack && (initialSettings.isVideoMuted ? 'empty' : true),\n };\n\n if (fetchTrackOptions.audio) {\n this.analyticsTimer.start(TimedEvent.LOCAL_AUDIO_TRACK);\n }\n if (fetchTrackOptions.video) {\n this.analyticsTimer.start(TimedEvent.LOCAL_VIDEO_TRACK);\n }\n try {\n HMSLogger.d(this.TAG, 'Init Local Tracks', { fetchTrackOptions });\n tracksToPublish = await this.getLocalTracks(fetchTrackOptions, trackSettings, localStream);\n } catch (error) {\n tracksToPublish = await this.retryGetLocalTracks(\n error as HMSException,\n trackSettings,\n fetchTrackOptions,\n localStream,\n );\n }\n if (fetchTrackOptions.audio) {\n this.analyticsTimer.end(TimedEvent.LOCAL_AUDIO_TRACK);\n }\n if (fetchTrackOptions.video) {\n this.analyticsTimer.end(TimedEvent.LOCAL_VIDEO_TRACK);\n }\n\n if (videoTrack && canPublishVideo && !isVideoTrackPublished) {\n tracksToPublish.push(videoTrack);\n }\n if (audioTrack && canPublishAudio && !isAudioTrackPublished) {\n tracksToPublish.push(audioTrack);\n }\n return tracksToPublish;\n }\n\n /**\n * @throws {HMSException}\n */\n async getLocalTracks(\n fetchTrackOptions: IFetchAVTrackOptions = { audio: true, video: true },\n settings: HMSTrackSettings,\n localStream?: HMSLocalStream,\n ): Promise<Array<HMSLocalTrack>> {\n try {\n const nativeTracks = await this.getNativeLocalTracks(fetchTrackOptions, settings);\n return this.createHMSLocalTracks(nativeTracks, settings, localStream);\n } catch (error) {\n // TOOD: On OverConstrained error, retry with dropping all constraints.\n // Just retry getusermedia again - it sometimes work when AbortError or NotFoundError is thrown on a few devices\n this.eventBus.analytics.publish(\n AnalyticsEventFactory.publish({\n devices: this.deviceManager.getDevices(),\n error: error as Error,\n settings,\n }),\n );\n throw error;\n }\n }\n\n /**\n * @throws {HMSException}\n */\n private async getNativeLocalTracks(\n fetchTrackOptions: IFetchAVTrackOptions = { audio: false, video: false },\n settings: HMSTrackSettings,\n ) {\n const trackSettings = new HMSTrackSettings(\n fetchTrackOptions.video === true ? settings.video : null,\n fetchTrackOptions.audio === true ? settings.audio : null,\n settings.simulcast,\n );\n const nativeTracks: MediaStreamTrack[] = [];\n\n if (trackSettings.audio || trackSettings.video) {\n nativeTracks.push(...(await this.getAVTracks(trackSettings)));\n }\n nativeTracks.push(...this.getEmptyTracks(fetchTrackOptions));\n return nativeTracks;\n }\n\n async getLocalScreen(partialConfig?: HMSScreenShareConfig) {\n const config = await this.getOrDefaultScreenshareConfig(partialConfig);\n const screenSettings = this.getScreenshareSettings(config.videoOnly);\n const constraints = {\n video: { ...screenSettings?.video.toConstraints(true), displaySurface: config.displaySurface },\n preferCurrentTab: config.preferCurrentTab,\n selfBrowserSurface: config.selfBrowserSurface,\n surfaceSwitching: config.surfaceSwitching,\n systemAudio: config.systemAudio,\n } as MediaStreamConstraints;\n if (screenSettings?.audio) {\n const audioConstraints: MediaTrackConstraints = screenSettings?.audio?.toConstraints();\n // remove advanced constraints as it not supported for screenshare audio\n delete audioConstraints.advanced;\n constraints.audio = {\n ...audioConstraints,\n autoGainControl: false,\n noiseSuppression: false,\n // @ts-ignore\n googAutoGainControl: false,\n echoCancellation: false,\n };\n }\n let stream;\n try {\n HMSLogger.d('retrieving screenshare with ', { config }, { constraints });\n // @ts-ignore [https://github.com/microsoft/TypeScript/issues/33232]\n stream = (await navigator.mediaDevices.getDisplayMedia(constraints)) as MediaStream;\n } catch (err) {\n HMSLogger.w(this.TAG, 'error in getting screenshare - ', err);\n const error = BuildGetMediaError(err as Error, HMSGetMediaActions.SCREEN);\n this.eventBus.analytics.publish(\n AnalyticsEventFactory.publish({\n error: error as Error,\n devices: this.deviceManager.getDevices(),\n settings: new HMSTrackSettings(screenSettings?.video, screenSettings?.audio, false),\n }),\n );\n throw error;\n }\n\n const tracks: Array<HMSLocalTrack> = [];\n const local = new HMSLocalStream(stream);\n const nativeVideoTrack = stream.getVideoTracks()[0];\n const videoTrack = new HMSLocalVideoTrack(local, nativeVideoTrack, 'screen', this.eventBus, screenSettings?.video);\n videoTrack.setSimulcastDefinitons(this.store.getSimulcastDefinitionsForPeer(this.store.getLocalPeer()!, 'screen'));\n\n try {\n const isCurrentTabShared = this.validateCurrentTabCapture(videoTrack, config.forceCurrentTab);\n videoTrack.isCurrentTab = isCurrentTabShared;\n await videoTrack.cropTo(config.cropTarget);\n } catch (err) {\n stream.getTracks().forEach(track => track.stop());\n throw err;\n }\n\n tracks.push(videoTrack);\n const nativeAudioTrack = stream.getAudioTracks()[0];\n if (nativeAudioTrack) {\n const audioTrack = new HMSLocalAudioTrack(\n local,\n nativeAudioTrack,\n 'screen',\n this.eventBus,\n screenSettings?.audio,\n );\n tracks.push(audioTrack);\n }\n\n HMSLogger.v(this.TAG, 'getLocalScreen', tracks);\n return tracks;\n }\n\n setScreenCaptureHandleConfig(config?: Partial<ScreenCaptureHandleConfig>) {\n // @ts-ignore\n if (!navigator.mediaDevices?.setCaptureHandleConfig || this.isInIframe()) {\n // setCaptureHandleConfig can't be called from within an iframe\n return;\n }\n config = config || {};\n Object.assign(config, { handle: uuid(), exposeOrigin: false, permittedOrigins: [window.location.origin] });\n HMSLogger.d('setting capture handle - ', config.handle);\n // @ts-ignore\n navigator.mediaDevices.setCaptureHandleConfig(config);\n this.captureHandleIdentifier = config.handle;\n }\n\n validateCurrentTabCapture(track: HMSLocalVideoTrack, forceCurrentTab: boolean): boolean {\n const trackHandle = track.getCaptureHandle();\n const isCurrentTabShared = !!(this.captureHandleIdentifier && trackHandle?.handle === this.captureHandleIdentifier);\n if (forceCurrentTab && !isCurrentTabShared) {\n HMSLogger.e(this.TAG, 'current tab was not shared with forceCurrentTab as true');\n throw ErrorFactory.TracksErrors.CurrentTabNotShared();\n }\n return isCurrentTabShared;\n }\n\n async requestPermissions() {\n try {\n const stream = await navigator.mediaDevices.getUserMedia({\n audio: true,\n video: true,\n });\n // Stop stream\n stream.getTracks().forEach(track => track.stop());\n } catch (error) {\n HMSLogger.e(this.TAG, error);\n }\n }\n\n static getEmptyVideoTrack(prevTrack?: MediaStreamTrack): MediaStreamTrack {\n const width = prevTrack?.getSettings()?.width || 320;\n const height = prevTrack?.getSettings()?.height || 240;\n const frameRate = 1; // fps TODO: experiment, see if this can be reduced\n if (!blankCanvas) {\n blankCanvas = document.createElement('canvas');\n blankCanvas.width = width;\n blankCanvas.height = height;\n blankCanvas.getContext('2d')?.fillRect(0, 0, width, height);\n }\n if (!intervalID) {\n // This is needed to send some data so the track is received on sfu\n intervalID = setInterval(() => {\n const ctx = blankCanvas?.getContext('2d');\n if (ctx) {\n ctx.fillRect(0, 0, 1, 1);\n }\n }, 1000 / frameRate);\n }\n\n const stream = blankCanvas.captureStream(frameRate);\n const emptyTrack = stream.getVideoTracks()[0];\n emptyTrack.enabled = false;\n return emptyTrack;\n }\n\n static getEmptyAudioTrack(): MediaStreamTrack {\n const ctx = HMSAudioContextHandler.getAudioContext();\n const oscillator = ctx.createOscillator();\n const dst = ctx.createMediaStreamDestination();\n oscillator.connect(dst);\n oscillator.start();\n const emptyTrack = dst.stream.getAudioTracks()[0];\n emptyTrack.enabled = false;\n return emptyTrack;\n }\n\n static cleanup() {\n clearInterval(intervalID);\n intervalID = undefined;\n blankCanvas = undefined;\n }\n\n /**\n * @throws {HMSException}\n */\n private async getAVTracks(settings: HMSTrackSettings): Promise<Array<MediaStreamTrack>> {\n try {\n const stream = await navigator.mediaDevices.getUserMedia({\n audio: settings.audio ? settings.audio.toConstraints() : false,\n video: settings.video ? settings.video.toConstraints() : false,\n });\n\n return stream.getVideoTracks().concat(stream.getAudioTracks());\n } catch (error) {\n await this.deviceManager.init();\n const videoError = !!(!this.deviceManager.hasWebcamPermission && settings.video);\n const audioError = !!(!this.deviceManager.hasMicrophonePermission && settings.audio);\n /**\n * TODO: Only permission error throws correct device info in error(audio or video or both),\n * Right now for other errors such as overconstrained error we are unable to get whether audio/video failure.\n * Fix this by checking the native error message.\n */\n const errorType = this.getErrorType(videoError, audioError);\n throw BuildGetMediaError(error as Error, errorType);\n }\n }\n\n private getAVTrackSettings(initialSettings: InitialSettings): HMSTrackSettings | null {\n const audioSettings = this.getAudioSettings(initialSettings);\n const videoSettings = this.getVideoSettings(initialSettings);\n if (!audioSettings && !videoSettings) {\n return null;\n }\n return new HMSTrackSettingsBuilder().video(videoSettings).audio(audioSettings).build();\n }\n\n private isInIframe() {\n try {\n return window.self !== window.top;\n } catch (e) {\n return true;\n }\n }\n\n // eslint-disable-next-line complexity\n private async retryGetLocalTracks(\n error: HMSException,\n trackSettings: HMSTrackSettings,\n fetchTrackOptions: IFetchAVTrackOptions,\n localStream?: HMSLocalStream,\n ): Promise<Array<HMSLocalTrack>> {\n if (error instanceof HMSException && error.action === HMSAction.TRACK) {\n this.observer.onFailure(error);\n\n const overConstrainedFailure = error.code === ErrorCodes.TracksErrors.OVER_CONSTRAINED;\n const audioFailure = error.message.includes('audio');\n const videoFailure = error.message.includes('video');\n if (overConstrainedFailure) {\n // TODO: Use this once TODO@L#250 is completed\n // const newTrackSettings = new HMSTrackSettingsBuilder()\n // .video(videoFailure ? new HMSVideoTrackSettings() : trackSettings.video)\n // .audio(audioFailure ? new HMSAudioTrackSettings() : trackSettings.audio)\n // .build();\n const newTrackSettings = new HMSTrackSettingsBuilder()\n .video(new HMSVideoTrackSettings())\n .audio(new HMSAudioTrackSettings())\n .build();\n\n HMSLogger.w(this.TAG, 'Fetch AV Tracks failed with overconstrained error', { fetchTrackOptions }, { error });\n\n try {\n // Try get local tracks with no constraints\n return await this.getLocalTracks(fetchTrackOptions, newTrackSettings, localStream);\n } catch (error) {\n /**\n * This error shouldn't be overconstrained error(as we've dropped all constraints).\n * If it's an overconstrained error, change error code to avoid recursive loop\n * Try get local tracks for empty tracks\n */\n const nativeError: Error | undefined = error instanceof HMSException ? error.nativeError : (error as Error);\n let ex = error;\n if (nativeError?.name === 'OverconstrainedError') {\n const newError = ErrorFactory.TracksErrors.GenericTrack(\n HMSAction.TRACK,\n 'Overconstrained error after dropping all constraints',\n );\n newError.addNativeError(nativeError);\n ex = newError;\n }\n\n return await this.retryGetLocalTracks(ex as HMSException, trackSettings, fetchTrackOptions, localStream);\n }\n }\n\n fetchTrackOptions.audio = audioFailure ? 'empty' : fetchTrackOptions.audio;\n fetchTrackOptions.video = videoFailure ? 'empty' : fetchTrackOptions.video;\n HMSLogger.w(this.TAG, 'Fetch AV Tracks failed', { fetchTrackOptions }, error);\n try {\n return await this.getLocalTracks(fetchTrackOptions, trackSettings, localStream);\n } catch (error) {\n HMSLogger.w(this.TAG, 'Fetch empty tacks failed', error);\n fetchTrackOptions.audio = fetchTrackOptions.audio && 'empty';\n fetchTrackOptions.video = fetchTrackOptions.video && 'empty';\n this.observer.onFailure(error as HMSException);\n return await this.getLocalTracks(fetchTrackOptions, trackSettings, localStream);\n }\n } else {\n HMSLogger.w(this.TAG, 'Fetch AV Tracks failed - unknown exception', error);\n this.observer.onFailure(error);\n return [];\n }\n }\n\n private getErrorType(videoError: boolean, audioError: boolean): HMSGetMediaActions {\n if (videoError && audioError) {\n return HMSGetMediaActions.AV;\n }\n if (videoError) {\n return HMSGetMediaActions.VIDEO;\n }\n if (audioError) {\n return HMSGetMediaActions.AUDIO;\n }\n return HMSGetMediaActions.UNKNOWN;\n }\n\n private getEmptyTracks(fetchTrackOptions: IFetchAVTrackOptions) {\n const nativeTracks: MediaStreamTrack[] = [];\n if (fetchTrackOptions.audio === 'empty') {\n nativeTracks.push(LocalTrackManager.getEmptyAudioTrack());\n }\n\n if (fetchTrackOptions.video === 'empty') {\n nativeTracks.push(LocalTrackManager.getEmptyVideoTrack());\n }\n return nativeTracks;\n }\n\n private async updateCurrentLocalTrackSettings(trackSettings: HMSTrackSettings | null) {\n const localTracks = this.store.getLocalPeerTracks();\n const videoTrack = localTracks.find(t => t.type === HMSTrackType.VIDEO && t.source === 'regular') as\n | HMSLocalVideoTrack\n | undefined;\n const audioTrack = localTracks.find(t => t.type === HMSTrackType.AUDIO && t.source === 'regular') as\n | HMSLocalAudioTrack\n | undefined;\n\n const screenVideoTrack = localTracks.find(t => t.type === HMSTrackType.VIDEO && t.source === 'screen') as\n | HMSLocalVideoTrack\n | undefined;\n\n if (trackSettings?.video) {\n await videoTrack?.setSettings(trackSettings.video);\n }\n\n if (trackSettings?.audio) {\n await audioTrack?.setSettings(trackSettings.audio);\n }\n\n const screenSettings = this.getScreenshareSettings(true);\n if (screenSettings?.video) {\n await screenVideoTrack?.setSettings(screenSettings?.video);\n }\n\n return { videoTrack, audioTrack };\n }\n\n private getAudioSettings(initialSettings: InitialSettings) {\n const publishParams = this.store.getPublishParams();\n if (!publishParams || !publishParams.allowed?.includes('audio')) {\n return null;\n }\n const localPeer = this.store.getLocalPeer();\n const audioTrack = localPeer?.audioTrack;\n // Get device from the tracks already added in preview\n const audioDeviceId = audioTrack?.settings.deviceId || initialSettings.audioInputDeviceId;\n\n return new HMSAudioTrackSettingsBuilder()\n .codec(publishParams.audio.codec as HMSAudioCodec)\n .maxBitrate(publishParams.audio.bitRate)\n .deviceId(audioDeviceId || defaultSettings.audioInputDeviceId)\n .build();\n }\n\n private getVideoSettings(initialSettings: InitialSettings) {\n const publishParams = this.store.getPublishParams();\n if (!publishParams || !publishParams.allowed?.includes('video')) {\n return null;\n }\n const localPeer = this.store.getLocalPeer();\n const videoTrack = localPeer?.videoTrack;\n // Get device from the tracks already added in preview\n const videoDeviceId = videoTrack?.settings.deviceId || initialSettings.videoDeviceId;\n const video = publishParams.video;\n return new HMSVideoTrackSettingsBuilder()\n .codec(video.codec as HMSVideoCodec)\n .maxBitrate(video.bitRate)\n .maxFramerate(video.frameRate)\n .setWidth(video.width) // take simulcast width if available\n .setHeight(video.height) // take simulcast width if available\n .deviceId(videoDeviceId || defaultSettings.videoDeviceId)\n .build();\n }\n\n private getScreenshareSettings(isVideoOnly = false) {\n const publishParams = this.store.getPublishParams();\n if (!publishParams || !publishParams.allowed?.includes('screen')) {\n return null;\n }\n const screen = publishParams.screen;\n return {\n video: new HMSVideoTrackSettingsBuilder()\n // Don't cap maxBitrate for screenshare.\n // If publish params doesn't have bitRate value - don't set maxBitrate.\n .maxBitrate(screen.bitRate, false)\n .codec(screen.codec as HMSVideoCodec)\n .maxFramerate(screen.frameRate)\n .setWidth(screen.width)\n .setHeight(screen.height)\n .build(),\n audio: isVideoOnly ? undefined : new HMSAudioTrackSettingsBuilder().build(),\n };\n }\n\n // eslint-disable-next-line complexity\n private async getOrDefaultScreenshareConfig(partialConfig?: Partial<HMSScreenShareConfig>) {\n type RequiredConfig = HMSScreenShareConfig &\n Required<Omit<HMSScreenShareConfig, 'cropTarget' | 'cropElement' | 'displaySurface'>>;\n const config: RequiredConfig = Object.assign(\n {\n videoOnly: false,\n audioOnly: false,\n forceCurrentTab: false,\n preferCurrentTab: false,\n selfBrowserSurface: 'exclude', // don't give self tab in options\n surfaceSwitching: 'include', // give option to switch tabs while sharing\n systemAudio: 'exclude', // system audio share leads to echo in windows\n displaySurface: 'monitor',\n },\n partialConfig || {},\n );\n if (config.forceCurrentTab) {\n config.videoOnly = true; // there will be echo otherwise\n config.preferCurrentTab = true;\n config.selfBrowserSurface = 'include';\n config.surfaceSwitching = 'exclude';\n }\n if (config.preferCurrentTab) {\n config.selfBrowserSurface = 'include';\n config.displaySurface = undefined; // so the default selected is the current tab\n }\n // @ts-ignore\n if (config.cropElement && window.CropTarget?.fromElement) {\n // @ts-ignore\n config.cropTarget = await window.CropTarget.fromElement(config.cropElement);\n }\n return config;\n }\n\n private createHMSLocalTracks(\n nativeTracks: MediaStreamTrack[],\n settings: HMSTrackSettings,\n localStream?: HMSLocalStream,\n ) {\n const nativeVideoTrack = nativeTracks.find(track => track.kind === 'video');\n const nativeAudioTrack = nativeTracks.find(track => track.kind === 'audio');\n if (localStream) {\n nativeTracks.forEach(track => localStream?.nativeStream.addTrack(track));\n } else {\n localStream = new HMSLocalStream(new MediaStream(nativeTracks));\n }\n\n const tracks: Array<HMSLocalTrack> = [];\n if (nativeAudioTrack && settings?.audio) {\n const audioTrack = new HMSLocalAudioTrack(\n localStream,\n nativeAudioTrack,\n 'regular',\n this.eventBus,\n settings.audio,\n );\n tracks.push(audioTrack);\n }\n\n if (nativeVideoTrack && settings?.video) {\n const videoTrack = new HMSLocalVideoTrack(\n localStream,\n nativeVideoTrack,\n 'regular',\n this.eventBus,\n settings.video,\n );\n videoTrack.setSimulcastDefinitons(\n this.store.getSimulcastDefinitionsForPeer(this.store.getLocalPeer()!, 'regular'),\n );\n tracks.push(videoTrack);\n }\n return tracks;\n }\n}\n", "import { v4 as uuid } from 'uuid';\nimport { AnalyticsEventLevel } from './AnalyticsEventLevel';\nimport { domainCategory } from './domain-analytics';\nimport { ISignalParamsProvider } from '../signal/ISignalSendParamsProvider';\nimport { getAnalyticsDeviceId } from '../utils/analytics-deviceId';\nimport { createUserAgent } from '../utils/user-agent';\n\ninterface AnalyticsEventInit {\n name: string;\n level: AnalyticsEventLevel;\n includesPII?: boolean;\n properties?: Record<string, any>;\n timestamp?: number;\n}\n\ninterface SignalEventParams {\n name: string;\n info: any;\n timestamp: number;\n}\n\nexport default class AnalyticsEvent implements ISignalParamsProvider<SignalEventParams> {\n name: string;\n level: AnalyticsEventLevel;\n includesPII: boolean;\n properties: Record<string, any>;\n metadata: {\n token?: string;\n peer: {\n peer_id?: string;\n session_id?: string;\n room_id?: string;\n role?: string;\n room_name?: string;\n joined_at?: number;\n template_id?: string;\n session_started_at?: number;\n user_name?: string;\n user_data?: string;\n };\n userAgent: string;\n } = {\n peer: {},\n userAgent: createUserAgent(),\n };\n timestamp: number;\n event_id: string;\n device_id: string;\n\n constructor({ name, level, properties, includesPII, timestamp }: AnalyticsEventInit) {\n this.name = name;\n this.level = level;\n this.includesPII = includesPII || false;\n this.properties = properties || {};\n this.timestamp = timestamp || new Date().getTime(); // Timestamp of generating the event\n this.event_id = uuid();\n this.device_id = getAnalyticsDeviceId();\n }\n\n toSignalParams() {\n return {\n name: this.name,\n info: { ...this.properties, timestamp: this.timestamp, domain: domainCategory },\n timestamp: new Date().getTime(), // Timestamp of sending the event\n };\n }\n}\n", "import { UAParser } from 'ua-parser-js';\n\nexport const parsedUserAgent = new UAParser();\n\nexport const isBrowser = typeof window !== 'undefined';\n\nexport const isNode =\n typeof window === 'undefined' && !parsedUserAgent.getBrowser().name?.toLowerCase().includes('electron');\n\nexport enum ENV {\n PROD = 'prod',\n QA = 'qa',\n DEV = 'dev',\n}\n\nconst checkIsSupported = () => {\n if (isNode) {\n return false;\n }\n // @TODO: Get this from preview/init API from server\n return true;\n};\n\nexport const isSupported = checkIsSupported();\n\nexport const isMobile = () => parsedUserAgent.getDevice().type === 'mobile';\n\nexport const isPageHidden = () => typeof document !== 'undefined' && document.hidden;\n\nexport const isIOS = () => parsedUserAgent.getOS().name?.toLowerCase() === 'ios';\n", "import { DomainCategory } from './AnalyticsEventDomains';\nimport { isBrowser } from '../utils/support';\n\nfunction getDomainCategory() {\n // this function gives us the domain category(sutom, hms , local) of the base url.\n // below if statement checks if it's running in a browser ; or if we can use 'window' safely\n\n if (isBrowser && window) {\n const baseurl = window.location.hostname;\n\n if (baseurl === 'localhost' || baseurl === '127.0.0.1') {\n return DomainCategory.LOCAL;\n }\n\n if (baseurl.includes('app.100ms.live')) {\n return DomainCategory.HMS;\n } else {\n return DomainCategory.CUSTOM;\n }\n }\n\n return DomainCategory.CUSTOM;\n}\n\nexport const domainCategory = getDomainCategory();\n", "import { v4 as uuid } from 'uuid';\nimport { LocalStorage } from './local-storage';\n\nexport const getAnalyticsDeviceId = () => {\n let id;\n const storage = new LocalStorage<string>('hms-analytics-deviceId');\n const storageId = storage.get();\n if (storageId) {\n id = storageId;\n } else {\n id = uuid();\n storage.set(id);\n }\n return id;\n};\n", "import { isBrowser } from './support';\n\nclass LocalStorage {\n valuesMap = new Map();\n getItem(key: string) {\n if (this.valuesMap.has(key)) {\n return String(this.valuesMap.get(key));\n }\n return null;\n }\n\n setItem(key: string, val: string) {\n this.valuesMap.set(key, val);\n }\n\n removeItem(key: string) {\n this.valuesMap.delete(key);\n }\n\n clear() {\n this.valuesMap.clear();\n }\n\n key(i: number) {\n if (arguments.length === 0) {\n throw new TypeError(\"Failed to execute 'key' on 'Storage': 1 argument required, but only 0 present.\"); // this is a TypeError implemented on Chrome, Firefox throws Not enough arguments to Storage.key.\n }\n const arr = Array.from(this.valuesMap.keys());\n return arr[i];\n }\n\n get length() {\n return this.valuesMap.size;\n }\n}\n\nexport const initializeLocalstoragePolyfill = () => {\n if (isBrowser && !localStorage) {\n window.localStorage = new LocalStorage();\n }\n};\n", "import { initializeLocalstoragePolyfill } from './local-storage-polyfill';\nimport { isBrowser } from './support';\n\nexport class LocalStorage<T> {\n private storage: Storage | null = null;\n\n constructor(public readonly key: string) {}\n\n /**\n * localstorage is not available in SSR, so get it only at time of use\n */\n getStorage() {\n if (isBrowser && !this.storage) {\n initializeLocalstoragePolyfill();\n this.storage = window.localStorage;\n }\n return this.storage;\n }\n\n get(): T | undefined {\n const stringItem = this.getStorage()?.getItem(this.key);\n if (!stringItem) {\n return;\n }\n const item = JSON.parse(stringItem) as T;\n return item;\n }\n\n set(value: T) {\n const stringValue = JSON.stringify(value);\n this.getStorage()?.setItem(this.key, stringValue);\n }\n\n clear() {\n this.getStorage()?.removeItem(this.key);\n }\n}\n", "export enum HMSLogLevel {\n VERBOSE,\n DEBUG,\n INFO,\n WARN,\n TIME,\n TIMEEND,\n ERROR,\n NONE,\n}\n\n// @ts-ignore - window.expect is available only when in test environment\nconst isTestEnv = typeof window !== 'undefined' && typeof window.expect !== 'undefined';\n/**\n * TODO: fix this so logs show the real file and line numbers where they originated from instead of this class\n * https://stackoverflow.com/questions/13815640/a-proper-wrapper-for-console-log-with-correct-line-number\n */\nexport default class HMSLogger {\n static level: HMSLogLevel = isTestEnv ? HMSLogLevel.NONE : HMSLogLevel.VERBOSE;\n\n static v(tag: string, ...data: any[]) {\n this.log(HMSLogLevel.VERBOSE, tag, ...data);\n }\n\n static d(tag: string, ...data: any[]) {\n this.log(HMSLogLevel.DEBUG, tag, ...data);\n }\n\n static i(tag: string, ...data: any[]) {\n this.log(HMSLogLevel.INFO, tag, ...data);\n }\n\n static w(tag: string, ...data: any[]) {\n this.log(HMSLogLevel.WARN, tag, ...data);\n }\n\n static e(tag: string, ...data: any[]) {\n this.log(HMSLogLevel.ERROR, tag, ...data);\n }\n\n static time(mark: string) {\n this.log(HMSLogLevel.TIME, '[HMSPerformanceTiming]', mark);\n }\n\n static timeEnd(mark: string) {\n this.log(HMSLogLevel.TIMEEND, '[HMSPerformanceTiming]', mark, mark);\n }\n\n static cleanup() {\n performance.clearMarks();\n performance.clearMeasures();\n }\n\n // eslint-disable-next-line complexity\n private static log(level: HMSLogLevel, tag: string, ...data: any[]) {\n if (this.level.valueOf() > level.valueOf()) {\n return;\n }\n\n switch (level) {\n case HMSLogLevel.VERBOSE: {\n console.log(tag, ...data);\n break;\n }\n case HMSLogLevel.DEBUG: {\n console.debug(tag, ...data);\n break;\n }\n case HMSLogLevel.INFO: {\n console.info(tag, ...data);\n break;\n }\n case HMSLogLevel.WARN: {\n console.warn(tag, ...data);\n break;\n }\n case HMSLogLevel.ERROR: {\n console.error(tag, ...data);\n break;\n }\n case HMSLogLevel.TIME: {\n performance.mark(data[0]);\n break;\n }\n case HMSLogLevel.TIMEEND: {\n const mark = data[0];\n try {\n const entry = performance.measure(mark, mark);\n // @ts-ignore\n this.log(HMSLogLevel.DEBUG, tag, mark, entry?.duration);\n performance.clearMarks(mark);\n performance.clearMeasures(mark);\n } catch (error) {\n this.log(HMSLogLevel.DEBUG, tag, mark, error);\n }\n break;\n }\n }\n }\n}\n", "/*\n * ErrorCodes.ts\n *\n * Created by codegen\n * Copyright \u00A9 2021 100ms. All rights reserved.\n */\n\nexport const ErrorCodes = {\n // https://www.rfc-editor.org/rfc/rfc6455#section-7.4.1\n WebSocketConnectionErrors: {\n // Error connecting to ws or init config not available\n FAILED_TO_CONNECT: 1000,\n\n // Network connection lost\n WEBSOCKET_CONNECTION_LOST: 1003,\n\n // Abnormal close without receiving a Close control frame\n ABNORMAL_CLOSE: 1006,\n },\n\n APIErrors: {\n // [INIT]: Server error\n SERVER_ERRORS: 2000,\n //init config not available\n INIT_CONFIG_NOT_AVAILABLE: 2002,\n\n // Endpoint is not reachable.\n ENDPOINT_UNREACHABLE: 2003,\n\n // Token is not in proper JWT format\n INVALID_TOKEN_FORMAT: 2004,\n },\n\n TracksErrors: {\n // [PUBLISH]: Error with getusermedia request\n GENERIC_TRACK: 3000,\n\n // [PUBLISH]: No permission to access capture device - {device_type}\n CANT_ACCESS_CAPTURE_DEVICE: 3001,\n\n // [PUBLISH]: Capture device is no longer available - {device_type}\n DEVICE_NOT_AVAILABLE: 3002,\n\n // [PUBLISH]: Capture device is in use by another application - {device_type}\n DEVICE_IN_USE: 3003,\n\n // Lost access to capture device midway - {device_type}\n DEVICE_LOST_MIDWAY: 3008,\n\n // There is no media to return. Please select either video or audio or both.\n NOTHING_TO_RETURN: 3005,\n\n // Cannot enable simulcast when no video settings are provided\n INVALID_VIDEO_SETTINGS: 3006,\n\n // Codec can't be changed mid call.\n CODEC_CHANGE_NOT_PERMITTED: 3007,\n\n // When the browser throws autoplay exception if something is played before interacting\n AUTOPLAY_ERROR: 3008,\n\n // Over constrained error - device hardware unable to satisfy requested constraints\n OVER_CONSTRAINED: 3009,\n\n // No audio detected from track, indicates problem with device hardware\n NO_AUDIO_DETECTED: 3010,\n\n // Operating System denied permission\n SYSTEM_DENIED_PERMISSION: 3011,\n\n // Current tab is not shared when forceCurrentTab was set to true for screenshare\n CURRENT_TAB_NOT_SHARED: 3012,\n\n // any error that occurs while playing audio of remote audio tracks\n AUDIO_PLAYBACK_ERROR: 3013,\n },\n\n WebrtcErrors: {\n // [{action}]: Failed to create offer.\n CREATE_OFFER_FAILED: 4001,\n\n // [{action}]: Failed to create answer.\n CREATE_ANSWER_FAILED: 4002,\n\n // [{action}]: Failed to set offer.\n SET_LOCAL_DESCRIPTION_FAILED: 4003,\n\n // [{action}]: Failed to set answer.\n SET_REMOTE_DESCRIPTION_FAILED: 4004,\n\n // [{action}]: Ice connection state FAILED\n ICE_FAILURE: 4005,\n\n // [{action}]: Ice connection state FAILED\n ICE_DISCONNECTED: 4006,\n\n STATS_FAILED: 4007,\n },\n\n WebsocketMethodErrors: {\n // [JOIN]: {server_error}\n SERVER_ERRORS: 5000,\n\n // [JOIN]: You have already joined this room.\n ALREADY_JOINED: 5001,\n\n // [JOIN]: Cannot join if preview is in progress\n CANNOT_JOIN_PREVIEW_IN_PROGRESS: 5002,\n },\n\n GenericErrors: {\n // Client is not connected\n NOT_CONNECTED: 6000,\n\n // Unknown signalling error: {action} {error_info}\n SIGNALLING: 6001,\n\n // Unknown exception: {error_info}\n UNKNOWN: 6002,\n\n // WebRTC engine is not ready yet\n NOT_READY: 6003,\n\n // Failed to parse JSON message - {json_message}\n JSON_PARSING_FAILED: 6004,\n\n // Track Metadata Missing\n TRACK_METADATA_MISSING: 6005,\n\n // RTC Track missing\n RTC_TRACK_MISSING: 6006,\n\n // Peer Metadata Missing\n PEER_METADATA_MISSING: 6007,\n\n // Joined with invalid role\n INVALID_ROLE: 6008,\n\n PREVIEW_IN_PROGRESS: 6009,\n\n MISSING_MEDIADEVICES: 6010,\n\n MISSING_RTCPEERCONNECTION: 6011,\n },\n\n PlaylistErrors: {\n NO_ENTRY_TO_PLAY: 8001,\n NO_ENTRY_IS_PLAYING: 8002,\n },\n};\n", "import { HMSAction } from './HMSAction';\nimport { IAnalyticsPropertiesProvider } from '../analytics/IAnalyticsPropertiesProvider';\nimport { HMSSignalMethod } from '../signal/jsonrpc/models';\n\nexport class HMSException extends Error implements IAnalyticsPropertiesProvider {\n action: string;\n nativeError?: Error;\n\n constructor(\n public readonly code: number,\n public name: string,\n action: HMSAction | HMSSignalMethod,\n public message: string,\n public description: string,\n public isTerminal: boolean = false,\n ) {\n super(message);\n\n // Ref: https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#extending-built-ins-like-error-array-and-map-may-no-longer-work\n Object.setPrototypeOf(this, HMSException.prototype);\n this.action = action.toString();\n }\n\n toAnalyticsProperties() {\n return {\n error_name: this.name,\n error_code: this.code,\n error_message: this.message,\n error_description: this.description,\n action: this.action,\n is_terminal: this.isTerminal,\n };\n }\n\n addNativeError(error: Error) {\n this.nativeError = error;\n }\n\n toString() {\n return `{\n code: ${this.code};\n name: ${this.name};\n action: ${this.action};\n message: ${this.message};\n description: ${this.description};\n isTerminal: ${this.isTerminal};\n nativeError: ${this.nativeError?.message};\n }`;\n }\n}\n", "import { HMSAction } from '../../error/HMSAction';\n\nexport interface JsonRpcRequest {\n id: string;\n method: string;\n params: Map<string, any>;\n}\n\nexport interface JsonRpcResponse {\n id: string;\n result: any;\n error: {\n code: number;\n message: string;\n };\n}\n\nexport enum HMSSignalMethod {\n JOIN = 'join',\n OFFER = 'offer',\n ANSWER = 'answer',\n TRICKLE = 'trickle',\n TRACK_UPDATE = 'track-update',\n BROADCAST = 'broadcast',\n ANALYTICS = 'analytics',\n SERVER_ERROR = 'on-error',\n SERVER_WARNING = 'on-warning',\n SDK_NOTIFICATION = 'sdk-notification',\n LEAVE = 'leave',\n END_ROOM = 'end-room',\n PING = 'ping',\n ROLE_CHANGE_REQUEST = 'role-change-request',\n ROLE_CHANGE = 'role-change',\n TRACK_UPDATE_REQUEST = 'track-update-request',\n PEER_LEAVE_REQUEST = 'peer-leave-request',\n CHANGE_TRACK_MUTE_STATE_REQUEST = 'change-track-mute-state-request',\n START_RTMP_OR_RECORDING_REQUEST = 'rtmp-start',\n STOP_RTMP_AND_RECORDING_REQUEST = 'rtmp-stop',\n UPDATE_PEER_METADATA = 'peer-update',\n START_HLS_STREAMING = 'hls-start',\n STOP_HLS_STREAMING = 'hls-stop',\n HLS_TIMED_METADATA = 'hls-timed-metadata',\n SET_METADATA = 'set-metadata',\n GET_METADATA = 'get-metadata',\n LISTEN_METADATA_CHANGE = 'listen-metadata-change',\n POLL_INFO_SET = 'poll-info-set',\n POLL_INFO_GET = 'poll-info-get',\n POLL_QUESTIONS_SET = 'poll-questions-set',\n POLL_QUESTIONS_GET = 'poll-questions-get',\n POLL_START = 'poll-start',\n POLL_STOP = 'poll-stop',\n POLL_RESPONSE_SET = 'poll-response',\n POLL_LIST = 'poll-list',\n POLL_RESPONSES = 'poll-responses',\n POLL_RESULT = 'poll-result',\n}\n\nexport function convertSignalMethodtoErrorAction(method: HMSSignalMethod): HMSAction {\n switch (method) {\n case HMSSignalMethod.JOIN:\n return HMSAction.JOIN;\n case HMSSignalMethod.OFFER:\n return HMSAction.PUBLISH;\n case HMSSignalMethod.ANSWER:\n return HMSAction.SUBSCRIBE;\n case HMSSignalMethod.TRACK_UPDATE:\n return HMSAction.TRACK;\n default:\n return HMSAction.NONE;\n }\n}\n", "/*\n * ErrorFactory.ts\n *\n * Created by codegen\n * Copyright \u00A9 2021 100ms. All rights reserved.\n */\n\nimport { ErrorCodes } from './ErrorCodes';\nimport { HMSAction } from './HMSAction';\nimport { HMSException } from './HMSException';\nimport { HMSSignalMethod } from '../signal/jsonrpc/models';\n\nconst terminalActions: (HMSSignalMethod | HMSAction)[] = [\n HMSSignalMethod.JOIN,\n HMSSignalMethod.OFFER,\n HMSSignalMethod.ANSWER,\n HMSSignalMethod.TRICKLE,\n HMSSignalMethod.SERVER_ERROR,\n HMSAction.JOIN,\n];\n\nexport const ErrorFactory = {\n WebSocketConnectionErrors: {\n FailedToConnect(action: HMSAction, description = '') {\n return new HMSException(\n ErrorCodes.WebSocketConnectionErrors.FAILED_TO_CONNECT,\n 'WebsocketFailedToConnect',\n action,\n `[WS]: ${description}`,\n `[WS]: ${description}`,\n );\n },\n\n WebSocketConnectionLost(action: HMSAction, description = '') {\n return new HMSException(\n ErrorCodes.WebSocketConnectionErrors.WEBSOCKET_CONNECTION_LOST,\n 'WebSocketConnectionLost',\n action,\n `Network connection lost `,\n description,\n );\n },\n\n AbnormalClose(action: HMSAction, description = '') {\n return new HMSException(\n ErrorCodes.WebSocketConnectionErrors.ABNORMAL_CLOSE,\n 'WebSocketAbnormalClose',\n action,\n `Websocket closed abnormally`,\n description,\n );\n },\n },\n\n APIErrors: {\n ServerErrors(code: number, action: HMSAction, description = '', isTerminal = true) {\n return new HMSException(\n code,\n 'ServerErrors',\n action,\n `[${action}]: Server error ${description}`,\n description,\n isTerminal,\n );\n },\n\n EndpointUnreachable(action: HMSAction, description = '') {\n return new HMSException(\n ErrorCodes.APIErrors.ENDPOINT_UNREACHABLE,\n 'EndpointUnreachable',\n action,\n `Endpoint is not reachable - ${description}`,\n description,\n );\n },\n\n InvalidTokenFormat(action: HMSAction, description = '') {\n return new HMSException(\n ErrorCodes.APIErrors.INVALID_TOKEN_FORMAT,\n 'InvalidTokenFormat',\n action,\n `Token is not in proper JWT format - ${description}`,\n description,\n true,\n );\n },\n\n InitConfigNotAvailable(action: HMSAction, description = '') {\n return new HMSException(\n ErrorCodes.APIErrors.INIT_CONFIG_NOT_AVAILABLE,\n 'InitError',\n action,\n `[INIT]: ${description}`,\n `[INIT]: ${description}`,\n );\n },\n },\n\n TracksErrors: {\n GenericTrack(action: HMSAction, description = '') {\n return new HMSException(\n ErrorCodes.TracksErrors.GENERIC_TRACK,\n 'GenericTrack',\n action,\n `[TRACK]: ${description}`,\n `[TRACK]: ${description}`,\n );\n },\n\n CantAccessCaptureDevice(action: HMSAction, deviceInfo: string, description = '') {\n return new HMSException(\n ErrorCodes.TracksErrors.CANT_ACCESS_CAPTURE_DEVICE,\n 'CantAccessCaptureDevice',\n action,\n `User denied permission to access capture device - ${deviceInfo}`,\n description,\n );\n },\n\n DeviceNotAvailable(action: HMSAction, deviceInfo: string, description = '') {\n return new HMSException(\n ErrorCodes.TracksErrors.DEVICE_NOT_AVAILABLE,\n 'DeviceNotAvailable',\n action,\n `[TRACK]: Capture device is no longer available - ${deviceInfo}`,\n description,\n );\n },\n\n DeviceInUse(action: HMSAction, deviceInfo: string, description = '') {\n return new HMSException(\n ErrorCodes.TracksErrors.DEVICE_IN_USE,\n 'DeviceInUse',\n action,\n `[TRACK]: Capture device is in use by another application - ${deviceInfo}`,\n description,\n );\n },\n\n DeviceLostMidway(action: HMSAction, deviceInfo: string, description = '') {\n return new HMSException(\n ErrorCodes.TracksErrors.DEVICE_LOST_MIDWAY,\n 'DeviceLostMidway',\n action,\n `Lost access to capture device midway - ${deviceInfo}`,\n description,\n );\n },\n\n NothingToReturn(\n action: HMSAction,\n description = '',\n message = `There is no media to return. Please select either video or audio or both.`,\n ) {\n return new HMSException(\n ErrorCodes.TracksErrors.NOTHING_TO_RETURN,\n 'NothingToReturn',\n action,\n message,\n description,\n );\n },\n\n InvalidVideoSettings(action: HMSAction, description = '') {\n return new HMSException(\n ErrorCodes.TracksErrors.INVALID_VIDEO_SETTINGS,\n 'InvalidVideoSettings',\n action,\n `Cannot enable simulcast when no video settings are provided`,\n description,\n );\n },\n\n AutoplayBlocked(action: HMSAction, description = '') {\n return new HMSException(\n ErrorCodes.TracksErrors.AUTOPLAY_ERROR,\n 'AutoplayBlocked',\n action,\n \"Autoplay blocked because the user didn't interact with the document first\",\n description,\n );\n },\n\n CodecChangeNotPermitted(action: HMSAction, description = '') {\n return new HMSException(\n ErrorCodes.TracksErrors.CODEC_CHANGE_NOT_PERMITTED,\n 'CodecChangeNotPermitted',\n action,\n `Codec can't be changed mid call.`,\n description,\n );\n },\n\n OverConstrained(action: HMSAction, deviceInfo: string, description = '') {\n return new HMSException(\n ErrorCodes.TracksErrors.OVER_CONSTRAINED,\n 'OverConstrained',\n action,\n `[TRACK]: Requested constraints cannot be satisfied with the device hardware - ${deviceInfo}`,\n description,\n );\n },\n\n NoAudioDetected(action: HMSAction, description = 'Please check the mic or use another audio input') {\n return new HMSException(\n ErrorCodes.TracksErrors.NO_AUDIO_DETECTED,\n 'NoAudioDetected',\n action,\n 'No audio input detected from microphone',\n description,\n );\n },\n\n SystemDeniedPermission(action: HMSAction, deviceInfo: string, description = '') {\n return new HMSException(\n ErrorCodes.TracksErrors.SYSTEM_DENIED_PERMISSION,\n 'SystemDeniedPermission',\n action,\n `Operating System denied permission to access capture device - ${deviceInfo}`,\n description,\n );\n },\n\n CurrentTabNotShared() {\n return new HMSException(\n ErrorCodes.TracksErrors.CURRENT_TAB_NOT_SHARED,\n 'CurrentTabNotShared',\n HMSAction.TRACK,\n 'The app requires you to share the current tab',\n 'You must screen share the current tab in order to proceed',\n );\n },\n\n AudioPlaybackError(description: string) {\n return new HMSException(\n ErrorCodes.TracksErrors.AUDIO_PLAYBACK_ERROR,\n 'Audio playback error',\n HMSAction.TRACK,\n description,\n description,\n );\n },\n },\n\n WebrtcErrors: {\n CreateOfferFailed(action: HMSAction, description = '') {\n return new HMSException(\n ErrorCodes.WebrtcErrors.CREATE_OFFER_FAILED,\n 'CreateOfferFailed',\n action,\n `[${action.toString()}]: Failed to create offer. `,\n description,\n );\n },\n\n CreateAnswerFailed(action: HMSAction, description = '') {\n return new HMSException(\n ErrorCodes.WebrtcErrors.CREATE_ANSWER_FAILED,\n 'CreateAnswerFailed',\n action,\n `[${action.toString()}]: Failed to create answer. `,\n description,\n );\n },\n\n SetLocalDescriptionFailed(action: HMSAction, description = '') {\n return new HMSException(\n ErrorCodes.WebrtcErrors.SET_LOCAL_DESCRIPTION_FAILED,\n 'SetLocalDescriptionFailed',\n action,\n `[${action.toString()}]: Failed to set offer. `,\n description,\n );\n },\n\n SetRemoteDescriptionFailed(action: HMSAction, description = '') {\n return new HMSException(\n ErrorCodes.WebrtcErrors.SET_REMOTE_DESCRIPTION_FAILED,\n 'SetRemoteDescriptionFailed',\n action,\n `[${action.toString()}]: Failed to set answer. `,\n description,\n true,\n );\n },\n\n ICEFailure(action: HMSAction, description = '') {\n return new HMSException(\n ErrorCodes.WebrtcErrors.ICE_FAILURE,\n 'ICEFailure',\n action,\n `[${action.toString()}]: Ice connection state FAILED`,\n description,\n );\n },\n\n ICEDisconnected(action: HMSAction, description = '') {\n return new HMSException(\n ErrorCodes.WebrtcErrors.ICE_DISCONNECTED,\n 'ICEDisconnected',\n action,\n `[${action.toString()}]: Ice connection state DISCONNECTED`,\n description,\n );\n },\n\n StatsFailed(action: HMSAction, description = '') {\n return new HMSException(\n ErrorCodes.WebrtcErrors.STATS_FAILED,\n 'StatsFailed',\n action,\n `Failed to WebRTC get stats - ${description}`,\n description,\n );\n },\n },\n\n WebsocketMethodErrors: {\n ServerErrors(code: number, action: HMSAction | HMSSignalMethod, description: string) {\n return new HMSException(code, 'ServerErrors', action, description, description, terminalActions.includes(action));\n },\n\n AlreadyJoined(action: HMSAction, description = '') {\n return new HMSException(\n ErrorCodes.WebsocketMethodErrors.ALREADY_JOINED,\n 'AlreadyJoined',\n action,\n `[JOIN]: You have already joined this room.`,\n description,\n );\n },\n\n CannotJoinPreviewInProgress(action: HMSAction, description = '') {\n return new HMSException(\n ErrorCodes.WebsocketMethodErrors.CANNOT_JOIN_PREVIEW_IN_PROGRESS,\n 'CannotJoinPreviewInProgress',\n action,\n `[JOIN]: Cannot join if preview is in progress`,\n description,\n );\n },\n },\n\n GenericErrors: {\n NotConnected(action: HMSAction, description = '') {\n return new HMSException(\n ErrorCodes.GenericErrors.NOT_CONNECTED,\n 'NotConnected',\n action,\n `Client is not connected`,\n description,\n );\n },\n\n Signalling(action: HMSAction, description: string) {\n return new HMSException(\n ErrorCodes.GenericErrors.SIGNALLING,\n 'Signalling',\n action,\n `Unknown signalling error: ${action.toString()} ${description} `,\n description,\n );\n },\n\n Unknown(action: HMSAction, description: string) {\n return new HMSException(\n ErrorCodes.GenericErrors.UNKNOWN,\n 'Unknown',\n action,\n `Unknown exception: ${description}`,\n description,\n );\n },\n\n NotReady(action: HMSAction, description = '') {\n return new HMSException(ErrorCodes.GenericErrors.NOT_READY, 'NotReady', action, description, description);\n },\n\n JsonParsingFailed(action: HMSAction, jsonMessage: string, description = '') {\n return new HMSException(\n ErrorCodes.GenericErrors.JSON_PARSING_FAILED,\n 'JsonParsingFailed',\n action,\n `Failed to parse JSON message - ${jsonMessage}`,\n description,\n );\n },\n\n TrackMetadataMissing(action: HMSAction, description = '') {\n return new HMSException(\n ErrorCodes.GenericErrors.TRACK_METADATA_MISSING,\n 'TrackMetadataMissing',\n action,\n `Track Metadata Missing`,\n description,\n );\n },\n\n RTCTrackMissing(action: HMSAction, description = '') {\n return new HMSException(\n ErrorCodes.GenericErrors.RTC_TRACK_MISSING,\n 'RTCTrackMissing',\n action,\n `RTC Track missing`,\n description,\n );\n },\n\n PeerMetadataMissing(action: HMSAction, description = '') {\n return new HMSException(\n ErrorCodes.GenericErrors.PEER_METADATA_MISSING,\n 'PeerMetadataMissing',\n action,\n `Peer Metadata Missing`,\n description,\n );\n },\n\n ValidationFailed(message: string, entity?: any) {\n return new HMSException(\n ErrorCodes.GenericErrors.INVALID_ROLE,\n 'ValidationFailed',\n HMSAction.VALIDATION,\n message,\n entity ? JSON.stringify(entity) : '',\n );\n },\n\n InvalidRole(action: HMSAction, description: string) {\n return new HMSException(\n ErrorCodes.GenericErrors.INVALID_ROLE,\n 'InvalidRole',\n action,\n `Invalid role. Join with valid role`,\n description,\n true,\n );\n },\n\n PreviewAlreadyInProgress(action: HMSAction, description = '') {\n return new HMSException(\n ErrorCodes.GenericErrors.PREVIEW_IN_PROGRESS,\n 'PreviewAlreadyInProgress',\n action,\n `[Preview]: Cannot join if preview is in progress`,\n description,\n );\n },\n\n MissingMediaDevices() {\n return new HMSException(\n ErrorCodes.GenericErrors.MISSING_MEDIADEVICES,\n 'MissingMediaDevices',\n HMSAction.JOIN,\n `navigator.mediaDevices is undefined. 100ms SDK won't work on this website as WebRTC is not supported on HTTP endpoints(missing navigator.mediaDevices). Please ensure you're using the SDK either on localhost or a valid HTTPS endpoint.`,\n '',\n true,\n );\n },\n\n MissingRTCPeerConnection() {\n return new HMSException(\n ErrorCodes.GenericErrors.MISSING_RTCPEERCONNECTION,\n 'MissingRTCPeerConnection',\n HMSAction.JOIN,\n `RTCPeerConnection which is a core requirement for WebRTC call was not found, this could be due to an unsupported browser or browser extensions blocking WebRTC`,\n '',\n true,\n );\n },\n },\n\n MediaPluginErrors: {\n PlatformNotSupported(action: HMSAction, description = '') {\n return new HMSException(\n 7001,\n 'PlatformNotSupported',\n action,\n 'Check HMS Docs to see the list of supported platforms',\n description,\n );\n },\n\n InitFailed(action: HMSAction, description = '') {\n return new HMSException(7002, 'InitFailed', action, 'Plugin init failed', description);\n },\n\n ProcessingFailed(action: HMSAction, description = '') {\n return new HMSException(7003, 'ProcessingFailed', action, 'Plugin processing failed', description);\n },\n\n AddAlreadyInProgress(action: HMSAction, description = '') {\n return new HMSException(7004, 'AddAlreadyInProgress', action, 'Plugin add already in progress', description);\n },\n\n DeviceNotSupported(action: HMSAction, description = '') {\n return new HMSException(\n 7005,\n 'DeviceNotSupported',\n action,\n 'Check HMS Docs to see the list of supported devices',\n description,\n );\n },\n },\n\n PlaylistErrors: {\n NoEntryToPlay(action: HMSAction, description: string) {\n return new HMSException(\n ErrorCodes.PlaylistErrors.NO_ENTRY_TO_PLAY,\n 'NoEntryToPlay',\n action,\n 'Reached end of playlist',\n description,\n );\n },\n NoEntryPlaying(action: HMSAction, description: string) {\n return new HMSException(\n ErrorCodes.PlaylistErrors.NO_ENTRY_IS_PLAYING,\n 'NoEntryIsPlaying',\n action,\n 'No entry is playing at this time',\n description,\n );\n },\n },\n};\n", "import HMSLogger from './logger';\nimport { ErrorFactory } from '../error/ErrorFactory';\n\nconst TAG = `[VALIDATIONS]`;\n\n/**\n * Check only for presence(not truthy) of a value.\n * Use in places where 0, false need to be considered valid.\n */\nexport function isPresent(value: any) {\n return value !== undefined && value !== null;\n}\n\n/**\n * checks if RTCPeerConnection constructor is available\n */\nexport const validateRTCPeerConnection = () => {\n if (!isPresent(RTCPeerConnection)) {\n const error = ErrorFactory.GenericErrors.MissingRTCPeerConnection();\n HMSLogger.e(TAG, error);\n throw error;\n }\n};\n\n/**\n * navigator.mediaDevices is undefined in insecure contexts served over HTTP protocol\n */\nexport const validateMediaDevicesExistence = () => {\n if (!isPresent(navigator.mediaDevices)) {\n const error = ErrorFactory.GenericErrors.MissingMediaDevices();\n HMSLogger.e(TAG, error);\n throw error;\n }\n};\n", "import { ENV, isNode, parsedUserAgent } from './support';\nimport { isPresent } from './validations';\nimport { DomainCategory } from '../analytics/AnalyticsEventDomains';\nimport { domainCategory } from '../analytics/domain-analytics';\nimport { HMSFrameworkInfo } from '../interfaces';\n\nconst sdk_version = require('../../package.json').version;\n\ntype UserAgent = {\n os: string;\n os_version: string;\n sdk: 'web';\n sdk_version: string;\n env: 'debug' | 'prod';\n domain: DomainCategory;\n is_prebuilt: boolean;\n device_model?: string;\n framework?: HMSFrameworkInfo['type'] | 'node';\n framework_version?: HMSFrameworkInfo['version'];\n framework_sdk_version?: HMSFrameworkInfo['sdkVersion'];\n};\n\nexport function createUserAgent(sdkEnv: ENV = ENV.PROD, frameworkInfo?: HMSFrameworkInfo): string {\n const sdk = 'web';\n const env = domainCategory !== DomainCategory.LOCAL && sdkEnv === ENV.PROD ? 'prod' : 'debug';\n\n if (isNode) {\n return convertObjectToString({\n os: 'web_nodejs',\n os_version: process.version,\n sdk,\n sdk_version,\n env,\n domain: domainCategory,\n is_prebuilt: !!frameworkInfo?.isPrebuilt,\n framework: 'node',\n framework_version: process.version,\n framework_sdk_version: frameworkInfo?.sdkVersion,\n });\n }\n\n const parsedOs = parsedUserAgent.getOS();\n const parsedDevice = parsedUserAgent.getDevice();\n const parsedBrowser = parsedUserAgent.getBrowser();\n\n const os = replaceSpaces(`web_${parsedOs.name}`);\n const os_version = parsedOs.version || '';\n\n const browser = replaceSpaces(`${parsedBrowser.name}_${parsedBrowser.version}`);\n let device_model = browser;\n if (parsedDevice.type) {\n const deviceVendor = replaceSpaces(`${parsedDevice.vendor}_${parsedDevice.type}`);\n device_model = `${deviceVendor}/${browser}`;\n }\n\n return convertObjectToString({\n os,\n os_version,\n sdk,\n sdk_version,\n device_model,\n env,\n domain: domainCategory,\n is_prebuilt: !!frameworkInfo?.isPrebuilt,\n framework: frameworkInfo?.type,\n framework_version: frameworkInfo?.version,\n framework_sdk_version: frameworkInfo?.sdkVersion,\n });\n}\n\nfunction replaceSpaces(s: string) {\n return s.replace(/ /g, '_');\n}\n\nconst convertObjectToString = (object: UserAgent, delimiter = ',') =>\n Object.keys(object)\n .filter(key => isPresent(object[key as keyof UserAgent]))\n .map(key => `${key}:${object[key as keyof UserAgent]}`)\n .join(delimiter);\n", "import { PublishAnalyticPayload } from './publish-stats/interfaces';\nimport { AdditionalAnalyticsProperties } from './AdditionalAnalyticsProperties';\nimport AnalyticsEvent from './AnalyticsEvent';\nimport { AnalyticsEventLevel } from './AnalyticsEventLevel';\nimport { IAnalyticsPropertiesProvider } from './IAnalyticsPropertiesProvider';\nimport { HMSException } from '../error/HMSException';\nimport { DeviceMap, SelectedDevices } from '../interfaces';\nimport { HMSTrackSettings } from '../media/settings/HMSTrackSettings';\nimport { HMSRemoteVideoTrack } from '../media/tracks';\n\nexport default class AnalyticsEventFactory {\n private static KEY_REQUESTED_AT = 'requested_at';\n private static KEY_RESPONDED_AT = 'responded_at';\n\n static connect(\n error?: Error,\n additionalProperties?: AdditionalAnalyticsProperties,\n requestedAt: Date = new Date(),\n respondedAt: Date = new Date(),\n endpoint?: string,\n ) {\n const name = this.eventNameFor('connect', error === undefined);\n const level = error ? AnalyticsEventLevel.ERROR : AnalyticsEventLevel.INFO;\n\n const properties = this.getPropertiesWithError(\n {\n ...additionalProperties,\n [this.KEY_REQUESTED_AT]: requestedAt?.getTime(),\n [this.KEY_RESPONDED_AT]: respondedAt?.getTime(),\n endpoint,\n },\n error,\n );\n\n return new AnalyticsEvent({ name, level, properties });\n }\n\n static disconnect(error?: Error, additionalProperties?: AdditionalAnalyticsProperties) {\n const name = 'disconnected';\n const level = error ? AnalyticsEventLevel.ERROR : AnalyticsEventLevel.INFO;\n const properties = this.getPropertiesWithError(additionalProperties, error);\n\n return new AnalyticsEvent({ name, level, properties });\n }\n\n static preview({\n error,\n ...props\n }: {\n error?: Error;\n time?: number;\n init_response_time?: number;\n ws_connect_time?: number;\n on_policy_change_time?: number;\n local_audio_track_time?: number;\n local_video_track_time?: number;\n }) {\n const name = this.eventNameFor('preview', error === undefined);\n const level = error ? AnalyticsEventLevel.ERROR : AnalyticsEventLevel.INFO;\n const properties = this.getPropertiesWithError(props, error);\n\n return new AnalyticsEvent({ name, level, properties });\n }\n\n static join({\n error,\n ...props\n }: {\n error?: Error;\n is_preview_called?: boolean;\n start?: Date;\n end?: Date;\n time?: number;\n init_response_time?: number;\n ws_connect_time?: number;\n on_policy_change_time?: number;\n local_audio_track_time?: number;\n local_video_track_time?: number;\n retries_join?: number;\n }) {\n const name = this.eventNameFor('join', error === undefined);\n const level = error ? AnalyticsEventLevel.ERROR : AnalyticsEventLevel.INFO;\n\n const properties = this.getPropertiesWithError({ ...props, is_preview_called: !!props.is_preview_called }, error);\n\n return new AnalyticsEvent({ name, level, properties });\n }\n\n static publish({ devices, settings, error }: { devices?: DeviceMap; settings?: HMSTrackSettings; error?: Error }) {\n const name = this.eventNameFor('publish', error === undefined);\n const level = error ? AnalyticsEventLevel.ERROR : AnalyticsEventLevel.INFO;\n const properties = this.getPropertiesWithError(\n {\n devices,\n audio: settings?.audio,\n video: settings?.video,\n },\n error,\n );\n return new AnalyticsEvent({\n name,\n level,\n properties,\n });\n }\n\n static hlsPlayerError(error: HMSException) {\n return new AnalyticsEvent({\n name: 'hlsPlayerError',\n level: AnalyticsEventLevel.ERROR,\n properties: this.getErrorProperties(error),\n });\n }\n static subscribeFail(error: Error) {\n const name = this.eventNameFor('subscribe', false);\n const level = AnalyticsEventLevel.ERROR;\n const properties = this.getErrorProperties(error);\n\n return new AnalyticsEvent({ name, level, properties });\n }\n\n static leave() {\n return new AnalyticsEvent({ name: 'leave', level: AnalyticsEventLevel.INFO });\n }\n\n static autoplayError() {\n return new AnalyticsEvent({ name: 'autoplayError', level: AnalyticsEventLevel.ERROR });\n }\n\n static audioPlaybackError(error: HMSException) {\n return new AnalyticsEvent({\n name: 'audioPlaybackError',\n level: AnalyticsEventLevel.ERROR,\n properties: this.getErrorProperties(error),\n });\n }\n\n static deviceChange({\n selection,\n type,\n devices,\n error,\n }: {\n selection: Partial<SelectedDevices>;\n type?: 'change' | 'list' | 'audioInput' | 'audioOutput' | 'video';\n devices: DeviceMap;\n error?: Error;\n }) {\n const name = this.eventNameFor(error ? 'publish' : `device.${type}`, error === undefined);\n const level = error ? AnalyticsEventLevel.ERROR : AnalyticsEventLevel.INFO;\n const properties = this.getPropertiesWithError({ selection, devices }, error);\n return new AnalyticsEvent({\n name,\n level,\n properties,\n });\n }\n\n static performance(stats: IAnalyticsPropertiesProvider) {\n const name = 'perf.stats';\n const level = AnalyticsEventLevel.INFO;\n const properties = stats.toAnalyticsProperties();\n\n return new AnalyticsEvent({ name, level, properties });\n }\n\n static rtcStats(stats: IAnalyticsPropertiesProvider) {\n const name = 'rtc.stats';\n const level = AnalyticsEventLevel.INFO;\n const properties = stats.toAnalyticsProperties();\n\n return new AnalyticsEvent({ name, level, properties });\n }\n\n static rtcStatsFailed(error: HMSException) {\n const name = 'rtc.stats.failed';\n const level = AnalyticsEventLevel.ERROR;\n\n return new AnalyticsEvent({ name, level, properties: this.getErrorProperties(error) });\n }\n\n /**\n * TODO: remove once everything is switched to server side degradation, this\n * event can be handled on server side as well.\n */\n static degradationStats(track: HMSRemoteVideoTrack, isDegraded: boolean) {\n const name = 'video.degradation.stats';\n const level = AnalyticsEventLevel.INFO;\n let properties: any = {\n degradedAt: track.degradedAt,\n trackId: track.trackId,\n };\n\n if (!isDegraded && track.degradedAt instanceof Date) {\n // not degraded => restored\n const restoredAt = new Date();\n const duration = restoredAt.valueOf() - track.degradedAt.valueOf();\n properties = { ...properties, duration, restoredAt };\n }\n\n return new AnalyticsEvent({ name, level, properties });\n }\n\n static audioDetectionFail(error: Error, device?: MediaDeviceInfo): AnalyticsEvent {\n const properties = this.getPropertiesWithError({ device }, error);\n const level = AnalyticsEventLevel.ERROR;\n const name = 'audiopresence.failed';\n\n return new AnalyticsEvent({ name, level, properties });\n }\n\n static previewNetworkQuality(properties: { downLink?: string; score?: number; error?: string }) {\n return new AnalyticsEvent({\n name: 'perf.networkquality.preview',\n level: properties.error ? AnalyticsEventLevel.ERROR : AnalyticsEventLevel.INFO,\n properties,\n });\n }\n\n static publishStats(properties: PublishAnalyticPayload) {\n return new AnalyticsEvent({\n name: 'publisher.stats',\n level: AnalyticsEventLevel.INFO,\n properties,\n });\n }\n\n private static eventNameFor(name: string, ok: boolean) {\n const suffix = ok ? 'success' : 'failed';\n return `${name}.${suffix}`;\n }\n\n private static getPropertiesWithError(initialProperties: any, error?: Error) {\n const errorProperties = this.getErrorProperties(error);\n initialProperties = { ...errorProperties, ...initialProperties };\n return initialProperties;\n }\n\n private static getErrorProperties(error?: Error): Record<string, any> {\n if (error) {\n return error instanceof HMSException\n ? error.toAnalyticsProperties()\n : {\n error_name: error.name,\n error_message: error.message,\n error_description: error.cause,\n };\n } else {\n return {};\n }\n }\n}\n", "import HMSLogger from '../utils/logger';\n\nexport type TimedEventName = 'init' | 'websocket-open' | 'on-policy-change' | 'local-tracks' | 'preview' | 'join';\n\nexport enum TimedEvent {\n INIT = 'init_response_time',\n WEBSOCKET_CONNECT = 'ws_connect_time',\n ON_POLICY_CHANGE = 'on_policy_change_time',\n LOCAL_AUDIO_TRACK = 'local_audio_track_time',\n LOCAL_VIDEO_TRACK = 'local_video_track_time',\n JOIN = 'join_time',\n PREVIEW = 'preview_time',\n PEER_LIST = 'peer_list_time',\n ROOM_STATE = 'room_state_time',\n JOIN_RESPONSE = 'join_response_time',\n GET_TOKEN = 'GET_TOKEN',\n}\n\nconst defaultEventNames = [\n TimedEvent.INIT,\n TimedEvent.WEBSOCKET_CONNECT,\n TimedEvent.ON_POLICY_CHANGE,\n TimedEvent.LOCAL_AUDIO_TRACK,\n TimedEvent.LOCAL_VIDEO_TRACK,\n TimedEvent.PEER_LIST,\n TimedEvent.ROOM_STATE,\n TimedEvent.JOIN_RESPONSE,\n];\n\nexport class AnalyticsTimer {\n private eventPerformanceMeasures: Partial<Record<TimedEvent, PerformanceMeasure>> = {};\n\n start(eventName: TimedEvent) {\n performance.mark(eventName);\n }\n\n end(eventName: TimedEvent) {\n try {\n this.eventPerformanceMeasures[eventName] = performance.measure(eventName, eventName);\n HMSLogger.d('[HMSPerformanceTiming]', eventName, this.eventPerformanceMeasures[eventName]?.duration);\n } catch (error) {\n HMSLogger.w('[AnalyticsTimer]', `Error in measuring performance for event ${eventName}`, { error });\n }\n }\n\n getTimeTaken(eventName: TimedEvent) {\n return this.eventPerformanceMeasures[eventName]?.duration;\n }\n\n getTimes(...eventNames: TimedEvent[]) {\n return [...defaultEventNames, ...eventNames].reduce(\n (timeObject, eventName) => ({ ...timeObject, [eventName]: this.getTimeTaken(eventName) }),\n {},\n );\n }\n\n cleanup() {\n this.eventPerformanceMeasures = {};\n }\n}\n", "import adapter from 'webrtc-adapter';\nimport { ErrorFactory } from './ErrorFactory';\nimport { HMSAction } from './HMSAction';\nimport { HMSException } from './HMSException';\n\nexport enum HMSGetMediaActions {\n UNKNOWN = 'unknown(video or audio)',\n AUDIO = 'audio',\n VIDEO = 'video',\n AV = 'audio, video',\n SCREEN = 'screen',\n}\n\nfunction getDefaultError(error: string, deviceInfo: string) {\n const message = error.toLowerCase();\n if (message.includes('device not found')) {\n return ErrorFactory.TracksErrors.DeviceNotAvailable(HMSAction.TRACK, deviceInfo, error);\n } else if (message.includes('permission denied')) {\n return ErrorFactory.TracksErrors.CantAccessCaptureDevice(HMSAction.TRACK, deviceInfo, error);\n } else {\n return ErrorFactory.TracksErrors.GenericTrack(HMSAction.TRACK, error);\n }\n}\n\n/**\n * # Edge Cases:\n * - Screenshare error: The problem is when block at OS level, chrome throws NotAllowedError(HMS code - 3001) while firefox throws NotFoundError(HMS code - 3002),\n * we will handle this internally and throw error as User block - 3001 and OS block - 3011 for all browsers.\n * Chrome -\n * User blocked - NotAllowedError - Permission denied\n * System blocked - NotAllowedError - Permission denied by system\n */\n// eslint-disable-next-line complexity\nfunction convertMediaErrorToHMSException(err: Error, deviceInfo = ''): HMSException {\n /**\n * Note: Adapter detects all chromium browsers as 'chrome'\n */\n const chromeSystemDenied =\n adapter.browserDetails.browser === 'chrome' &&\n err.name === 'NotAllowedError' &&\n err.message.includes('denied by system');\n\n if (chromeSystemDenied) {\n return ErrorFactory.TracksErrors.SystemDeniedPermission(HMSAction.TRACK, deviceInfo, err.message);\n }\n\n if (adapter.browserDetails.browser === 'firefox' && err.name === 'NotFoundError') {\n const hmsError = ErrorFactory.TracksErrors.SystemDeniedPermission(HMSAction.TRACK, deviceInfo, err.message);\n hmsError.description = `Capture device is either blocked at Operating System level or not available - ${deviceInfo}`;\n return hmsError;\n }\n\n switch (err.name) {\n case 'OverconstrainedError':\n return ErrorFactory.TracksErrors.OverConstrained(\n HMSAction.TRACK,\n deviceInfo,\n (err as OverconstrainedError).constraint,\n );\n case 'NotAllowedError':\n return ErrorFactory.TracksErrors.CantAccessCaptureDevice(HMSAction.TRACK, deviceInfo, err.message);\n case 'NotFoundError':\n return ErrorFactory.TracksErrors.DeviceNotAvailable(HMSAction.TRACK, deviceInfo, err.message);\n case 'NotReadableError':\n return ErrorFactory.TracksErrors.DeviceInUse(HMSAction.TRACK, deviceInfo, err.message);\n case 'TypeError':\n return ErrorFactory.TracksErrors.NothingToReturn(HMSAction.TRACK, err.message);\n default:\n return getDefaultError(err.message, deviceInfo);\n }\n}\n\nexport function BuildGetMediaError(err: Error, deviceInfo: string): HMSException {\n const exception = convertMediaErrorToHMSException(err, deviceInfo);\n exception.addNativeError(err);\n return exception;\n}\n", "import adapter from 'webrtc-adapter';\nimport './utils/local-storage-polyfill';\nimport HMSLogger from './utils/logger';\nconst sdk_version = require('../package.json').version;\n\nHMSLogger.d('adapter', `${adapter.browserDetails.browser} v${adapter.browserDetails.version}`);\nHMSLogger.d('sdk version', sdk_version);\n\nexport * from './media/streams';\nexport * from './media/tracks';\nexport * from './utils/media';\nexport * from './utils/device-error';\nexport * from './utils/support';\nexport * from './error/HMSException';\nexport * from './interfaces';\nexport * from './rtc-stats';\nexport * from './plugins';\nexport * from './utils/logger';\n", "import { HMSTrack } from '../tracks';\n\nexport class HMSMediaStream {\n readonly nativeStream: MediaStream;\n id: string;\n\n readonly tracks = new Array<HMSTrack>();\n\n constructor(nativeStream: MediaStream) {\n this.nativeStream = nativeStream;\n this.id = nativeStream.id;\n }\n\n /**\n * This is only used when onDemandTracks flag is enabled in Init\n * @param id\n */\n updateId(id: string) {\n this.id = id;\n }\n}\n", "export const stringifyMediaStreamTrack = (track: MediaStreamTrack) => {\n if (!track) {\n return '';\n }\n return `{\n trackId: ${track.id};\n kind: ${track.kind};\n enabled: ${track.enabled};\n muted: ${track.muted};\n readyState: ${track.readyState};\n }`;\n};\n", "import { HMSTrackType } from './HMSTrackType';\nimport { stringifyMediaStreamTrack } from '../../utils/json';\nimport HMSLogger from '../../utils/logger';\nimport { HMSMediaStream } from '../streams';\n\nexport type HMSTrackSource = 'regular' | 'screen' | 'plugin' | 'audioplaylist' | 'videoplaylist' | string;\n\nexport abstract class HMSTrack {\n /**\n * @internal\n */\n readonly stream: HMSMediaStream;\n source?: HMSTrackSource;\n peerId?: string;\n transceiver?: RTCRtpTransceiver;\n\n /**\n * @internal to print as a helpful identifier alongside logs\n */\n logIdentifier = '';\n\n /** The native mediastream track, for local, this changes on mute/unmute(for video),\n * and on device change.\n * @internal */\n nativeTrack: MediaStreamTrack;\n\n /**\n * Firefox doesn't respect the track id as sent from the backend when calling peerconnection.ontrack callback. This\n * breaks correlation of future track updates from backend. So we're storing the sdp track id as present in the\n * original offer along with the track as well and will let this override the native track id for any correlation\n * purpose.\n * This applies for remote tracks only.\n * @internal */\n private sdpTrackId?: string;\n\n /**\n * @internal\n * The local track id is changed on mute/unmute or when device id changes, this is abstracted as an internal\n * detail of HMSTrack and the variable is used for this enacapsulation where the first track id is remembered\n * and treated as the fixed track id for this HMSTrack. This simplifies things for the user of the sdk who\n * do not have to worry about changing track IDs.\n * This applies for local tracks only.\n */\n private firstTrackId?: string;\n\n abstract readonly type: HMSTrackType;\n\n public get enabled(): boolean {\n return this.nativeTrack.enabled;\n }\n\n /**\n * firstTrackId => encapsulates change in local track ids\n * sdpTrackId => fixes remote track updates correlation on firefox\n */\n public get trackId(): string {\n return this.firstTrackId || this.sdpTrackId || this.nativeTrack.id;\n }\n\n getMediaTrackSettings(): MediaTrackSettings {\n return this.nativeTrack.getSettings();\n }\n\n async setEnabled(value: boolean): Promise<void> {\n this.nativeTrack.enabled = value;\n }\n\n protected constructor(stream: HMSMediaStream, track: MediaStreamTrack, source?: HMSTrackSource) {\n this.stream = stream;\n this.nativeTrack = track;\n this.source = source;\n }\n\n /**\n * @internal\n */\n setSdpTrackId(sdpTrackId: string) {\n this.sdpTrackId = sdpTrackId;\n }\n\n /**\n * @internal\n */\n protected setFirstTrackId(trackId: string) {\n this.firstTrackId = trackId;\n }\n\n /**\n * @internal\n * take care of -\n * 1. https://bugs.chromium.org/p/chromium/issues/detail?id=1232649\n * 2. stopping any tracks\n * 3. plugins related cleanups and stopping\n */\n cleanup() {\n HMSLogger.d('[HMSTrack]', 'Stopping track', this.toString());\n this.nativeTrack?.stop();\n }\n\n toString() {\n return `{\n streamId: ${this.stream.id};\n peerId: ${this.peerId};\n trackId: ${this.trackId};\n mid: ${this.transceiver?.mid || '-'};\n logIdentifier: ${this.logIdentifier};\n source: ${this.source};\n enabled: ${this.enabled};\n nativeTrack: ${stringifyMediaStreamTrack(this.nativeTrack)};\n }`;\n }\n}\n", "export enum HMSTrackType {\n AUDIO = 'audio',\n VIDEO = 'video',\n}\n", "import { HMSTrack, HMSTrackSource } from './HMSTrack';\nimport { HMSTrackType } from './HMSTrackType';\nimport HMSLogger from '../../utils/logger';\nimport { HMSMediaStream, HMSRemoteStream } from '../streams';\n\nexport class HMSAudioTrack extends HMSTrack {\n readonly type: HMSTrackType = HMSTrackType.AUDIO;\n private audioElement: HTMLAudioElement | null = null;\n private outputDevice?: MediaDeviceInfo;\n\n constructor(stream: HMSMediaStream, track: MediaStreamTrack, source?: string) {\n super(stream, track, source as HMSTrackSource);\n if (track.kind !== 'audio') {\n throw new Error(\"Expected 'track' kind = 'audio'\");\n }\n }\n\n getVolume() {\n return this.audioElement ? this.audioElement.volume * 100 : null;\n }\n\n async setVolume(value: number) {\n if (value < 0 || value > 100) {\n throw Error('Please pass a valid number between 0-100');\n }\n // Don't subscribe to audio when volume is 0\n await this.subscribeToAudio(value === 0 ? false : this.enabled);\n if (this.audioElement) {\n this.audioElement.volume = value / 100;\n }\n }\n\n setAudioElement(element: HTMLAudioElement | null) {\n HMSLogger.d('[HMSAudioTrack]', this.logIdentifier, 'adding audio element', `${this}`, element);\n this.audioElement = element;\n }\n\n /**\n * @internal\n * @returns {HTMLAudioElement | null}\n */\n getAudioElement(): HTMLAudioElement | null {\n return this.audioElement;\n }\n\n getOutputDevice() {\n return this.outputDevice;\n }\n\n cleanup() {\n super.cleanup();\n if (this.audioElement) {\n this.audioElement.srcObject = null;\n this.audioElement.remove();\n this.audioElement = null;\n }\n }\n\n async setOutputDevice(device?: MediaDeviceInfo) {\n if (!device) {\n HMSLogger.d('[HMSAudioTrack]', this.logIdentifier, 'device is null', `${this}`);\n return;\n }\n if (!this.audioElement) {\n HMSLogger.d('[HMSAudioTrack]', this.logIdentifier, 'no audio element to set output', `${this}`);\n this.outputDevice = device;\n return;\n }\n try {\n // @ts-ignore\n if (typeof this.audioElement.setSinkId === 'function') {\n // @ts-ignore\n await this.audioElement?.setSinkId(device.deviceId);\n this.outputDevice = device;\n }\n } catch (error) {\n HMSLogger.d('[HMSAudioTrack]', 'error in setSinkId', error);\n }\n }\n\n protected async subscribeToAudio(value: boolean) {\n if (this.stream instanceof HMSRemoteStream) {\n await this.stream.setAudio(value, this.trackId, this.logIdentifier);\n }\n }\n}\n", "import { DeviceMap, SelectedDevices } from '../interfaces';\nimport { LocalStorage } from '../utils/local-storage';\nimport HMSLogger from '../utils/logger';\n\ntype DeviceInfo = { deviceId?: string; groupId?: string };\n/**\n * This class is to manage storing and retrieving selected devices\n * from localstorage\n * @internal\n */\nclass DeviceStorage {\n private storage = new LocalStorage<SelectedDevices>('hms-device-selection');\n private remember = false;\n private devices?: DeviceMap;\n private readonly TAG = '[HMSDeviceStorage]';\n\n setDevices(devices: DeviceMap) {\n this.devices = devices;\n }\n\n rememberDevices(value: boolean) {\n this.remember = value;\n }\n\n /**\n * This will update the passed in type value in storage\n * @param {string} type - One of audioInput | videoInput | audioOutput\n * @param {DeviceInfo} param\n * @returns {void}\n */\n updateSelection(type: 'audioInput' | 'videoInput' | 'audioOutput', { deviceId, groupId }: DeviceInfo) {\n if (!this.devices || !this.remember) {\n return;\n }\n const newSelection = this.devices[type].find(device => this.isSame({ deviceId, groupId }, device));\n if (!newSelection) {\n HMSLogger.w(this.TAG, `Could not find device with deviceId: ${deviceId}, groupId: ${groupId}`);\n return;\n }\n const selectedDevices = this.storage.get() || {};\n if (type === 'audioOutput') {\n selectedDevices[type] = newSelection as MediaDeviceInfo;\n } else {\n selectedDevices[type] = newSelection as MediaDeviceInfo;\n }\n this.storage.set(selectedDevices);\n }\n\n getSelection() {\n if (!this.remember) {\n return undefined;\n }\n return this.storage.get();\n }\n\n cleanup() {\n this.remember = false;\n this.devices = undefined;\n }\n\n private isSame(current: DeviceInfo, device: DeviceInfo) {\n // Safari doesn't give groupId from nativeTrack. Check if groupId's match or current groupId is not present\n return current.deviceId === device.deviceId && (current.groupId === device.groupId || !current.groupId);\n }\n}\n\nexport const DeviceStorageManager = new DeviceStorage();\n", "/**\n * A plugin implementing this interface can be registered with HMSLocalAudioTrack to transform, process or\n * analyze the local audio track.These can include applications like background noise removal, speech commands, live\n * analysis of audio etc. The below functions are required for the sdk to properly use the plugin, usually\n * the plugin would also be exposing some public functions of its own for the UI to control its working.\n */\n\nexport interface HMSAudioPlugin {\n /**\n * This function will be called by the SDK for audio track which the plugin needs to process.\n * The reason audio context is also part of the interface is that it's recommeneded to reuse on audio context\n * instead of creating new for every use - https://developer.mozilla.org/en-US/docs/Web/API/AudioContext\n */\n processAudioTrack(ctx: AudioContext, source: AudioNode): Promise<AudioNode>;\n\n /**\n * This function will be called before the call to init, it is used to check whether the plugin supports current\n * OS, browser and audio device or not. An error object will be thrown back to the user if they try to use an unsupported plugin.\n */\n checkSupport(ctx?: AudioContext): HMSPluginSupportResult;\n\n /**\n * @deprecated. Will be deleted in future updates. Use checkSupport instead.\n */\n isSupported(): boolean;\n\n /**\n * This function will be called in the beginning for initialization which may include tasks like setting up\n * variables, loading ML models etc. This can be used by a plugin to ensure it's prepared at the time\n * processAudio is called.\n */\n init(): Promise<void> | void;\n\n /**\n * The name is meant to uniquely specify a plugin instance. This will be used to track number of plugins\n * added to the track, and same name won't be allowed twice.\n */\n getName(): string;\n\n /**\n This sets the Plugin type @see HMSAudioPluginType, processing will happen\n based on the type of plugin\n */\n getPluginType(): HMSAudioPluginType;\n /*\n * the plugin can use this function to dispose off its resources. It'll be called when the plugin instance is\n * no longer needed at the end.\n */\n stop(): void;\n}\n\n/**\n * Specifies the type of the plugin a transforming plugin will get an output audio node to give the resulting\n * transformation. While an analyzing plugin will only be passed the input node.\n * For analyse plugins, you can return the source node passed to plugin.processTrack to not modify anything\n */\nexport enum HMSAudioPluginType {\n TRANSFORM = 'TRANSFORM',\n ANALYZE = 'ANALYZE',\n}\n\nexport interface HMSPluginSupportResult {\n isSupported: boolean;\n errType?: HMSPluginUnsupportedTypes;\n errMsg?: string;\n}\n\nexport enum HMSPluginUnsupportedTypes {\n PLATFORM_NOT_SUPPORTED = 'PLATFORM_NOT_SUPPORTED',\n DEVICE_NOT_SUPPORTED = 'DEVICE_NOT_SUPPORTED',\n}\n", "import AnalyticsEvent from './AnalyticsEvent';\nimport { AnalyticsEventLevel } from './AnalyticsEventLevel';\nimport { HMSException } from '../error/HMSException';\n\nexport default class MediaPluginsAnalyticsFactory {\n static failure(pluginName: string, error: HMSException) {\n const name = 'mediaPlugin.failed';\n const level = AnalyticsEventLevel.ERROR;\n const properties = { plugin_name: pluginName, ...error.toAnalyticsProperties() };\n\n return new AnalyticsEvent({ name, level, properties });\n }\n\n static audioPluginFailure(pluginName: string, sampleRate: number, error: HMSException) {\n const name = 'mediaPlugin.failed';\n const level = AnalyticsEventLevel.ERROR;\n const properties = { plugin_name: pluginName, sampleRate: sampleRate, ...error.toAnalyticsProperties() };\n\n return new AnalyticsEvent({ name, level, properties });\n }\n\n static audioPluginStats({\n pluginName,\n duration,\n loadTime,\n sampleRate,\n }: {\n pluginName: string;\n duration: number;\n loadTime: number;\n sampleRate: number;\n }) {\n const name = 'mediaPlugin.stats';\n const level = AnalyticsEventLevel.INFO;\n const properties = {\n plugin_name: pluginName,\n duration: duration,\n load_time: loadTime,\n sampleRate: sampleRate,\n };\n return new AnalyticsEvent({ name, level, properties });\n }\n\n static stats({\n pluginName,\n duration,\n loadTime,\n avgPreProcessingTime,\n avgProcessingTime,\n inputFrameRate,\n pluginFrameRate,\n }: {\n pluginName: string;\n duration: number;\n loadTime: number;\n avgPreProcessingTime: number;\n avgProcessingTime: number;\n inputFrameRate: number;\n pluginFrameRate: number;\n }) {\n const name = 'mediaPlugin.stats';\n const level = AnalyticsEventLevel.INFO;\n const properties = {\n plugin_name: pluginName,\n duration: duration,\n load_time: loadTime,\n avg_preprocessing_time: avgPreProcessingTime,\n avg_processing_time: avgProcessingTime,\n input_frame_rate: inputFrameRate,\n plugin_frame_rate: pluginFrameRate,\n };\n return new AnalyticsEvent({ name, level, properties });\n }\n}\n", "import MediaPluginsAnalyticsFactory from '../../analytics/MediaPluginsAnalyticsFactory';\nimport { ErrorFactory } from '../../error/ErrorFactory';\nimport { HMSAction } from '../../error/HMSAction';\nimport { HMSException } from '../../error/HMSException';\nimport { EventBus } from '../../events/EventBus';\nimport HMSLogger from '../../utils/logger';\n\nexport class AudioPluginsAnalytics {\n private readonly TAG = '[AudioPluginsAnalytics]';\n private readonly initTime: Record<string, number>;\n private readonly addedTimestamps: Record<string, number>;\n private readonly pluginAdded: Record<string, boolean>;\n private readonly pluginSampleRate: Record<string, number>;\n\n constructor(private eventBus: EventBus) {\n this.initTime = {};\n this.addedTimestamps = {};\n this.pluginAdded = {};\n this.pluginSampleRate = {};\n }\n\n added(name: string, sampleRate: number) {\n this.pluginAdded[name] = true;\n this.addedTimestamps[name] = Date.now();\n this.initTime[name] = 0;\n this.pluginSampleRate[name] = sampleRate;\n }\n\n removed(name: string) {\n //send stats\n if (this.pluginAdded[name]) {\n const stats = {\n pluginName: name,\n // duration in seconds\n duration: Math.floor((Date.now() - this.addedTimestamps[name]) / 1000),\n loadTime: this.initTime[name],\n sampleRate: this.pluginSampleRate[name],\n };\n //send stats\n this.eventBus.analytics.publish(MediaPluginsAnalyticsFactory.audioPluginStats(stats));\n //clean the plugin details\n this.clean(name);\n }\n }\n\n failure(name: string, error: HMSException) {\n // send failure event\n if (this.pluginAdded[name]) {\n this.eventBus.analytics.publish(\n MediaPluginsAnalyticsFactory.audioPluginFailure(name, this.pluginSampleRate[name], error),\n );\n //clean the plugin details\n this.clean(name);\n }\n }\n\n async initWithTime<T>(name: string, initFn: () => Promise<T>) {\n if (this.initTime[name]) {\n HMSLogger.i(this.TAG, `Plugin Already loaded ${name}, time it took: ${this.initTime[name]}`);\n return;\n }\n let time: number | undefined = undefined;\n try {\n time = await this.timeInMs(initFn);\n HMSLogger.i(this.TAG, `Time taken for Plugin ${name} initialization : ${time}`);\n } catch (e) {\n //Failed during initialization of plugin(model loading etc...)\n const err = ErrorFactory.MediaPluginErrors.InitFailed(\n HMSAction.AUDIO_PLUGINS,\n `failed during initialization of plugin${(e as Error).message || e}`,\n );\n HMSLogger.e(this.TAG, err);\n this.failure(name, err);\n throw err;\n }\n if (time) {\n this.initTime[name] = time;\n }\n }\n\n private async timeInMs<T>(fn: () => Promise<T>): Promise<number> {\n const start = Date.now();\n await fn();\n return Math.floor(Date.now() - start);\n }\n\n private clean(name: string) {\n delete this.addedTimestamps[name];\n delete this.initTime[name];\n delete this.pluginAdded[name];\n delete this.pluginSampleRate[name];\n }\n}\n", "import { AudioPluginsAnalytics } from './AudioPluginsAnalytics';\nimport { HMSAudioPlugin, HMSPluginUnsupportedTypes } from './HMSAudioPlugin'; //HMSAudioPluginType\nimport { ErrorFactory } from '../../error/ErrorFactory';\nimport { HMSAction } from '../../error/HMSAction';\nimport { EventBus } from '../../events/EventBus';\nimport { HMSLocalAudioTrack } from '../../media/tracks';\nimport HMSLogger from '../../utils/logger';\n\nconst DEFAULT_SAMPLE_RATE = 48000;\n\n//Handling sample rate error in case of firefox\nconst checkBrowserSupport = () => {\n return navigator.userAgent.indexOf('Firefox') !== -1;\n};\n\n/**\n * This class manages applying different plugins on a local audio track. Plugins which need to modify the audio\n * are called in the order they were added. Plugins which do not need to modify the audio are called\n * with the original input.\n *\n * Concepts -\n * Audio Plugin - A module which can take in input audio, do some processing on it and return an AudioNode\n *\n * For Each Plugin, an AudioNode will be created and the source will be created from local audio track.\n * Each Audio node will be connected in the following order\n * source -> first plugin -> second plugin -> third plugin .. so on\n * @see HMSAudioPlugin\n */\nexport class HMSAudioPluginsManager {\n private readonly TAG = '[AudioPluginsManager]';\n private readonly hmsTrack: HMSLocalAudioTrack;\n // Map maintains the insertion order\n private readonly pluginsMap: Map<string, HMSAudioPlugin>;\n private audioContext?: AudioContext;\n\n private sourceNode?: MediaStreamAudioSourceNode;\n private destinationNode?: MediaStreamAudioDestinationNode;\n private prevAudioNode?: any;\n private analytics: AudioPluginsAnalytics;\n // This will replace the native track in peer connection when plugins are enabled\n private outputTrack?: MediaStreamTrack;\n private pluginAddInProgress = false;\n\n constructor(track: HMSLocalAudioTrack, eventBus: EventBus) {\n this.hmsTrack = track;\n this.pluginsMap = new Map();\n this.analytics = new AudioPluginsAnalytics(eventBus);\n this.createAudioContext();\n }\n\n getPlugins(): string[] {\n return Array.from(this.pluginsMap.keys());\n }\n\n async addPlugin(plugin: HMSAudioPlugin) {\n const name = plugin.getName?.();\n if (!name) {\n HMSLogger.w('no name provided by the plugin');\n return;\n }\n if (this.pluginAddInProgress) {\n const err = ErrorFactory.MediaPluginErrors.AddAlreadyInProgress(\n HMSAction.AUDIO_PLUGINS,\n 'Add Plugin is already in Progress',\n );\n this.analytics.added(name, this.audioContext!.sampleRate);\n this.analytics.failure(name, err);\n HMSLogger.w(\"can't add another plugin when previous add is in progress\");\n throw err;\n }\n\n this.pluginAddInProgress = true;\n\n try {\n await this.addPluginInternal(plugin);\n } finally {\n this.pluginAddInProgress = false;\n }\n }\n\n // eslint-disable-next-line complexity\n private async addPluginInternal(plugin: HMSAudioPlugin) {\n const name = plugin.getName?.();\n if (this.pluginsMap.get(name)) {\n HMSLogger.w(this.TAG, `plugin - ${name} already added.`);\n return;\n }\n\n await this.validateAndThrow(name, plugin);\n\n try {\n if (this.pluginsMap.size === 0) {\n await this.initAudioNodes();\n } else if (this.prevAudioNode) {\n // Previous node will be connected to destination. Disconnect that\n this.prevAudioNode.disconnect();\n }\n this.analytics.added(name, this.audioContext!.sampleRate);\n await this.analytics.initWithTime(name, async () => plugin.init());\n this.pluginsMap.set(name, plugin);\n await this.processPlugin(plugin);\n await this.connectToDestination();\n } catch (err) {\n HMSLogger.e(this.TAG, 'failed to add plugin', err);\n throw err;\n }\n }\n\n validatePlugin(plugin: HMSAudioPlugin) {\n return plugin.checkSupport(this.audioContext);\n }\n\n async validateAndThrow(name: string, plugin: HMSAudioPlugin) {\n const result = this.validatePlugin(plugin);\n if (result.isSupported) {\n HMSLogger.i(this.TAG, `plugin is supported,- ${plugin.getName()}`);\n } else {\n //Needed to re-add in the reprocess case, to send error message in case of failure\n this.analytics.added(name, this.audioContext!.sampleRate);\n if (result.errType === HMSPluginUnsupportedTypes.PLATFORM_NOT_SUPPORTED) {\n const err = ErrorFactory.MediaPluginErrors.PlatformNotSupported(\n HMSAction.AUDIO_PLUGINS,\n 'platform not supported, see docs',\n );\n this.analytics.failure(name, err);\n await this.cleanup();\n throw err;\n } else if (result.errType === HMSPluginUnsupportedTypes.DEVICE_NOT_SUPPORTED) {\n const err = ErrorFactory.MediaPluginErrors.DeviceNotSupported(\n HMSAction.AUDIO_PLUGINS,\n 'audio device not supported, see docs',\n );\n this.analytics.failure(name, err);\n await this.cleanup();\n throw err;\n }\n }\n }\n\n async removePlugin(plugin: HMSAudioPlugin) {\n await this.removePluginInternal(plugin);\n if (this.pluginsMap.size === 0) {\n // remove all previous nodes\n await this.cleanup();\n HMSLogger.i(this.TAG, `No plugins left, stopping plugins loop`);\n await this.hmsTrack.setProcessedTrack(undefined);\n } else {\n // Reprocess the remaining plugins again because there is no way to connect\n // the source of the removed plugin to destination of removed plugin\n await this.reprocessPlugins();\n }\n }\n\n async cleanup() {\n for (const plugin of this.pluginsMap.values()) {\n await this.removePluginInternal(plugin);\n }\n await this.hmsTrack.setProcessedTrack(undefined);\n //disconnect nodes, stop track\n this.sourceNode?.disconnect();\n this.prevAudioNode?.disconnect();\n this.outputTrack?.stop();\n\n // reset all variables\n this.sourceNode = undefined;\n this.destinationNode = undefined;\n this.prevAudioNode = undefined;\n this.outputTrack = undefined;\n }\n\n //Keeping it separate since we are initializing context only once\n async closeContext() {\n this.audioContext?.close();\n this.audioContext = undefined;\n }\n\n async reprocessPlugins() {\n if (this.pluginsMap.size === 0 || !this.sourceNode) {\n return;\n }\n const plugins = Array.from(this.pluginsMap.values()); // make a copy of plugins\n await this.cleanup();\n await this.initAudioNodes();\n for (const plugin of plugins) {\n await this.addPlugin(plugin);\n }\n }\n\n private async initAudioNodes() {\n if (this.audioContext) {\n if (!this.sourceNode) {\n const audioStream = new MediaStream([this.hmsTrack.nativeTrack]);\n this.sourceNode = this.audioContext.createMediaStreamSource(audioStream);\n }\n if (!this.destinationNode) {\n this.destinationNode = this.audioContext.createMediaStreamDestination();\n this.outputTrack = this.destinationNode.stream.getAudioTracks()[0];\n try {\n await this.hmsTrack.setProcessedTrack(this.outputTrack);\n } catch (err) {\n HMSLogger.e(this.TAG, 'error in setting processed track', err);\n throw err;\n }\n }\n }\n }\n\n private async processPlugin(plugin: HMSAudioPlugin) {\n try {\n const currentNode = await plugin.processAudioTrack(\n this.audioContext!, // it is always present at this point\n this.prevAudioNode || this.sourceNode,\n );\n if (this.prevAudioNode) {\n // if previous node was present while adding this plugin\n // it is disconnected from destination, connect the previous node to\n // to the current node\n this.prevAudioNode.connect(currentNode);\n }\n this.prevAudioNode = currentNode;\n } catch (err) {\n const name = plugin.getName();\n //TODO error happened on processing of plugin notify UI\n HMSLogger.e(this.TAG, `error in processing plugin ${name}`, err);\n //remove plugin from loop and stop analytics for it\n await this.removePluginInternal(plugin);\n }\n }\n\n private async connectToDestination() {\n try {\n if (this.prevAudioNode && this.destinationNode && this.prevAudioNode.context === this.destinationNode.context) {\n this.prevAudioNode.connect(this.destinationNode);\n }\n } catch (err) {\n HMSLogger.e(this.TAG, 'error in connecting to destination node', err);\n }\n }\n\n private async removePluginInternal(plugin: HMSAudioPlugin) {\n const name = plugin.getName?.();\n if (!this.pluginsMap.get(name)) {\n HMSLogger.w(this.TAG, `plugin - ${name} not found to remove.`);\n return;\n }\n HMSLogger.i(this.TAG, `removing plugin ${name}`);\n this.pluginsMap.delete(name);\n plugin.stop();\n this.analytics.removed(name);\n }\n\n private createAudioContext() {\n if (!this.audioContext) {\n if (checkBrowserSupport()) {\n /**\n Not setting default sample rate for firefox since connecting\n audio nodes from context with different sample rate is not\n supported in firefox\n */\n this.audioContext = new AudioContext();\n } else {\n this.audioContext = new AudioContext({ sampleRate: DEFAULT_SAMPLE_RATE });\n }\n }\n }\n}\n", "import { BuildGetMediaError, HMSGetMediaActions } from '../error/utils';\nimport { HMSAudioTrackSettings, HMSVideoTrackSettings } from '../media/settings';\n\nexport async function getAudioTrack(settings: HMSAudioTrackSettings): Promise<MediaStreamTrack> {\n try {\n const stream = await navigator.mediaDevices.getUserMedia({\n audio: settings ? settings.toConstraints() : false,\n });\n return stream.getAudioTracks()[0];\n } catch (err) {\n throw BuildGetMediaError(err as Error, HMSGetMediaActions.AUDIO);\n }\n}\n\nexport async function getVideoTrack(settings: HMSVideoTrackSettings): Promise<MediaStreamTrack> {\n try {\n const stream = await navigator.mediaDevices.getUserMedia({\n video: settings ? settings.toConstraints() : false,\n });\n return stream.getVideoTracks()[0];\n } catch (err) {\n throw BuildGetMediaError(err as Error, HMSGetMediaActions.VIDEO);\n }\n}\n\n// To differentiate between normal track and empty track.\nexport function isEmptyTrack(track: MediaStreamTrack) {\n // Firefox gives '' as label for empty track(created from audio context)\n return 'canvas' in track || track.label === 'MediaStreamAudioDestinationNode' || track.label === '';\n}\n", "import HMSLogger from './logger';\nimport { BuildGetMediaError, HMSGetMediaActions } from '../error/utils';\n\nexport async function getLocalStream(constraints: MediaStreamConstraints): Promise<MediaStream> {\n try {\n const stream = await navigator.mediaDevices.getUserMedia(constraints);\n return stream;\n } catch (err) {\n throw BuildGetMediaError(err as Error, HMSGetMediaActions.AV);\n }\n}\n\nexport async function getLocalScreen(constraints: MediaStreamConstraints['video']): Promise<MediaStream> {\n try {\n // @ts-ignore [https://github.com/microsoft/TypeScript/issues/33232]\n const stream = await navigator.mediaDevices.getDisplayMedia({ video: constraints, audio: false });\n return stream;\n } catch (err) {\n throw BuildGetMediaError(err as Error, HMSGetMediaActions.SCREEN);\n }\n}\n\ninterface MediaDeviceGroups {\n audioinput: MediaDeviceInfo[];\n audiooutput: MediaDeviceInfo[];\n videoinput: MediaDeviceInfo[];\n}\n\nexport async function getLocalDevices(): Promise<MediaDeviceGroups> {\n try {\n const devices = await navigator.mediaDevices.enumerateDevices();\n const deviceGroups: MediaDeviceGroups = {\n audioinput: [],\n audiooutput: [],\n videoinput: [],\n };\n devices.forEach(device => deviceGroups[device.kind].push(device));\n return deviceGroups;\n } catch (err) {\n throw BuildGetMediaError(err as Error, HMSGetMediaActions.AV);\n }\n}\n\nexport interface HMSAudioContext {\n audioContext: AudioContext | null;\n getAudioContext: () => AudioContext;\n resumeContext: () => Promise<void>;\n}\n\nexport const HMSAudioContextHandler: HMSAudioContext = {\n audioContext: null,\n getAudioContext() {\n if (!this.audioContext) {\n this.audioContext = new AudioContext();\n }\n return this.audioContext;\n },\n async resumeContext() {\n try {\n return await this.getAudioContext().resume();\n } catch (error) {\n HMSLogger.e('AudioContext', error);\n }\n },\n};\n", "export interface IQueue<T> {\n size(): number;\n enqueue(item: T): void;\n dequeue(): T | undefined;\n}\n\nexport class Queue<T> implements IQueue<T> {\n protected storage: T[] = [];\n\n constructor(private capacity: number = Infinity) {}\n\n size() {\n return this.storage.length;\n }\n\n toList() {\n return this.storage.slice(0);\n }\n\n enqueue(item: T) {\n if (this.size() === this.capacity) {\n this.dequeue();\n }\n this.storage.push(item);\n }\n\n dequeue() {\n return this.storage.shift();\n }\n\n aggregate<R>(aggregationFn: (values: T[]) => R): R {\n return aggregationFn(this.storage);\n }\n}\n", "/**\n * Delay for a @see ms amount of time\n * @param ms -- time in milliseconds\n */\nexport function sleep(ms: number): Promise<void> {\n if (ms < 0) {\n throw Error('`ms` should be a positive integer');\n }\n return new Promise(resolve => setTimeout(resolve, ms));\n}\n\n/**\n * Debounce Fn - Function to limit the number of executions of the passed in\n * function in a given time duration\n * @param fn Function to be called\n * @param delay time by which the function execution has to be delayed\n * @returns {void}\n */\nexport function debounce<T extends (...args: any) => any>(fn: T, delay = 300) {\n let timer: any | undefined;\n return function (...args: []) {\n clearTimeout(timer);\n timer = undefined;\n //@ts-ignore\n //eslint-disable-next-line\n const context = this;\n timer = setTimeout(() => {\n fn.apply(context, args);\n }, delay);\n };\n}\n", "import HMSLogger from './logger';\nimport { HMSAudioContextHandler } from './media';\nimport { Queue } from './queue';\nimport { sleep } from './timer-utils';\nimport { HMSInternalEvent } from '../events/HMSInternalEvent';\nimport { HMSLocalAudioTrack } from '../internal';\n\n/** Send update only if audio level is above THRESHOLD */\nconst THRESHOLD = 35;\n\n/** Send update only if audio level is changed by UPDATE_THRESHOLD */\nconst UPDATE_THRESHOLD = 5;\n\nexport interface ITrackAudioLevelUpdate {\n track: HMSLocalAudioTrack;\n audioLevel: number;\n}\n\nexport class TrackAudioLevelMonitor {\n private readonly TAG = '[TrackAudioLevelMonitor]';\n private audioLevel = 0;\n private analyserNode?: AnalyserNode;\n private isMonitored = false;\n /** Frequency of polling audio level from track */\n private interval = 100;\n /** Store past audio levels for this duration */\n private historyInterval = 700;\n private history = new Queue<number>(this.historyInterval / this.interval);\n\n constructor(\n private track: HMSLocalAudioTrack,\n private audioLevelEvent: HMSInternalEvent<ITrackAudioLevelUpdate>,\n private silenceEvent: HMSInternalEvent<{ track: HMSLocalAudioTrack }>,\n ) {\n try {\n const stream = new MediaStream([this.track.nativeTrack]);\n this.analyserNode = this.createAnalyserNodeForStream(stream);\n } catch (ex) {\n HMSLogger.w(this.TAG, 'Unable to initialize AudioContext', ex);\n }\n }\n\n /**\n * To detect silence we check if the track is unmuted and silent in the current moment\n * periodically. If the track is found to be silent more than a threshold number of times\n * we send the event. The threshold number of time is there to reduce the chance of false\n * positives.\n */\n detectSilence = async () => {\n const tickInterval = 20;\n const tickThreshold = 50;\n let silenceCounter = 0;\n\n while (this.isMonitored) {\n if (this.track.enabled) {\n if (this.isSilentThisInstant()) {\n silenceCounter++;\n if (silenceCounter > tickThreshold) {\n this.silenceEvent.publish({ track: this.track });\n break;\n }\n } else {\n // bail out immediately if sound is found\n break;\n }\n }\n await sleep(tickInterval);\n }\n };\n\n start() {\n this.stop();\n this.isMonitored = true;\n HMSLogger.d(this.TAG, 'Starting track Monitor', `${this.track}`);\n this.loop().then(() => HMSLogger.d(this.TAG, 'Stopping track Monitor', `${this.track}`));\n }\n\n stop() {\n if (!this.analyserNode) {\n HMSLogger.d(this.TAG, 'AudioContext not initialized');\n return;\n }\n\n this.sendAudioLevel(0);\n this.isMonitored = false;\n }\n\n private async loop() {\n while (this.isMonitored) {\n this.sendAudioLevel(this.getMaxAudioLevelOverPeriod());\n await sleep(this.interval);\n }\n }\n\n private sendAudioLevel(audioLevel = 0) {\n audioLevel = audioLevel > THRESHOLD ? audioLevel : 0;\n const isSignificantChange = Math.abs(this.audioLevel - audioLevel) > UPDATE_THRESHOLD;\n if (isSignificantChange) {\n this.audioLevel = audioLevel;\n const audioLevelUpdate: ITrackAudioLevelUpdate = { track: this.track, audioLevel: this.audioLevel };\n this.audioLevelEvent.publish(audioLevelUpdate);\n }\n }\n\n private getMaxAudioLevelOverPeriod() {\n if (!this.analyserNode) {\n HMSLogger.d(this.TAG, 'AudioContext not initialized');\n return;\n }\n const newLevel = this.calculateAudioLevel();\n newLevel !== undefined && this.history.enqueue(newLevel);\n return this.history.aggregate(values => Math.max(...values));\n }\n\n private calculateAudioLevel() {\n if (!this.analyserNode) {\n HMSLogger.d(this.TAG, 'AudioContext not initialized');\n return;\n }\n\n const data = new Uint8Array(this.analyserNode.fftSize);\n this.analyserNode.getByteTimeDomainData(data);\n const lowest = 0.009;\n let max = lowest;\n for (const frequency of data) {\n max = Math.max(max, (frequency - 128) / 128);\n }\n const normalized = (Math.log(lowest) - Math.log(max)) / Math.log(lowest);\n const percent = Math.ceil(Math.min(Math.max(normalized * 100, 0), 100));\n return percent;\n }\n\n private isSilentThisInstant() {\n if (!this.analyserNode) {\n HMSLogger.d(this.TAG, 'AudioContext not initialized');\n return;\n }\n\n const data = new Uint8Array(this.analyserNode.fftSize);\n this.analyserNode.getByteTimeDomainData(data);\n\n // For absolute silence(in case of mic/software failures), all frequencies are 128 or 0.\n return !data.some(frequency => frequency !== 128 && frequency !== 0);\n }\n\n private createAnalyserNodeForStream(stream: MediaStream): AnalyserNode {\n const audioContext = HMSAudioContextHandler.getAudioContext();\n const analyser = audioContext.createAnalyser();\n const source = audioContext.createMediaStreamSource(stream);\n source.connect(analyser);\n return analyser;\n }\n}\n", "import { HMSChangeMultiTrackStateRequest, HMSChangeTrackStateRequest } from './change-track-state';\nimport { DeviceChangeListener } from './devices';\nimport { HMSLeaveRoomRequest } from './leave-room-request';\nimport { HMSMessage } from './message';\nimport { HMSConnectionQuality } from './peer';\nimport { HMSRoleChangeRequest } from './role-change-request';\nimport { HMSRoom } from './room';\nimport { HMSPoll, SessionStoreUpdate } from './session-store';\nimport { HMSSpeaker } from './speaker';\nimport { HMSException } from '../error/HMSException';\nimport { HMSTrack } from '../media/tracks/HMSTrack';\nimport { HMSPeer } from '../sdk/models/peer';\n\nexport enum HMSRoomUpdate {\n RECORDING_STATE_UPDATED = 'RECORDING_STATE_UPDATED',\n BROWSER_RECORDING_STATE_UPDATED = 'BROWSER_RECORDING_STATE_UPDATED',\n SERVER_RECORDING_STATE_UPDATED = 'SERVER_RECORDING_STATE_UPDATED',\n RTMP_STREAMING_STATE_UPDATED = 'RTMP_STREAMING_STATE_UPDATED',\n HLS_STREAMING_STATE_UPDATED = 'HLS_STREAMING_STATE_UPDATED',\n}\n\nexport enum HMSPeerUpdate {\n PEER_JOINED,\n PEER_LEFT,\n AUDIO_TOGGLED,\n VIDEO_TOGGLED,\n BECAME_DOMINANT_SPEAKER,\n RESIGNED_DOMINANT_SPEAKER,\n STARTED_SPEAKING,\n STOPPED_SPEAKING,\n ROLE_UPDATED,\n PEER_LIST,\n NAME_UPDATED,\n METADATA_UPDATED,\n}\n\nexport enum HMSTrackUpdate {\n TRACK_ADDED,\n TRACK_REMOVED,\n TRACK_MUTED,\n TRACK_UNMUTED,\n TRACK_DESCRIPTION_CHANGED,\n TRACK_DEGRADED,\n TRACK_RESTORED,\n}\n\nexport enum HMSPollsUpdate {\n POLL_CREATED,\n POLL_STARTED,\n POLL_STOPPED,\n POLL_STATS_UPDATED,\n}\n\nexport interface HMSAudioListener {\n onAudioLevelUpdate(speakers: HMSSpeaker[]): void;\n}\n\nexport interface HMSConnectionQualityListener {\n onConnectionQualityUpdate(qualityUpdates: HMSConnectionQuality[]): void;\n}\n\nexport interface SessionStoreListener {\n onSessionStoreUpdate(values: SessionStoreUpdate[]): void;\n}\n\nexport interface PollsListener {\n onPollsUpdate(type: HMSPollsUpdate, polls: HMSPoll[]): void;\n}\n\nexport interface HMSUpdateListener extends DeviceChangeListener, SessionStoreListener, PollsListener {\n onJoin(room: HMSRoom): void;\n onRoomUpdate(type: HMSRoomUpdate, room: HMSRoom): void;\n onPeerUpdate(type: HMSPeerUpdate, peer: HMSPeer | HMSPeer[] | null): void;\n onTrackUpdate(type: HMSTrackUpdate, track: HMSTrack, peer: HMSPeer): void;\n onMessageReceived(message: HMSMessage): void;\n onError(error: HMSException): void;\n onReconnecting(error: HMSException): void;\n onReconnected(): void;\n onRoleChangeRequest(request: HMSRoleChangeRequest): void;\n onRoleUpdate(newRole: string): void;\n onChangeTrackStateRequest(request: HMSChangeTrackStateRequest): void;\n onChangeMultiTrackStateRequest(request: HMSChangeMultiTrackStateRequest): void;\n onRemovedFromRoom(request: HMSLeaveRoomRequest): void;\n onNetworkQuality?(score: number): void;\n}\n", "export enum HMSSimulcastLayer {\n NONE = 'none',\n LOW = 'low',\n MEDIUM = 'medium',\n HIGH = 'high',\n}\n\nexport interface SimulcastLayer {\n rid: string;\n scaleResolutionDownBy: number;\n maxBitrate: number;\n maxFramerate: number;\n}\n\nexport interface SimulcastResolution {\n width: number;\n height: number;\n}\n\nexport interface SimulcastLayers {\n layers?: SimulcastLayer[];\n}\n\nexport type HMSPreferredSimulcastLayer = Exclude<HMSSimulcastLayer, HMSSimulcastLayer.NONE>;\nexport interface HMSSimulcastLayerDefinition {\n layer: HMSPreferredSimulcastLayer;\n resolution: SimulcastResolution;\n}\n\nexport type RID = 'f' | 'h' | 'q';\n\nexport const simulcastMapping = {\n f: HMSSimulcastLayer.HIGH,\n h: HMSSimulcastLayer.MEDIUM,\n q: HMSSimulcastLayer.LOW,\n};\n", "export enum HMSVideoCodec {\n VP8 = 'vp8',\n VP9 = 'vp9',\n H264 = 'h264',\n}\n\nexport enum HMSAudioCodec {\n OPUS = 'opus',\n}\n\n/**\n * Refer https://developer.mozilla.org/en-US/docs/Web/API/MediaTrackConstraints/facingMode\n * for more details.\n */\nexport enum HMSFacingMode {\n USER = 'user',\n ENVIRONMENT = 'environment',\n LEFT = 'left',\n RIGHT = 'right',\n}\n\nexport interface HMSAudioTrackSettings {\n volume?: number;\n codec?: HMSAudioCodec;\n maxBitrate?: number;\n deviceId?: string;\n advanced?: Array<MediaTrackConstraintSet>;\n}\n\nexport interface HMSVideoTrackSettings {\n width?: number;\n height?: number;\n codec?: HMSVideoCodec;\n maxFramerate?: number;\n maxBitrate?: number;\n deviceId?: string;\n advanced?: Array<MediaTrackConstraintSet>;\n facingMode?: HMSFacingMode;\n}\n\n/**\n * Config to have control over screenshare being captured. Note that\n * not all fields are supported on all browsers. Even when they're supported\n * the fields acts as hints and the browser can override them.\n */\nexport interface HMSScreenShareConfig {\n /**\n * discard the video and only share audio track with others, useful\n * for sharing music.\n * @default false\n */\n audioOnly?: boolean;\n /**\n * do not give an option to share audio while screen sharing.\n * @default false\n */\n videoOnly?: boolean;\n /**\n * preselect the relevant tab in screenshare menu\n * browser - for preferring a browser tab\n * window - for application window\n * monitor - for full screen\n * @default monitor\n */\n displaySurface?: 'browser' | 'monitor' | 'window';\n /**\n * show the current tab first in supported browser, throws\n * error if user doesn't select current tab for sharing.\n * @default false\n */\n forceCurrentTab?: boolean;\n /**\n * show the current tab first in supported browser, but don't throw error\n * if user selects something else.\n * @default false\n */\n preferCurrentTab?: boolean;\n /**\n * whether to show an option for sharing the current tab in the screen share\n * prompt. Screen sharing current tab might lead to hall of mirrors effect.\n * Default is exclude, if either of forceCurrentTab or preferCurrentTab are true,\n * this is set to include.\n * @default exclude\n */\n selfBrowserSurface?: 'include' | 'exclude';\n /**\n * whether to hint browser to show a \"share this tab instead\" option when\n * tab is shared.\n * Default is include, set to exclude if forceCurrentTab is true\n * @default include\n */\n surfaceSwitching?: 'include' | 'exclude';\n /**\n * whether to show option for sharing system level audio if full screen\n * is being shared. Not applicable if isVideoOnly is true.\n * Note that sharing system audio will cause echo if mic is on.\n * @default exclude\n */\n systemAudio?: 'include' | 'exclude';\n /**\n * used for region capture in screenshare, if the current tab is being screenshared\n * the screenshare video track will be cropped to only this element. Will throw\n * error if the element is not present in DOM.\n */\n cropElement?: HTMLDivElement;\n /**\n * used for region capture in screenshare, the screenshare video track will be\n * cropped to only the passed in cropTarget. This cropTarget must come from\n * the tab which is being shared\n */\n cropTarget?: object;\n}\n\nexport interface ScreenCaptureHandle {\n handle: string;\n exposeOrigin: boolean;\n}\n\nexport interface ScreenCaptureHandleConfig extends ScreenCaptureHandle {\n permittedOrigins: string[];\n}\n", "import { HMSException } from '../error/HMSException';\n\nexport interface HMSDeviceChangeEvent {\n error?: HMSException;\n devices: DeviceMap;\n selection?: MediaDeviceInfo;\n type: 'audioOutput' | 'audioInput' | 'video';\n}\n\nexport enum DeviceType {\n videoInput = 'videoInput',\n audioInput = 'audioInput',\n audioOutput = 'audioOutput',\n}\n\nexport interface DeviceMap {\n [DeviceType.audioInput]: MediaDeviceInfo[];\n [DeviceType.audioOutput]: MediaDeviceInfo[];\n [DeviceType.videoInput]: MediaDeviceInfo[];\n}\n\nexport interface DeviceChangeListener {\n onDeviceChange?(event: HMSDeviceChangeEvent): void;\n}\n\nexport type SelectedDevices = {\n [DeviceType.audioInput]?: MediaDeviceInfo;\n [DeviceType.videoInput]?: MediaDeviceInfo;\n [DeviceType.audioOutput]?: MediaDeviceInfo;\n};\n", "export enum HMSPlaylistType {\n audio = 'audio',\n video = 'video',\n}\n\nexport interface HMSPlaylistItem<T> {\n /**\n * uniquely identifies a playlist item\n */\n id: string;\n name: string;\n type: HMSPlaylistType;\n /**\n * the url to play from, local files are not supported currently.\n */\n url: string;\n /**\n * any additional info, for eg. composer, musician etc.\n */\n metadata?: T;\n /**\n * duration in seconds\n */\n duration?: number;\n}\n\nexport interface HMSPlaylistProgressEvent {\n type: HMSPlaylistType;\n progress: number;\n}\n\nexport interface HMSPlaylistManager {\n getList<T>(type: HMSPlaylistType): HMSPlaylistItem<T>[];\n setList<T>(list: HMSPlaylistItem<T>[]): void;\n clearList(type: HMSPlaylistType): Promise<void>;\n playNext(type: HMSPlaylistType): Promise<void>;\n playPrevious(type: HMSPlaylistType): Promise<void>;\n removeItem(id: string, type: HMSPlaylistType): Promise<boolean>;\n /**\n * Seek forward/backward on selected type relative to currentTime\n * @param value - number in seconds to go forward(if negative, it goes backwards)\n * @param {HMSPlaylistType} type\n */\n seek(value: number, type: HMSPlaylistType): void;\n /**\n * Seek forward/backward on selected type - absolute value\n * @param value - point in playlist item to go to\n * @param {HMSPlaylistType} type\n */\n seekTo(value: number, type: HMSPlaylistType): void;\n /**\n * set volume on the selected type\n * @param value - number between 0-100\n * @param type\n */\n setVolume(value: number, type: HMSPlaylistType): void;\n /**\n * Get volume of selected type, between 0-100\n * @param type\n */\n getVolume(type: HMSPlaylistType): number;\n isPlaying(type: HMSPlaylistType): boolean;\n getCurrentIndex(type: HMSPlaylistType): number;\n getCurrentSelection<T>(type: HMSPlaylistType): HMSPlaylistItem<T> | undefined;\n /**\n * Returns a value between 0-100\n * @param {HMSPlaylistType} type\n */\n getCurrentProgress(type: HMSPlaylistType): number;\n /**\n * Get the currentTime of audio/video based on type\n * @param {HMSPlaylistType} type\n */\n getCurrentTime(type: HMSPlaylistType): number;\n setEnabled(enabled: boolean, info: { id: string; type: HMSPlaylistType }): Promise<void>;\n stop(type: HMSPlaylistType): Promise<void>;\n /**\n * Subscriber to progress event with a callback\n * @param fn\n */\n onProgress(fn: (event: HMSPlaylistProgressEvent) => void): void;\n /**\n * This will be called when a new track is played\n * @param fn\n */\n onNewTrackStart<T>(fn: (item: HMSPlaylistItem<T>) => void): void;\n\n onPlaylistEnded(fn: (type: HMSPlaylistType) => void): void;\n onCurrentTrackEnded<T>(fn: (item: HMSPlaylistItem<T>) => void): void;\n /**\n * Function to autoplay status i.e. whether next item in playlist after the current one ends\n * @param {HMSPlaylistType} type\n * @param {boolean} autoplay\n */\n setIsAutoplayOn(type: HMSPlaylistType, autoplay: boolean): void;\n /**\n * Get the current playback rate of audio/video playlist\n * @param {HMSPlaylistType} type\n */\n getPlaybackRate(type: HMSPlaylistType): number;\n /**\n * set the current playback rate of audio/video playlist\n * @param {HMSPlaylistType}type\n * @param {number}value - number between 0.25 and 2.0\n */\n setPlaybackRate(type: HMSPlaylistType, value: number): void;\n}\n", "import { HMSRoleName } from '../role';\n\nexport type HMSPollUserTrackingMode = 'peerID' | 'customerID' | 'userName';\n\nexport type HMSPollState = 'created' | 'started' | 'stopped';\n\nexport interface HMSPoll {\n id: string;\n title: string;\n state?: HMSPollState;\n type: 'poll' | 'quiz';\n duration?: number;\n anonymous?: boolean;\n visibility?: boolean;\n locked?: boolean;\n mode?: HMSPollUserTrackingMode;\n createdBy?: string;\n startedBy?: string;\n stoppedBy?: string;\n createdAt?: Date;\n startedAt?: Date;\n stoppedAt?: Date;\n questions?: HMSPollQuestion[];\n rolesThatCanVote?: HMSRoleName[];\n rolesThatCanViewResponses?: HMSRoleName[];\n result?: HMSPollResult;\n}\n\nexport interface HMSPollCreateParams\n extends Pick<\n HMSPoll,\n | 'id'\n | 'title'\n | 'type'\n | 'duration'\n | 'anonymous'\n | 'visibility'\n | 'locked'\n | 'mode'\n | 'rolesThatCanVote'\n | 'rolesThatCanViewResponses'\n > {\n questions?: HMSPollQuestionCreateParams[];\n}\n\nexport interface HMSPollQuestion {\n index: number;\n text: string;\n type: HMSPollQuestionType;\n skippable?: boolean;\n duration?: number;\n once?: boolean;\n weight?: number;\n negative?: boolean;\n answerMinLen?: number;\n answerMaxLen?: number;\n options?: HMSPollQuestionOption[];\n answer?: HMSPollQuestionAnswer;\n responses?: HMSPollQuestionResponse[];\n result?: HMSPollQuestionResult;\n}\n\nexport interface HMSPollQuestionCreateParams extends Pick<HMSPollQuestion, 'text' | 'skippable' | 'type' | 'answer'> {\n index?: number;\n options?: HMSPollQuestionOptionCreateParams[];\n}\n\nexport interface HMSPollQuestionAnswer {\n hidden: boolean; // if true answer will not be returned when poll is running\n option?: number; // option index for correct answer, in case of single choice\n options?: number[]; // list of options that shoould be in answer\n text?: string; // answer text for answer.\n case?: boolean; // if false case is ignored when comparing.\n trim?: boolean; // if true, empty space is trimmer from start and end of asnwer.\n}\n\nexport enum HMSPollQuestionType {\n SINGLE_CHOICE = 'single-choice',\n MULTIPLE_CHOICE = 'multiple-choice',\n SHORT_ANSWER = 'short-answer',\n LONG_ANSWER = 'long-answer',\n}\n\nexport interface HMSPollQuestionOption {\n index: number;\n text: string;\n weight?: number;\n voteCount?: number;\n}\n\nexport interface HMSPollQuestionOptionCreateParams extends Pick<HMSPollQuestionOption, 'text' | 'weight'> {\n isCorrectAnswer?: boolean;\n}\n\nexport interface HMSPollQuestionResponse {\n id?: string;\n questionIndex: number;\n peer?: HMSPollResponsePeerInfo;\n type?: HMSPollQuestionType;\n skipped?: boolean;\n option?: number;\n options?: number[];\n text?: string;\n update?: boolean; // SDK Needs to track wether we previously answered and set accordingly\n duration?: number; // Time it took to answer the question for leaderboard\n responseFinal?: boolean; // Indicates wether this is last update when fetching responses\n}\n\nexport type HMSPollQuestionResponseCreateParams = Omit<\n HMSPollQuestionResponse,\n 'type' | 'peer' | 'update' | 'responseFinal'\n>;\n\ninterface HMSPollResponsePeerInfo {\n userHash?: string;\n peerid?: string;\n userid?: string;\n username?: string;\n}\n\nexport interface HMSPollResult {\n /**\n * The number of unique users who responded to the poll\n */\n totalUsers?: number;\n /**\n * The maximum number of users in the room during the poll.\n */\n maxUsers?: number;\n totalResponses?: number;\n}\n\nexport interface HMSPollQuestionResult {\n correctResponses?: number;\n skippedCount?: number;\n totalResponses?: number;\n}\n", "import { IAnalyticsPropertiesProvider } from '../../analytics/IAnalyticsPropertiesProvider';\nimport { HMSAudioCodec, HMSAudioTrackSettings as IHMSAudioTrackSettings } from '../../interfaces';\n\nexport class HMSAudioTrackSettingsBuilder {\n private _volume = 1.0;\n private _codec?: HMSAudioCodec = HMSAudioCodec.OPUS;\n private _maxBitrate?: number = 32;\n private _deviceId = 'default';\n private _advanced: Array<MediaTrackConstraintSet> = [\n // @ts-ignore\n { googEchoCancellation: { exact: true } },\n // @ts-ignore\n { googExperimentalEchoCancellation: { exact: true } },\n // @ts-ignore\n { autoGainControl: { exact: true } },\n // @ts-ignore\n { noiseSuppression: { exact: true } },\n // @ts-ignore\n { googHighpassFilter: { exact: true } },\n // @ts-ignore\n { googAudioMirroring: { exact: true } },\n ];\n\n volume(volume: number) {\n if (!(0.0 <= volume && volume <= 1.0)) {\n throw Error('volume can only be in range [0.0, 1.0]');\n }\n this._volume = volume;\n return this;\n }\n\n codec(codec?: HMSAudioCodec) {\n this._codec = codec;\n return this;\n }\n\n maxBitrate(maxBitrate?: number) {\n if (maxBitrate && maxBitrate <= 0) {\n throw Error('maxBitrate should be >= 1');\n }\n this._maxBitrate = maxBitrate;\n return this;\n }\n\n deviceId(deviceId: string) {\n // TODO: Validate if device-id is OK\n this._deviceId = deviceId;\n return this;\n }\n\n advanced(advanced: Array<MediaTrackConstraintSet>) {\n this._advanced = advanced;\n return this;\n }\n\n build() {\n return new HMSAudioTrackSettings(this._volume, this._codec, this._maxBitrate, this._deviceId, this._advanced);\n }\n}\n\nexport class HMSAudioTrackSettings implements IHMSAudioTrackSettings, IAnalyticsPropertiesProvider {\n readonly volume?: number;\n readonly codec?: HMSAudioCodec;\n readonly maxBitrate?: number;\n readonly deviceId?: string;\n readonly advanced?: Array<MediaTrackConstraintSet>;\n\n constructor(\n volume?: number,\n codec?: HMSAudioCodec,\n maxBitrate?: number,\n deviceId?: string,\n advanced?: Array<MediaTrackConstraintSet>,\n ) {\n this.volume = volume;\n this.codec = codec;\n this.maxBitrate = maxBitrate;\n this.deviceId = deviceId;\n this.advanced = advanced;\n }\n\n toConstraints(): MediaTrackConstraints {\n return {\n deviceId: this.deviceId,\n advanced: this.advanced,\n };\n }\n\n toAnalyticsProperties() {\n return {\n audio_bitrate: this.maxBitrate,\n audio_codec: this.codec,\n };\n }\n}\n", "import { IAnalyticsPropertiesProvider } from '../../analytics/IAnalyticsPropertiesProvider';\nimport { HMSFacingMode, HMSVideoCodec, HMSVideoTrackSettings as IHMSVideoTrackSettings } from '../../interfaces';\n\nexport class HMSVideoTrackSettingsBuilder {\n private _width?: number = 320;\n private _height?: number = 180;\n private _codec?: HMSVideoCodec = HMSVideoCodec.VP8;\n private _maxFramerate?: number = 30;\n private _maxBitrate?: number = 150;\n private _deviceId?: string;\n private _facingMode?: HMSFacingMode;\n private _advanced: Array<MediaTrackConstraintSet> = [];\n\n setWidth(width?: number) {\n this._width = width;\n return this;\n }\n\n setHeight(height?: number) {\n this._height = height;\n return this;\n }\n\n codec(codec?: HMSVideoCodec) {\n this._codec = codec;\n return this;\n }\n\n maxFramerate(maxFramerate?: number) {\n if (maxFramerate && maxFramerate <= 0) {\n throw Error('maxFramerate should be >= 1');\n }\n this._maxFramerate = maxFramerate;\n return this;\n }\n\n /**\n * @param useDefault Ignored if maxBitrate is valid.\n * If true and maxBitrate is undefined - sets a default value.\n * If false and maxBitrate is undefined - sets undefined.\n */\n maxBitrate(maxBitrate?: number, useDefault = true) {\n if (typeof maxBitrate === 'number' && maxBitrate <= 0) {\n throw Error('maxBitrate should be >= 1');\n }\n this._maxBitrate = maxBitrate;\n if (!this._maxBitrate && useDefault) {\n this._maxBitrate = 150_000;\n }\n return this;\n }\n\n deviceId(deviceId: string) {\n // TODO: Validate if device-id is OK\n this._deviceId = deviceId;\n return this;\n }\n\n advanced(advanced: Array<MediaTrackConstraintSet>) {\n this._advanced = advanced;\n return this;\n }\n\n facingMode(mode: HMSFacingMode) {\n this._facingMode = mode;\n return this;\n }\n\n build() {\n return new HMSVideoTrackSettings(\n this._width,\n this._height,\n this._codec,\n this._maxFramerate,\n this._deviceId,\n this._advanced,\n this._maxBitrate,\n this._facingMode,\n );\n }\n}\n\nexport class HMSVideoTrackSettings implements IHMSVideoTrackSettings, IAnalyticsPropertiesProvider {\n readonly width?: number;\n readonly height?: number;\n readonly codec?: HMSVideoCodec;\n readonly maxFramerate?: number;\n readonly maxBitrate?: number;\n readonly deviceId?: string;\n readonly advanced?: Array<MediaTrackConstraintSet>;\n facingMode?: HMSFacingMode;\n\n constructor(\n width?: number,\n height?: number,\n codec?: HMSVideoCodec,\n maxFramerate?: number,\n deviceId?: string | undefined,\n advanced?: Array<MediaTrackConstraintSet>,\n maxBitrate?: number,\n facingMode?: HMSFacingMode,\n ) {\n this.width = width;\n this.height = height;\n this.codec = codec;\n this.maxFramerate = maxFramerate;\n this.maxBitrate = maxBitrate;\n this.deviceId = deviceId;\n this.advanced = advanced;\n this.facingMode = facingMode;\n }\n\n toConstraints(isScreenShare?: boolean): MediaTrackConstraints {\n let dimensionConstraintKey = 'ideal';\n if (isScreenShare) {\n dimensionConstraintKey = 'max';\n }\n return {\n width: { [dimensionConstraintKey]: this.width },\n height: { [dimensionConstraintKey]: this.height },\n frameRate: this.maxFramerate,\n deviceId: this.deviceId,\n facingMode: this.facingMode,\n };\n }\n\n toAnalyticsProperties() {\n return {\n width: this.width,\n height: this.height,\n video_bitrate: this.maxBitrate,\n framerate: this.maxFramerate,\n video_codec: this.codec,\n facingMode: this.facingMode,\n };\n }\n}\n", "import { HMSAudioTrackSettings, HMSAudioTrackSettingsBuilder } from './HMSAudioTrackSettings';\nimport { HMSVideoTrackSettings, HMSVideoTrackSettingsBuilder } from './HMSVideoTrackSettings';\nimport { IAnalyticsPropertiesProvider } from '../../analytics/IAnalyticsPropertiesProvider';\nimport { ErrorFactory } from '../../error/ErrorFactory';\nimport { HMSAction } from '../../error/HMSAction';\n\nexport class HMSTrackSettingsBuilder {\n private _video: HMSVideoTrackSettings | null = new HMSVideoTrackSettingsBuilder().build();\n private _audio: HMSAudioTrackSettings | null = new HMSAudioTrackSettingsBuilder().build();\n private _screen: HMSVideoTrackSettings | null = new HMSVideoTrackSettingsBuilder().build();\n private _simulcast = false;\n\n video(video: HMSVideoTrackSettings | null) {\n this._video = video;\n return this;\n }\n\n audio(audio: HMSAudioTrackSettings | null) {\n this._audio = audio;\n return this;\n }\n\n screen(screen: HMSVideoTrackSettings | null) {\n this._screen = screen;\n return this;\n }\n\n simulcast(enabled: boolean) {\n this._simulcast = enabled;\n return this;\n }\n\n build() {\n if (this._audio === null && this._video === null) {\n throw ErrorFactory.TracksErrors.NothingToReturn(HMSAction.TRACK);\n }\n\n if (this._video === null && this._simulcast) {\n throw ErrorFactory.TracksErrors.InvalidVideoSettings(\n HMSAction.TRACK,\n 'Cannot enable simulcast when no video settings are provided',\n );\n }\n\n return new HMSTrackSettings(this._video, this._audio, this._simulcast, this._screen || undefined);\n }\n}\n\nexport class HMSTrackSettings implements IAnalyticsPropertiesProvider {\n readonly video: HMSVideoTrackSettings | null | undefined;\n readonly audio: HMSAudioTrackSettings | null | undefined;\n readonly screen: HMSVideoTrackSettings | null;\n readonly simulcast: boolean;\n\n constructor(\n video: HMSVideoTrackSettings | null | undefined,\n audio: HMSAudioTrackSettings | null | undefined,\n simulcast: boolean,\n screen: HMSVideoTrackSettings | null = null,\n ) {\n this.video = video;\n this.audio = audio;\n this.simulcast = simulcast;\n this.screen = screen;\n }\n\n toAnalyticsProperties() {\n let properties = {\n audio_enabled: this.audio !== null,\n video_enabled: this.video !== null,\n };\n\n if (this.audio) {\n properties = { ...this.audio.toAnalyticsProperties(), ...properties };\n }\n\n if (this.video) {\n properties = { ...this.video.toAnalyticsProperties(), ...properties };\n }\n\n return properties;\n }\n}\n", "import { HMSAudioTrack } from './HMSAudioTrack';\nimport { DeviceStorageManager } from '../../device-manager/DeviceStorage';\nimport { HMSException } from '../../error/HMSException';\nimport { EventBus } from '../../events/EventBus';\nimport { HMSAudioTrackSettings as IHMSAudioTrackSettings } from '../../interfaces';\nimport { HMSAudioPlugin, HMSPluginSupportResult } from '../../plugins';\nimport { HMSAudioPluginsManager } from '../../plugins/audio';\nimport HMSLogger from '../../utils/logger';\nimport { isBrowser, isIOS } from '../../utils/support';\nimport { getAudioTrack, isEmptyTrack } from '../../utils/track';\nimport { TrackAudioLevelMonitor } from '../../utils/track-audio-level-monitor';\nimport { HMSAudioTrackSettings, HMSAudioTrackSettingsBuilder } from '../settings';\nimport { HMSLocalStream } from '../streams';\n\nfunction generateHasPropertyChanged(newSettings: Partial<HMSAudioTrackSettings>, oldSettings: HMSAudioTrackSettings) {\n return function hasChanged(prop: 'codec' | 'volume' | 'maxBitrate' | 'deviceId' | 'advanced') {\n return prop in newSettings && newSettings[prop] !== oldSettings[prop];\n };\n}\n\nexport class HMSLocalAudioTrack extends HMSAudioTrack {\n private readonly TAG = '[HMSLocalAudioTrack]';\n settings: HMSAudioTrackSettings;\n private pluginsManager: HMSAudioPluginsManager;\n private processedTrack?: MediaStreamTrack;\n\n audioLevelMonitor?: TrackAudioLevelMonitor;\n\n /**\n * see the doc in HMSLocalVideoTrack\n * @internal\n */\n publishedTrackId?: string;\n\n /**\n * will be false for preview tracks\n */\n isPublished = false;\n\n constructor(\n stream: HMSLocalStream,\n track: MediaStreamTrack,\n source: string,\n private eventBus: EventBus,\n settings: HMSAudioTrackSettings = new HMSAudioTrackSettingsBuilder().build(),\n ) {\n super(stream, track, source);\n stream.tracks.push(this);\n\n this.settings = settings;\n // Replace the 'default' or invalid deviceId with the actual deviceId\n // This is to maintain consistency with selected devices as in some cases there will be no 'default' device\n if (settings.deviceId !== track.getSettings().deviceId && !isEmptyTrack(track)) {\n this.settings = this.buildNewSettings({ deviceId: track.getSettings().deviceId });\n }\n this.pluginsManager = new HMSAudioPluginsManager(this, eventBus);\n this.setFirstTrackId(track.id);\n if (isIOS() && isBrowser) {\n document.addEventListener('visibilitychange', this.handleVisibilityChange);\n }\n }\n\n private handleVisibilityChange = async () => {\n if (document.visibilityState === 'visible') {\n await this.replaceTrackWith(this.settings);\n }\n };\n\n private async replaceTrackWith(settings: HMSAudioTrackSettings) {\n const prevTrack = this.nativeTrack;\n /*\n * Note: Do not change the order of this.\n * stop the previous before acquiring the new track otherwise this can lead to\n * no audio when the above getAudioTrack throws an error. ex: DeviceInUse error\n */\n prevTrack?.stop();\n const isLevelMonitored = Boolean(this.audioLevelMonitor);\n const newTrack = await getAudioTrack(settings);\n newTrack.enabled = this.enabled;\n HMSLogger.d(this.TAG, 'replaceTrack, Previous track stopped', prevTrack, 'newTrack', newTrack);\n\n const localStream = this.stream as HMSLocalStream;\n // change nativeTrack so plugin can start its work\n await localStream.replaceSenderTrack(prevTrack, this.processedTrack || newTrack);\n await localStream.replaceStreamTrack(prevTrack, newTrack);\n this.nativeTrack = newTrack;\n isLevelMonitored && this.initAudioLevelMonitor();\n try {\n await this.pluginsManager.reprocessPlugins();\n } catch (e) {\n this.eventBus.audioPluginFailed.publish(e as HMSException);\n }\n }\n\n async setEnabled(value: boolean) {\n if (value === this.enabled) {\n return;\n }\n\n // Replace silent empty track with an actual audio track, if enabled.\n if (value && isEmptyTrack(this.nativeTrack)) {\n await this.replaceTrackWith(this.settings);\n }\n await super.setEnabled(value);\n if (value) {\n this.settings = this.buildNewSettings({ deviceId: this.nativeTrack.getSettings().deviceId });\n }\n this.eventBus.localAudioEnabled.publish({ enabled: value, track: this });\n }\n\n /**\n * verify if the track id being passed is of this track for correlating server messages like audio level\n */\n isPublishedTrackId(trackId: string) {\n return this.publishedTrackId === trackId;\n }\n\n async setSettings(settings: Partial<IHMSAudioTrackSettings>, internal = false) {\n const newSettings = this.buildNewSettings(settings);\n\n if (isEmptyTrack(this.nativeTrack)) {\n // if it is an empty track, cache the settings for when it is unmuted\n this.settings = newSettings;\n return;\n }\n await this.handleDeviceChange(newSettings, internal);\n await this.handleSettingsChange(newSettings);\n this.settings = newSettings;\n }\n\n /**\n * @see HMSAudioPlugin\n */\n getPlugins(): string[] {\n return this.pluginsManager.getPlugins();\n }\n\n /**\n * @see HMSAudioPlugin\n */\n async addPlugin(plugin: HMSAudioPlugin): Promise<void> {\n return this.pluginsManager.addPlugin(plugin);\n }\n\n /**\n * @see HMSAudioPlugin\n */\n async removePlugin(plugin: HMSAudioPlugin): Promise<void> {\n return this.pluginsManager.removePlugin(plugin);\n }\n\n /**\n * @see HMSAudioPlugin\n */\n validatePlugin(plugin: HMSAudioPlugin): HMSPluginSupportResult {\n return this.pluginsManager.validatePlugin(plugin);\n }\n\n /**\n * @internal\n */\n async setProcessedTrack(processedTrack?: MediaStreamTrack) {\n // if all plugins are removed reset everything back to native track\n if (!processedTrack) {\n if (this.processedTrack) {\n // remove, reset back to the native track\n await (this.stream as HMSLocalStream).replaceSenderTrack(this.processedTrack, this.nativeTrack);\n }\n this.processedTrack = undefined;\n return;\n }\n if (processedTrack !== this.processedTrack) {\n if (this.processedTrack) {\n // replace previous processed track with new one\n await (this.stream as HMSLocalStream).replaceSenderTrack(this.processedTrack, processedTrack);\n } else {\n // there is no prev processed track, replace native with new one\n await (this.stream as HMSLocalStream).replaceSenderTrack(this.nativeTrack, processedTrack);\n }\n this.processedTrack = processedTrack;\n }\n }\n\n initAudioLevelMonitor() {\n if (this.audioLevelMonitor) {\n this.destroyAudioLevelMonitor();\n }\n HMSLogger.d(this.TAG, 'Monitor Audio Level for', this, this.getMediaTrackSettings().deviceId);\n this.audioLevelMonitor = new TrackAudioLevelMonitor(\n this,\n this.eventBus.trackAudioLevelUpdate,\n this.eventBus.localAudioSilence,\n );\n this.audioLevelMonitor.start();\n this.audioLevelMonitor.detectSilence();\n }\n\n destroyAudioLevelMonitor() {\n this.audioLevelMonitor?.stop();\n this.audioLevelMonitor = undefined;\n }\n\n async cleanup() {\n super.cleanup();\n await this.pluginsManager.cleanup();\n await this.pluginsManager.closeContext();\n this.transceiver = undefined;\n this.processedTrack?.stop();\n this.isPublished = false;\n this.destroyAudioLevelMonitor();\n if (isIOS() && isBrowser) {\n document.removeEventListener('visibilitychange', this.handleVisibilityChange);\n }\n }\n\n /**\n * @internal\n * published track id will be different in case there was some processing done using plugins.\n */\n getTrackIDBeingSent() {\n return this.processedTrack ? this.processedTrack.id : this.nativeTrack.id;\n }\n\n /**\n * @internal\n */\n getTrackBeingSent() {\n return this.processedTrack || this.nativeTrack;\n }\n\n private buildNewSettings(settings: Partial<HMSAudioTrackSettings>) {\n const { volume, codec, maxBitrate, deviceId, advanced } = { ...this.settings, ...settings };\n const newSettings = new HMSAudioTrackSettings(volume, codec, maxBitrate, deviceId, advanced);\n return newSettings;\n }\n\n private handleSettingsChange = async (settings: HMSAudioTrackSettings) => {\n const stream = this.stream as HMSLocalStream;\n const hasPropertyChanged = generateHasPropertyChanged(settings, this.settings);\n if (hasPropertyChanged('maxBitrate') && settings.maxBitrate) {\n await stream.setMaxBitrateAndFramerate(this);\n }\n\n if (hasPropertyChanged('advanced')) {\n await this.replaceTrackWith(settings);\n }\n };\n\n /**\n * Replace audio track with new track on device change if enabled\n * @param settings - AudioSettings Object constructed with new settings\n * @param internal - whether the change was because of internal sdk call or external client call\n */\n private handleDeviceChange = async (settings: HMSAudioTrackSettings, internal = false) => {\n const hasPropertyChanged = generateHasPropertyChanged(settings, this.settings);\n if (hasPropertyChanged('deviceId')) {\n await this.replaceTrackWith(settings);\n if (!internal) {\n DeviceStorageManager.updateSelection('audioInput', {\n deviceId: settings.deviceId,\n groupId: this.nativeTrack.getSettings().groupId,\n });\n }\n }\n };\n}\n", "import { HMSAudioTrack } from './HMSAudioTrack';\n\nexport class HMSRemoteAudioTrack extends HMSAudioTrack {\n async setEnabled(value: boolean): Promise<void> {\n if (value === this.enabled) {\n return;\n }\n await super.setEnabled(value);\n await this.subscribeToAudio(value);\n }\n}\n", "import { HMSTrack, HMSTrackSource } from './HMSTrack';\nimport { HMSTrackType } from './HMSTrackType';\nimport { VideoElementManager } from './VideoElementManager';\nimport { HMSMediaStream } from '../streams';\n\nexport class HMSVideoTrack extends HMSTrack {\n readonly type: HMSTrackType = HMSTrackType.VIDEO;\n private sinkCount = 0;\n videoHandler!: VideoElementManager;\n\n constructor(stream: HMSMediaStream, track: MediaStreamTrack, source?: string) {\n super(stream, track, source as HMSTrackSource);\n if (track.kind !== 'video') {\n throw new Error(\"Expected 'track' kind = 'video'\");\n }\n }\n\n setVideoHandler(videoHandler: VideoElementManager) {\n this.videoHandler = videoHandler;\n }\n\n /**\n * sink=video element rendering the video\n */\n hasSinks() {\n return this.sinkCount > 0;\n }\n\n getSinks() {\n return this.videoHandler.getVideoElements() || [];\n }\n\n attach(videoElement: HTMLVideoElement) {\n this.videoHandler.addVideoElement(videoElement);\n }\n\n detach(videoElement: HTMLVideoElement) {\n this.videoHandler.removeVideoElement(videoElement);\n }\n\n /**\n * attaches the track to the passed in video element\n * @param videoElement\n */\n addSink(videoElement: HTMLVideoElement) {\n this.addSinkInternal(videoElement, this.nativeTrack);\n }\n\n /**\n * removes the track from the passed in video element\n * @param videoElement\n */\n removeSink(videoElement: HTMLVideoElement) {\n if (videoElement.srcObject !== null) {\n videoElement.srcObject = null;\n this.reduceSinkCount();\n }\n }\n\n cleanup(): void {\n super.cleanup();\n this.videoHandler.cleanup();\n }\n\n protected addSinkInternal(videoElement: HTMLVideoElement, track: MediaStreamTrack) {\n const srcObject = videoElement.srcObject;\n if (srcObject !== null && srcObject instanceof MediaStream) {\n const existingTrack = srcObject.getVideoTracks()[0];\n if (existingTrack?.id === track.id) {\n if (!existingTrack.muted && existingTrack.readyState === 'live') {\n // it's already attached, attaching again would just cause flickering\n return;\n } else {\n this.reduceSinkCount();\n }\n } else {\n this.reduceSinkCount();\n }\n }\n videoElement.srcObject = new MediaStream([track]);\n this.sinkCount++;\n }\n\n private reduceSinkCount() {\n if (this.sinkCount > 0) {\n this.sinkCount--;\n }\n }\n}\n", "import { v4 as uuid } from 'uuid';\nimport { getClosestLayer, layerToIntMapping } from './trackUtils';\nimport { HMSPreferredSimulcastLayer } from '../../interfaces/simulcast-layers';\nimport { HMSLocalVideoTrack, HMSRemoteVideoTrack } from '../../internal';\nimport { HMSIntersectionObserver } from '../../utils/intersection-observer';\nimport HMSLogger from '../../utils/logger';\nimport { HMSResizeObserver } from '../../utils/resize-observer';\nimport { isBrowser } from '../../utils/support';\n\n/**\n * This class is to manager video elements for video tracks.\n * This will handle attaching/detaching when element is in view or out of view.\n * This will also handle selecting appropriate layer when element size changesx\n */\nexport class VideoElementManager {\n private readonly TAG = '[VideoElementManager]';\n private resizeObserver?: typeof HMSResizeObserver;\n private intersectionObserver?: typeof HMSIntersectionObserver;\n private videoElements = new Set<HTMLVideoElement>();\n private entries = new WeakMap<HTMLVideoElement, DOMRectReadOnly>();\n private id: string;\n\n constructor(private track: HMSLocalVideoTrack | HMSRemoteVideoTrack) {\n this.init();\n this.id = uuid();\n }\n\n updateSinks(requestLayer = false) {\n for (const videoElement of this.videoElements) {\n if (this.track.enabled) {\n this.track.addSink(videoElement, requestLayer);\n } else {\n this.track.removeSink(videoElement, requestLayer);\n }\n }\n }\n\n // eslint-disable-next-line complexity\n async addVideoElement(videoElement: HTMLVideoElement) {\n if (this.videoElements.has(videoElement)) {\n return;\n }\n // Call init again, to initialize again if for some reason it failed in constructor\n // it will be a no-op if initialize already\n this.init();\n HMSLogger.d(this.TAG, `Adding video element for ${this.track}`, this.id);\n this.videoElements.add(videoElement);\n if (this.videoElements.size >= 10) {\n HMSLogger.w(\n this.TAG,\n `${this.track}`,\n `the track is added to ${this.videoElements.size} video elements, while this may be intentional, it's likely that there is a bug leading to unnecessary creation of video elements in the UI`,\n );\n }\n\n if (this.intersectionObserver?.isSupported()) {\n this.intersectionObserver.observe(videoElement, this.handleIntersection);\n } else if (isBrowser) {\n if (this.isElementInViewport(videoElement)) {\n this.track.addSink(videoElement);\n } else {\n this.track.removeSink(videoElement);\n }\n }\n if (this.resizeObserver) {\n this.resizeObserver.observe(videoElement, this.handleResize);\n } else if (this.track instanceof HMSRemoteVideoTrack) {\n await this.track.setPreferredLayer(this.track.getPreferredLayer());\n }\n }\n\n removeVideoElement(videoElement: HTMLVideoElement): void {\n this.track.removeSink(videoElement);\n this.videoElements.delete(videoElement);\n this.entries.delete(videoElement);\n this.resizeObserver?.unobserve(videoElement);\n this.intersectionObserver?.unobserve(videoElement);\n HMSLogger.d(this.TAG, `Removing video element for ${this.track}`);\n }\n\n getVideoElements(): HTMLVideoElement[] {\n return Array.from(this.videoElements);\n }\n\n private init() {\n if (isBrowser) {\n this.resizeObserver = HMSResizeObserver;\n this.intersectionObserver = HMSIntersectionObserver;\n }\n }\n\n private handleIntersection = async (entry: IntersectionObserverEntry) => {\n const isVisibile = getComputedStyle(entry.target).visibility === 'visible';\n // .contains check is needed for pip component as the video tiles are not mounted to dom element\n if (this.track.enabled && ((entry.isIntersecting && isVisibile) || !document.contains(entry.target))) {\n HMSLogger.d(this.TAG, 'add sink intersection', `${this.track}`, this.id);\n this.entries.set(entry.target as HTMLVideoElement, entry.boundingClientRect);\n await this.selectMaxLayer();\n await this.track.addSink(entry.target as HTMLVideoElement);\n } else {\n HMSLogger.d(this.TAG, 'remove sink intersection', `${this.track}`, this.id);\n await this.track.removeSink(entry.target as HTMLVideoElement);\n }\n };\n\n private handleResize = async (entry: ResizeObserverEntry) => {\n if (!this.track.enabled || !(this.track instanceof HMSRemoteVideoTrack)) {\n return;\n }\n this.entries.set(entry.target as HTMLVideoElement, entry.contentRect);\n await this.selectMaxLayer();\n };\n\n /**\n * Taken from\n * https://stackoverflow.com/a/125106/4321808\n */\n // eslint-disable-next-line complexity\n private isElementInViewport(el: HTMLElement) {\n let top = el.offsetTop;\n let left = el.offsetLeft;\n const width = el.offsetWidth;\n const height = el.offsetHeight;\n const { hidden } = el;\n const { opacity, display } = getComputedStyle(el);\n\n while (el.offsetParent) {\n el = el.offsetParent as HTMLElement;\n top += el.offsetTop;\n left += el.offsetLeft;\n }\n\n return (\n top < window.pageYOffset + window.innerHeight &&\n left < window.pageXOffset + window.innerWidth &&\n top + height > window.pageYOffset &&\n left + width > window.pageXOffset &&\n !hidden &&\n (opacity !== '' ? parseFloat(opacity) > 0 : true) &&\n display !== 'none'\n );\n }\n\n // eslint-disable-next-line complexity\n private async selectMaxLayer() {\n if (!(this.track instanceof HMSRemoteVideoTrack) || this.videoElements.size === 0) {\n return;\n }\n let maxLayer!: HMSPreferredSimulcastLayer;\n for (const element of this.videoElements) {\n const entry = this.entries.get(element);\n if (!entry) {\n continue;\n }\n const { width, height } = entry;\n if (width === 0 || height === 0) {\n continue;\n }\n const layer = getClosestLayer(this.track.getSimulcastDefinitions(), { width, height });\n if (!maxLayer) {\n maxLayer = layer;\n } else {\n maxLayer = layerToIntMapping[layer] > layerToIntMapping[maxLayer] ? layer : maxLayer;\n }\n }\n if (maxLayer) {\n HMSLogger.d(this.TAG, `selecting max layer ${maxLayer} for the track`, `${this.track}`);\n await this.track.setPreferredLayer(maxLayer);\n }\n }\n\n cleanup = () => {\n this.videoElements.forEach(videoElement => {\n videoElement.srcObject = null;\n this.resizeObserver?.unobserve(videoElement);\n this.intersectionObserver?.unobserve(videoElement);\n });\n this.videoElements.clear();\n this.resizeObserver = undefined;\n this.intersectionObserver = undefined;\n };\n}\n", "import { HMSPreferredSimulcastLayer, HMSSimulcastLayer, HMSSimulcastLayerDefinition } from '../../interfaces';\n\nexport const layerToIntMapping = {\n [HMSSimulcastLayer.NONE]: -1,\n [HMSSimulcastLayer.LOW]: 0,\n [HMSSimulcastLayer.MEDIUM]: 1,\n [HMSSimulcastLayer.HIGH]: 2,\n};\nconst DELTA_THRESHOLD = 0.5;\n\n/**\n * Given the simulcast layers and the current video element dimensions, this function finds the\n * layer with dimensions closer to the video element dimensions.\n */\n// eslint-disable-next-line complexity\nexport const getClosestLayer = (\n simulcastLayers: HMSSimulcastLayerDefinition[],\n videoElementDimensions: { width: number; height: number },\n): HMSPreferredSimulcastLayer => {\n let closestLayer: HMSPreferredSimulcastLayer = HMSSimulcastLayer.HIGH;\n // when both width and height are equal pick height to select a better quality\n const maxDimension = videoElementDimensions.width > videoElementDimensions.height ? 'width' : 'height';\n const layers = [...simulcastLayers].sort((a, b) => layerToIntMapping[a.layer] - layerToIntMapping[b.layer]);\n const videoDimesion = videoElementDimensions[maxDimension] * (window?.devicePixelRatio || 1);\n for (let i = 0; i < layers.length; i++) {\n const { resolution, layer } = layers[i];\n const layerDimension = resolution[maxDimension];\n // we break here because the layers are already sorted, the next would always be greater if the below condition satisifes\n if (videoDimesion <= layerDimension) {\n closestLayer = layer;\n break;\n } else {\n const nextLayer = layers[i + 1];\n const nextLayerDimension = nextLayer ? nextLayer.resolution[maxDimension] : Number.POSITIVE_INFINITY;\n // calculating which layer this dimension is closer to\n const proximityPercent = (videoDimesion - layerDimension) / (nextLayerDimension - layerDimension);\n if (proximityPercent < DELTA_THRESHOLD) {\n // the element's dimension is closer to the current layer\n closestLayer = layer;\n break;\n }\n }\n }\n return closestLayer;\n};\n", "import HMSLogger from './logger';\nimport { isBrowser } from './support';\n\nexport interface HMSIntersectionObserverCallback {\n (entry: IntersectionObserverEntry): void;\n}\n\n/**\n * This is a wrapper around IntersectionObserver which will call the callback passed\n * for an element while observing, only when that element is intersecting\n */\nexport class HMSIntersectionObserverWrapper {\n private intersectionObserver?: IntersectionObserver;\n private readonly TAG = '[HMSIntersectionObserverWrapper]';\n private listeners = new WeakMap<HTMLElement, HMSIntersectionObserverCallback>();\n constructor() {\n this.createObserver();\n }\n\n isSupported() {\n const isSupported = isBrowser && typeof window.IntersectionObserver !== 'undefined';\n if (!isSupported) {\n HMSLogger.w(this.TAG, 'IntersectionObserver is not supported, fallback will be used instead');\n }\n return isSupported;\n }\n\n observe = (element: HTMLElement, onIntersection: HMSIntersectionObserverCallback) => {\n this.createObserver();\n // unobserve before observing the element\n this.unobserve(element);\n this.intersectionObserver?.observe(element);\n this.listeners.set(element, onIntersection);\n };\n\n unobserve = (element: HTMLElement) => {\n this.intersectionObserver?.unobserve(element);\n this.listeners.delete(element);\n };\n\n private createObserver = () => {\n if (this.isSupported() && !this.intersectionObserver) {\n this.intersectionObserver = new IntersectionObserver(this.handleIntersection);\n }\n };\n\n private handleIntersection = (entries: IntersectionObserverEntry[]) => {\n for (const entry of entries) {\n this.listeners.get(entry.target as HTMLElement)?.(entry);\n }\n };\n}\n\nexport const HMSIntersectionObserver = new HMSIntersectionObserverWrapper();\n", "import HMSLogger from './logger';\nimport { isBrowser } from './support';\nimport { debounce } from './timer-utils';\n\nexport interface HMSResizeObserverCallback {\n (entry: ResizeObserverEntry): void;\n}\n\n/**\n * This is a wrapper around ResizeObserver which will call the callback passed\n * for an element while observing, only when that element is intersecting\n */\nexport class HMSResizeObserverWrapper {\n private resizeObserver?: ResizeObserver;\n private readonly TAG = '[HMSResizeObserverWrapper]';\n private listeners = new WeakMap<HTMLElement, HMSResizeObserverCallback>();\n constructor() {\n this.createObserver();\n }\n\n isSupported() {\n const isSupported = isBrowser && typeof window.ResizeObserver !== 'undefined';\n if (!isSupported) {\n HMSLogger.w(this.TAG, 'Resize Observer is not supported');\n }\n return isSupported;\n }\n\n observe = (\n element: HTMLElement,\n onResize: HMSResizeObserverCallback,\n options: ResizeObserverOptions = { box: 'border-box' },\n ) => {\n this.createObserver();\n // unobserve before observing the element\n this.unobserve(element);\n this.resizeObserver?.observe(element, options);\n this.listeners.set(element, onResize);\n };\n\n unobserve = (element: HTMLElement) => {\n this.resizeObserver?.unobserve(element);\n this.listeners.delete(element);\n };\n\n private createObserver = () => {\n if (this.isSupported() && !this.resizeObserver) {\n this.resizeObserver = new ResizeObserver(debounce(this.handleResize, 300));\n }\n };\n\n private handleResize = (entries: ResizeObserverEntry[]) => {\n for (const entry of entries) {\n this.listeners.get(entry.target as HTMLElement)?.(entry);\n }\n };\n}\n\nexport const HMSResizeObserver = new HMSResizeObserverWrapper();\n", "import { HMSPluginSupportResult } from '../audio';\n\n/**\n * A plugin implementing this interface can be registered with HMSLocalVideoTrack to transform, process or\n * analyze the local video track.These can include applications like video filters, virtual background, live\n * analysis of video etc. The below functions are required for the sdk to properly use the plugin, usually\n * the plugin would also be exposing some public functions of its own for the UI to control its working.\n */\nexport interface HMSVideoPlugin {\n /**\n * The name is meant to uniquely specify a plugin instance. This will be used to track number of plugins\n * added to the track, and same name won't be allowed twice.\n */\n getName(): string;\n\n /**\n * This function will be called before the call to init, it is used to check whether the plugin supports current\n * OS and device or not. An error will be thrown back to the user if they try to use an unsupported plugin.\n */\n checkSupport(): HMSPluginSupportResult;\n\n /**\n * @deprecated. Will be deleted in future updates. Use checkSupport instead.\n */\n isSupported(): boolean;\n\n /**\n * This function will be called in the beginning for initialization which may include tasks like setting up\n * variables, loading ML models etc. This can be used by a plugin to ensure it's prepared at the time\n * processVideoFrame is called.\n */\n init(): Promise<void>;\n\n /**\n * @see HMSVideoPluginType\n */\n getPluginType(): HMSVideoPluginType;\n\n getContextType?(): HMSVideoPluginCanvasContextType;\n\n /**\n * This function will be called by the SDK for every video frame which the plugin needs to process.\n * PluginFrameRate - the rate at which the plugin is expected to process the video frames. This is not necessarily\n * equal to the capture frame rate. The user can specify this rate, and the sdk might also change it on basis of\n * device type, or CPU usage.\n * For an analyzing plugin, the below function will be called at plugin framerate.\n * For a transforming plugin, the sdk will pass in the input and output at real frame rate with an additional boolean\n * pass. The expectation is that the plugin should use results of previous runs instead of doing a complex processing\n * again when pass is set to true. This helps in maintaining the framerate of the video as well as bringing down\n * CPU usage in case of complex processing.\n * @param input input canvas containing the input frame\n * @param output the output canvas which should contain the output frame\n * @param skipProcessing use results from previous run if true\n */\n processVideoFrame(\n input: HTMLCanvasElement,\n output?: HTMLCanvasElement,\n skipProcessing?: boolean,\n ): Promise<void> | void;\n\n /**\n * the plugin can use this function to dispose off its resources. It'll be called when the processor instance is\n * no longer needed at the end.\n */\n stop(): void;\n}\n\n/**\n * Specifies the type of the plugin a transforming plugin will get an output canvas to give the resulting\n * transformation. While an analyzing plugin will only be passed the input canvas.\n */\nexport enum HMSVideoPluginType {\n TRANSFORM = 'TRANSFORM',\n ANALYZE = 'ANALYZE',\n}\n\nexport enum HMSVideoPluginCanvasContextType {\n '2D' = '2d',\n WEBGL = 'webgl',\n 'WEBGL2' = 'webgl2',\n}\n", "export class RunningAverage {\n private total = 0;\n private count = 0;\n\n add(item: number) {\n this.count++;\n this.total += item;\n }\n\n getAvg(): number {\n return Math.floor(this.total / this.count);\n }\n\n reset() {\n this.total = 0;\n this.count = 0;\n }\n}\n", "import MediaPluginsAnalyticsFactory from '../../analytics/MediaPluginsAnalyticsFactory';\nimport { ErrorFactory } from '../../error/ErrorFactory';\nimport { HMSAction } from '../../error/HMSAction';\nimport { HMSException } from '../../error/HMSException';\nimport { EventBus } from '../../events/EventBus';\nimport HMSLogger from '../../utils/logger';\nimport { RunningAverage } from '../../utils/math';\n\nexport class VideoPluginsAnalytics {\n private readonly TAG = '[VideoPluginsAnalytics]';\n private readonly initTime: Record<string, number>;\n private readonly addedTimestamps: Record<string, number>;\n private readonly preProcessingAvgs: RunningAverage;\n private readonly processingAvgs: Record<string, RunningAverage>;\n private readonly pluginAdded: Record<string, boolean>;\n private readonly pluginInputFrameRate: Record<string, number>;\n private readonly pluginFrameRate: Record<string, number>;\n\n constructor(private eventBus: EventBus) {\n this.initTime = {};\n this.preProcessingAvgs = new RunningAverage();\n this.addedTimestamps = {};\n this.processingAvgs = {};\n this.pluginAdded = {};\n this.pluginInputFrameRate = {};\n this.pluginFrameRate = {};\n }\n\n added(name: string, inputFrameRate: number, pluginFrameRate?: number) {\n this.pluginAdded[name] = true;\n this.addedTimestamps[name] = Date.now();\n this.initTime[name] = 0;\n this.processingAvgs[name] = new RunningAverage();\n this.pluginInputFrameRate[name] = inputFrameRate;\n this.pluginFrameRate[name] = pluginFrameRate || inputFrameRate;\n }\n\n removed(name: string) {\n //send stats\n if (this.pluginAdded[name]) {\n const stats = {\n pluginName: name,\n // duration in seconds\n duration: Math.floor((Date.now() - this.addedTimestamps[name]) / 1000),\n loadTime: this.initTime[name],\n avgPreProcessingTime: this.preProcessingAvgs.getAvg(), //Do we need this in stat not plugin specific\n avgProcessingTime: this.processingAvgs[name]?.getAvg(),\n inputFrameRate: this.pluginInputFrameRate[name],\n pluginFrameRate: this.pluginFrameRate[name],\n };\n //send stats\n this.eventBus.analytics.publish(MediaPluginsAnalyticsFactory.stats(stats));\n //clean the plugin details\n this.clean(name);\n }\n }\n\n failure(name: string, error: HMSException) {\n // send failure event\n if (this.pluginAdded[name]) {\n this.eventBus.analytics.publish(MediaPluginsAnalyticsFactory.failure(name, error));\n //clean the plugin details\n this.clean(name);\n }\n }\n\n async initWithTime<T>(name: string, initFn: () => Promise<T>) {\n if (this.initTime[name]) {\n HMSLogger.i(this.TAG, `Plugin Already loaded ${name}, time it took: ${this.initTime[name]}`);\n return;\n }\n let time: number | undefined = undefined;\n try {\n time = await this.timeInMs(initFn);\n HMSLogger.i(this.TAG, `Time taken for Plugin ${name} initialization : ${time}`);\n } catch (e) {\n //Failed during initialization of plugin(model loading etc...)\n const err = ErrorFactory.MediaPluginErrors.InitFailed(\n HMSAction.VIDEO_PLUGINS,\n `failed during initialization of plugin${(e as Error).message || e}`,\n );\n HMSLogger.e(this.TAG, err);\n this.failure(name, err);\n throw err;\n }\n if (time) {\n this.initTime[name] = time;\n }\n }\n\n async preProcessWithTime<T>(preProcessFn: () => Promise<T>) {\n //TODO: check if it is required to maintain and shall we handle preprocess failures\n const time = await this.timeInMs(preProcessFn);\n this.preProcessingAvgs.add(time);\n }\n\n async processWithTime<T>(name: string, processFn: () => Promise<T>) {\n let time: number | undefined = undefined;\n try {\n time = await this.timeInMs(processFn);\n } catch (e) {\n //Failed during processing of plugin\n const err = ErrorFactory.MediaPluginErrors.ProcessingFailed(\n HMSAction.VIDEO_PLUGINS,\n `Failed during processing of plugin${(e as Error).message || e}`,\n );\n HMSLogger.e(this.TAG, err);\n this.failure(name, err);\n throw err;\n }\n if (time) {\n this.processingAvgs[name]?.add(time);\n }\n }\n\n private async timeInMs<T>(fn: () => Promise<T>): Promise<number> {\n const start = Date.now();\n await fn();\n return Math.floor(Date.now() - start);\n }\n\n private clean(name: string) {\n delete this.addedTimestamps[name];\n delete this.initTime[name];\n delete this.processingAvgs[name];\n delete this.pluginAdded[name];\n delete this.pluginInputFrameRate[name];\n delete this.pluginFrameRate[name];\n }\n}\n", "/* eslint-disable complexity */\nimport { HMSVideoPlugin, HMSVideoPluginCanvasContextType, HMSVideoPluginType } from './HMSVideoPlugin';\nimport { VideoPluginsAnalytics } from './VideoPluginsAnalytics';\nimport { ErrorFactory } from '../../error/ErrorFactory';\nimport { HMSAction } from '../../error/HMSAction';\nimport { EventBus } from '../../events/EventBus';\nimport { HMSLocalVideoTrack } from '../../media/tracks';\nimport HMSLogger from '../../utils/logger';\nimport { sleep } from '../../utils/timer-utils';\nimport { HMSPluginUnsupportedTypes } from '../audio';\n\nconst DEFAULT_FRAME_RATE = 24;\nconst DEFAULT_WIDTH = 320;\nconst DEFAULT_HEIGHT = 240;\n\ninterface CanvasElement extends HTMLCanvasElement {\n captureStream(frameRate?: number): MediaStream;\n}\n\n/**\n * This class manages applying different plugins on a local video track. Plugins which need to modify the video\n * are called in the order they were added. Plugins which do not need to modify the video frames are called\n * with the original input.\n *\n * Concepts -\n * Video Plugin - A module which can take in input video painted on a canvas, do some processing on it and optionally\n * render its output on a passed in output canvas which will be shown in the UI.\n *\n * frameRate - the frame rate of the input video as present in track.getSettings, this is the rate at which new frames\n * are being produced and the rate we need to maintain in output as well.\n *\n * pluginFrameRate - this is the rate at which the plugin is supposed to do its processing. The processing can be an\n * expensive operation and can result in high usage of resources like CPU. This rate would usually be lower than the\n * real frame rate.\n *\n * pluginsLoop - a loop is run at framerate in this class, on each loop if the original track is unmuted all added\n * plugins are called one by one in the order they were called.\n *\n * @see HMSVideoPlugin\n */\nexport class HMSVideoPluginsManager {\n private readonly TAG = '[VideoPluginsManager]';\n\n /**\n * plugins loop is the loop in which all plugins are applied\n */\n private pluginsLoopRunning = false;\n private pluginsLoopState: 'paused' | 'running' = 'paused';\n private readonly hmsTrack: HMSLocalVideoTrack;\n private readonly pluginsMap: Map<string, HMSVideoPlugin>; // plugin names to their instance mapping\n private inputVideo?: HTMLVideoElement;\n private inputCanvas?: CanvasElement;\n private outputCanvas?: CanvasElement;\n private outputTrack?: MediaStreamTrack;\n private analytics: VideoPluginsAnalytics;\n private pluginAddInProgress = false;\n private pluginNumFramesToSkip: Record<string, number>;\n private pluginNumFramesSkipped: Record<string, number>;\n private canvases: Array<CanvasElement>; //array of canvases to store intermediate result\n\n constructor(track: HMSLocalVideoTrack, eventBus: EventBus) {\n this.hmsTrack = track;\n this.pluginsMap = new Map();\n this.pluginNumFramesToSkip = {};\n this.pluginNumFramesSkipped = {};\n this.analytics = new VideoPluginsAnalytics(eventBus);\n this.canvases = new Array<CanvasElement>();\n }\n\n getPlugins(): string[] {\n return Array.from(this.pluginsMap.keys());\n }\n\n /**\n * @param plugin\n * @param pluginFrameRate\n */\n async addPlugin(plugin: HMSVideoPlugin, pluginFrameRate?: number) {\n if (this.pluginAddInProgress) {\n const name = plugin.getName?.();\n if (!name || name === '') {\n HMSLogger.w('no name provided by the plugin');\n return;\n }\n\n const err = ErrorFactory.MediaPluginErrors.AddAlreadyInProgress(\n HMSAction.VIDEO_PLUGINS,\n 'Add Plugin is already in Progress',\n );\n this.analytics.failure(name, err);\n\n HMSLogger.w(\"can't add another plugin when previous add is in progress\");\n throw err;\n }\n\n this.pluginAddInProgress = true;\n\n try {\n await this.addPluginInternal(plugin, pluginFrameRate);\n } finally {\n this.pluginAddInProgress = false;\n }\n }\n\n private async addPluginInternal(plugin: HMSVideoPlugin, pluginFrameRate?: number) {\n const name = plugin.getName?.();\n if (!name || name === '') {\n HMSLogger.w('no name provided by the plugin');\n return;\n }\n if (this.pluginsMap.has(name)) {\n HMSLogger.w(this.TAG, `plugin - ${plugin.getName()} already added.`);\n return;\n }\n //TODO: assuming this inputFrameRate from getMediaTrackSettings will not change once set\n //TODO: even if it changes will not have the info/params to know the change\n const inputFrameRate = this.hmsTrack.getMediaTrackSettings().frameRate || DEFAULT_FRAME_RATE;\n\n let numFramesToSkip = 0;\n if (pluginFrameRate && pluginFrameRate > 0) {\n HMSLogger.i(this.TAG, `adding plugin ${plugin.getName()} with framerate ${pluginFrameRate}`);\n if (pluginFrameRate < inputFrameRate) {\n numFramesToSkip = Math.ceil(inputFrameRate / pluginFrameRate) - 1;\n }\n this.analytics.added(name, inputFrameRate, pluginFrameRate);\n } else {\n HMSLogger.i(this.TAG, `adding plugin ${plugin.getName()}`);\n this.analytics.added(name, inputFrameRate);\n }\n\n HMSLogger.i(this.TAG, 'numFrames to skip processing', numFramesToSkip);\n this.pluginNumFramesToSkip[name] = numFramesToSkip;\n this.pluginNumFramesSkipped[name] = numFramesToSkip;\n\n this.validateAndThrow(name, plugin);\n\n try {\n await this.analytics.initWithTime(name, async () => await plugin.init());\n this.pluginsMap.set(name, plugin);\n // add new canvases according to new added plugins\n if (this.pluginsMap.size + 1 > this.canvases.length) {\n for (let i = this.canvases.length; i <= this.pluginsMap.size; i++) {\n this.canvases[i] = document.createElement('canvas') as CanvasElement;\n }\n }\n await this.startPluginsLoop(plugin.getContextType?.());\n } catch (err) {\n HMSLogger.e(this.TAG, 'failed to add plugin', err);\n await this.removePlugin(plugin);\n throw err;\n }\n }\n\n validatePlugin(plugin: HMSVideoPlugin) {\n return plugin.checkSupport();\n }\n\n validateAndThrow(name: string, plugin: HMSVideoPlugin) {\n const result = this.validatePlugin(plugin);\n if (result.isSupported) {\n HMSLogger.i(this.TAG, `plugin is supported,- ${plugin.getName()}`);\n } else {\n let error;\n switch (result.errType) {\n case HMSPluginUnsupportedTypes.PLATFORM_NOT_SUPPORTED:\n error = ErrorFactory.MediaPluginErrors.PlatformNotSupported(\n HMSAction.VIDEO_PLUGINS,\n 'platform not supported, see docs',\n );\n this.analytics.failure(name, error);\n throw error;\n case HMSPluginUnsupportedTypes.DEVICE_NOT_SUPPORTED:\n error = ErrorFactory.MediaPluginErrors.DeviceNotSupported(\n HMSAction.VIDEO_PLUGINS,\n 'video device not supported, see docs',\n );\n this.analytics.failure(name, error);\n throw error;\n }\n }\n }\n\n async removePlugin(plugin: HMSVideoPlugin) {\n const name = plugin.getName();\n if (!this.pluginsMap.get(name)) {\n HMSLogger.w(this.TAG, `plugin - ${name} not found to remove.`);\n return;\n }\n HMSLogger.i(this.TAG, `removing plugin ${name}`);\n this.removePluginEntry(name);\n if (this.pluginsMap.size === 0) {\n HMSLogger.i(this.TAG, `No plugins left, stopping plugins loop`);\n await this.stopPluginsLoop();\n }\n plugin.stop();\n this.analytics.removed(name);\n }\n\n removePluginEntry(name: string) {\n this.pluginsMap.delete(name);\n if (this.pluginNumFramesToSkip[name]) {\n delete this.pluginNumFramesToSkip[name];\n }\n if (this.pluginNumFramesSkipped[name]) {\n delete this.pluginNumFramesSkipped[name];\n }\n }\n\n /**\n * when video is unmuted it takes some time for all the plugins to be re run and an output stream to be\n * produced. It can await on this function to confirm and tell the new unmuted state.\n * If this is not awaited on video will freeze with a frame from past run.\n */\n async waitForRestart() {\n if (!this.pluginsLoopRunning || this.pluginsLoopState === 'running') {\n return;\n }\n while (this.pluginsLoopState === 'paused') {\n await sleep(100);\n }\n }\n\n /**\n * remove every plugin one by one\n */\n async cleanup() {\n for (const plugin of this.pluginsMap.values()) {\n await this.removePlugin(plugin);\n }\n // memory cleanup\n this.outputTrack?.stop();\n }\n\n private initElementsAndStream(contextType?: HMSVideoPluginCanvasContextType) {\n if (!this.inputCanvas) {\n this.inputCanvas = document.createElement('canvas') as CanvasElement;\n }\n this.outputCanvas = document.createElement('canvas') as CanvasElement;\n if (!this.inputVideo) {\n this.inputVideo = document.createElement('video');\n }\n // FF issue https://bugzilla.mozilla.org/show_bug.cgi?id=1388974\n this.inputCanvas.getContext('2d');\n this.outputCanvas.getContext(contextType || HMSVideoPluginCanvasContextType['2D']);\n // capture stream automatically uses the framerate at which the output canvas is changing\n const outputStream = this.outputCanvas.captureStream();\n this.outputTrack = outputStream.getVideoTracks()[0];\n }\n\n private async startPluginsLoop(contextType?: HMSVideoPluginCanvasContextType) {\n if (this.pluginsLoopRunning) {\n return;\n }\n this.initElementsAndStream(contextType);\n this.pluginsLoopRunning = true;\n try {\n await this.hmsTrack.setProcessedTrack(this.outputTrack);\n } catch (err) {\n this.pluginsLoopRunning = false;\n HMSLogger.e(this.TAG, 'error in setting processed track', err);\n throw err;\n }\n // can't await on pluginsLoop as it'll run for a long long time\n this.pluginsLoop().then(() => {\n HMSLogger.d(this.TAG, 'processLoop stopped');\n });\n }\n\n private async stopPluginsLoop() {\n this.pluginsLoopRunning = false;\n await this.hmsTrack.setProcessedTrack(undefined);\n this.resetCanvases();\n this.outputTrack?.stop();\n if (this.inputVideo) {\n this.inputVideo.srcObject = null;\n this.inputVideo = undefined;\n }\n }\n\n private async pluginsLoop() {\n while (this.pluginsLoopRunning) {\n const inputFrameRate = this.hmsTrack.getMediaTrackSettings().frameRate || DEFAULT_FRAME_RATE;\n const sleepTimeMs = Math.floor(1000 / inputFrameRate);\n if (!this.hmsTrack.enabled || this.hmsTrack.nativeTrack.readyState === 'ended') {\n if (this.pluginsLoopState === 'running') {\n // mute just happened, reset canvases to black so even if it is sent to remote, it\n // is a black screen instead of a stucked frame from previous run\n this.resetCanvases();\n }\n this.pluginsLoopState = 'paused';\n await sleep(sleepTimeMs);\n continue;\n }\n let processingTime = 0;\n try {\n await this.analytics.preProcessWithTime(async () => await this.doPreProcessing());\n const start = Date.now();\n await this.processFramesThroughPlugins();\n processingTime = Math.floor(Date.now() - start);\n if (processingTime > sleepTimeMs) {\n processingTime = sleepTimeMs;\n }\n } catch (err) {\n // TODO: handle failures properly, detect which plugin failed, stop it and notify back to the UI\n HMSLogger.e(this.TAG, 'error in plugins loop', err);\n }\n this.pluginsLoopState = 'running';\n // take into account processing time to decide time to wait for the next loop\n await sleep(sleepTimeMs - processingTime);\n }\n }\n\n private async doPreProcessing() {\n await this.addTrackToVideo(); // ensure current native track is playing in video\n await this.updateInputCanvas(); // put the latest video frame on input canvas\n }\n\n /**\n * pass the input canvas through all plugins in a loop\n * @private\n */\n private async processFramesThroughPlugins() {\n this.canvases[0] = this.inputCanvas!;\n let i = 0;\n for (const plugin of this.pluginsMap.values()) {\n const name = plugin.getName();\n if (!plugin) {\n continue;\n }\n try {\n const skipProcessing = this.checkIfSkipRequired(name);\n\n if (plugin.getPluginType() === HMSVideoPluginType.TRANSFORM) {\n const process = async (input: CanvasElement, output: CanvasElement) => {\n try {\n await plugin.processVideoFrame(input, output, skipProcessing);\n } catch (err) {\n HMSLogger.e(this.TAG, `error in processing plugin ${name}`, err);\n }\n };\n if (!skipProcessing) {\n const currentCanvas = this.canvases[i];\n const nextCanvas = this.canvases[i + 1];\n if (i === this.pluginsMap.size - 1) {\n await this.analytics.processWithTime(name, async () => process(currentCanvas, this.outputCanvas!));\n } else {\n await this.analytics.processWithTime(name, async () => process(currentCanvas, nextCanvas));\n }\n } else {\n if (i === this.pluginsMap.size - 1) {\n await process(this.canvases[i], this.outputCanvas!);\n } else {\n await process(this.canvases[i], this.canvases[i + 1]);\n }\n }\n } else if (plugin.getPluginType() === HMSVideoPluginType.ANALYZE && !skipProcessing) {\n // there is no need to await for this case\n await this.analytics.processWithTime(name, async () => await plugin.processVideoFrame(this.inputCanvas!));\n }\n } catch (err) {\n //TODO error happened on processing of plugin notify UI\n HMSLogger.e(this.TAG, `error in processing plugin ${name}`, err);\n //remove plugin from loop and stop analytics for it\n await this.removePlugin(plugin);\n }\n i++;\n }\n }\n\n /**\n * add the current native track to the inputVideoElement if it's not already added.\n * @private\n */\n private async addTrackToVideo() {\n if (!this.inputVideo) {\n return;\n }\n const srcObject = this.inputVideo.srcObject;\n if (srcObject !== null && srcObject instanceof MediaStream) {\n const existingTrackID = srcObject.getVideoTracks()[0]?.id;\n if (existingTrackID === this.hmsTrack.nativeTrack.id) {\n // it's already attached\n return;\n }\n }\n this.inputVideo.pause();\n this.inputVideo.srcObject = new MediaStream([this.hmsTrack.nativeTrack]);\n this.inputVideo.muted = true;\n this.inputVideo.playsInline = true;\n await this.inputVideo.play();\n }\n\n /**\n * get the new video frame from input video element and put it on canvas\n * @private\n */\n private async updateInputCanvas() {\n if (!this.inputCanvas || !this.inputVideo) {\n return;\n }\n const { width = DEFAULT_WIDTH, height = DEFAULT_HEIGHT } = this.hmsTrack.getMediaTrackSettings();\n // TODO: should we reduce height/width to optimize?\n if (this.inputCanvas.height !== height) {\n this.inputCanvas.height = height;\n }\n if (this.inputCanvas.width !== width) {\n this.inputCanvas.width = width;\n }\n const ctx = this.inputCanvas.getContext('2d');\n ctx!.drawImage(this.inputVideo, 0, 0, width, height);\n }\n\n private resetCanvases() {\n if (!this.outputCanvas || !this.inputCanvas) {\n return;\n }\n const inputCtx = this.inputCanvas.getContext('2d');\n if (inputCtx) {\n inputCtx.fillStyle = `rgb(0, 0, 0)`;\n inputCtx.fillRect(0, 0, this.outputCanvas.width, this.outputCanvas.height);\n }\n this.canvases = [];\n }\n\n /**\n N = ceil(inputFrameRate/pluginFrameRate) - 1\n N = this.pluginNumFramesToSkip[name] = frames to skip for every processed frame\n all the frames we are skipping are using the previous frame output\n **/\n private checkIfSkipRequired(name: string) {\n let skip = false;\n\n if (this.pluginNumFramesSkipped[name] < this.pluginNumFramesToSkip[name]) {\n this.pluginNumFramesSkipped[name]++;\n skip = true;\n } else {\n skip = false;\n this.pluginNumFramesSkipped[name] = 0;\n }\n\n return skip;\n }\n}\n", "import { HMSVideoTrack } from './HMSVideoTrack';\nimport { VideoElementManager } from './VideoElementManager';\nimport { DeviceStorageManager } from '../../device-manager/DeviceStorage';\nimport { ErrorFactory } from '../../error/ErrorFactory';\nimport { HMSAction } from '../../error/HMSAction';\nimport { EventBus } from '../../events/EventBus';\nimport {\n HMSFacingMode,\n HMSSimulcastLayerDefinition,\n HMSVideoTrackSettings as IHMSVideoTrackSettings,\n ScreenCaptureHandle,\n} from '../../interfaces';\nimport { HMSPluginSupportResult, HMSVideoPlugin } from '../../plugins';\nimport { HMSVideoPluginsManager } from '../../plugins/video';\nimport { LocalTrackManager } from '../../sdk/LocalTrackManager';\nimport HMSLogger from '../../utils/logger';\nimport { getVideoTrack, isEmptyTrack } from '../../utils/track';\nimport { HMSVideoTrackSettings, HMSVideoTrackSettingsBuilder } from '../settings';\nimport { HMSLocalStream } from '../streams';\n\nfunction generateHasPropertyChanged(newSettings: Partial<HMSVideoTrackSettings>, oldSettings: HMSVideoTrackSettings) {\n return function hasChanged(\n prop: 'codec' | 'width' | 'height' | 'maxFramerate' | 'maxBitrate' | 'deviceId' | 'advanced' | 'facingMode',\n ) {\n return prop in newSettings && newSettings[prop] !== oldSettings[prop];\n };\n}\n\nexport class HMSLocalVideoTrack extends HMSVideoTrack {\n settings: HMSVideoTrackSettings;\n private pluginsManager: HMSVideoPluginsManager;\n private processedTrack?: MediaStreamTrack;\n private _layerDefinitions: HMSSimulcastLayerDefinition[] = [];\n private TAG = '[HMSLocalVideoTrack]';\n\n /**\n * true if it's screenshare and current tab is what is being shared. Browser dependent, Chromium only\n * at the point of writing this comment.\n */\n isCurrentTab = false;\n\n /**\n * @internal\n * This is required for handling remote mute/unmute as the published track will not necessarily be same as\n * the first track id or current native track's id.\n * It won't be same as first track id if the native track was changed after preview started but before join happened,\n * with device change, or mute/unmute.\n * It won't be same as native track id, as the native track can change post join(and publish), when the nativetrack\n * changes, replacetrack is used which doesn't involve republishing which means from server's point of view, the track id\n * is same as what was initially published.\n * This will only be available if the track was actually published and won't be set for preview tracks.\n */\n publishedTrackId?: string;\n\n /**\n * will be false for preview tracks\n */\n isPublished = false;\n\n constructor(\n stream: HMSLocalStream,\n track: MediaStreamTrack,\n source: string,\n private eventBus: EventBus,\n settings: HMSVideoTrackSettings = new HMSVideoTrackSettingsBuilder().build(),\n ) {\n super(stream, track, source);\n stream.tracks.push(this);\n this.setVideoHandler(new VideoElementManager(this));\n this.settings = settings;\n // Replace the 'default' or invalid deviceId with the actual deviceId\n // This is to maintain consistency with selected devices as in some cases there will be no 'default' device\n if (settings.deviceId !== track.getSettings().deviceId && track.enabled) {\n this.settings = this.buildNewSettings({ deviceId: track.getSettings().deviceId });\n }\n this.pluginsManager = new HMSVideoPluginsManager(this, eventBus);\n this.setFirstTrackId(this.trackId);\n }\n\n /** @internal */\n setSimulcastDefinitons(definitions: HMSSimulcastLayerDefinition[]) {\n this._layerDefinitions = definitions;\n }\n\n /**\n * Method to get available simulcast definitions for the track\n * @returns {HMSSimulcastLayerDefinition[]}\n */\n getSimulcastDefinitions(): HMSSimulcastLayerDefinition[] {\n return this._layerDefinitions;\n }\n\n /**\n * use this function to set the enabled state of a track. If true the track will be unmuted and muted otherwise.\n * @param value\n */\n async setEnabled(value: boolean): Promise<void> {\n if (value === this.enabled) {\n return;\n }\n if (this.source === 'regular') {\n let track: MediaStreamTrack;\n if (value) {\n track = await this.replaceTrackWith(this.settings);\n } else {\n track = await this.replaceTrackWithBlank();\n }\n await this.replaceSender(track, value);\n this.nativeTrack?.stop();\n this.nativeTrack = track;\n await super.setEnabled(value);\n if (value) {\n await this.pluginsManager.waitForRestart();\n this.settings = this.buildNewSettings({ deviceId: track.getSettings().deviceId });\n }\n this.videoHandler.updateSinks();\n }\n this.eventBus.localVideoEnabled.publish({ enabled: value, track: this });\n }\n\n /**\n * verify if the track id being passed is of this track for correlating server messages like degradation\n */\n isPublishedTrackId(trackId: string) {\n return this.publishedTrackId === trackId;\n }\n\n /**\n * @see HMSVideoTrack#addSink()\n */\n addSink(videoElement: HTMLVideoElement) {\n this.addSinkInternal(videoElement, this.processedTrack || this.nativeTrack);\n }\n\n /**\n * This function can be used to set media track settings. Frequent options -\n * deviceID: can be used to change to different input source\n * width, height - can be used to change capture dimensions\n * maxFramerate - can be used to control the capture framerate\n * @param settings\n */\n async setSettings(settings: Partial<IHMSVideoTrackSettings>, internal = false) {\n const newSettings = this.buildNewSettings(settings);\n await this.handleDeviceChange(newSettings, internal);\n if (!this.enabled || isEmptyTrack(this.nativeTrack)) {\n // if track is muted, we just cache the settings for when it is unmuted\n this.settings = newSettings;\n return;\n }\n await this.handleSettingsChange(newSettings);\n this.settings = newSettings;\n }\n\n /**\n * @see HMSVideoPlugin\n */\n getPlugins(): string[] {\n return this.pluginsManager.getPlugins();\n }\n\n /**\n * @see HMSVideoPlugin\n */\n async addPlugin(plugin: HMSVideoPlugin, pluginFrameRate?: number): Promise<void> {\n return this.pluginsManager.addPlugin(plugin, pluginFrameRate);\n }\n\n /**\n * @see HMSVideoPlugin\n */\n async removePlugin(plugin: HMSVideoPlugin): Promise<void> {\n return this.pluginsManager.removePlugin(plugin);\n }\n\n /**\n * @see HMSVideoPlugin\n */\n validatePlugin(plugin: HMSVideoPlugin): HMSPluginSupportResult {\n return this.pluginsManager.validatePlugin(plugin);\n }\n\n /**\n * @internal\n */\n async cleanup() {\n super.cleanup();\n this.transceiver = undefined;\n await this.pluginsManager.cleanup();\n this.processedTrack?.stop();\n this.isPublished = false;\n }\n\n /**\n * only for screenshare track to crop to a cropTarget\n * @internal\n */\n async cropTo(cropTarget?: object) {\n if (!cropTarget) {\n return;\n }\n if (this.source !== 'screen') {\n return;\n }\n try {\n // @ts-ignore\n if (this.nativeTrack.cropTo) {\n // @ts-ignore\n await this.nativeTrack.cropTo(cropTarget);\n }\n } catch (err) {\n HMSLogger.e(this.TAG, 'failed to crop screenshare capture - ', err);\n throw ErrorFactory.TracksErrors.GenericTrack(HMSAction.TRACK, 'failed to crop screenshare capture');\n }\n }\n\n /**\n * only for screenshare track to get the captureHandle\n * TODO: add an API for capturehandlechange event\n * @internal\n */\n getCaptureHandle(): ScreenCaptureHandle | undefined {\n // @ts-ignore\n if (this.nativeTrack.getCaptureHandle) {\n // @ts-ignore\n return this.nativeTrack.getCaptureHandle();\n }\n return undefined;\n }\n\n /**\n * once the plugin manager has done its processing it can set or remove processed track via this method\n * note that replacing sender track only makes sense if the native track is enabled. if it's disabled there is\n * no point in replacing it. We'll update the processed track variable though so next time unmute happens\n * it's set properly.\n * @internal\n */\n async setProcessedTrack(processedTrack?: MediaStreamTrack) {\n // required replacement will happen when video is unmuted\n if (!this.nativeTrack.enabled) {\n this.processedTrack = processedTrack;\n return;\n }\n await this.removeOrReplaceProcessedTrack(processedTrack);\n this.videoHandler.updateSinks();\n }\n\n /**\n * @internal\n * sent track id will be different in case there was some processing done using plugins.\n * replace track is used to, start sending data from a new track without un publishing the prior one. There\n * are thus two track ids - the one which was initially published and should be unpublished when required.\n * The one whose data is currently being sent, which will be used when removing from connection senders.\n */\n getTrackIDBeingSent() {\n return this.getTrackBeingSent().id;\n }\n\n getTrackBeingSent() {\n return this.enabled ? this.processedTrack || this.nativeTrack : this.nativeTrack;\n }\n\n /**\n * will change the facingMode to environment if current facing mode is user or vice versa.\n * will be useful when on mobile web to toggle between front and back camera's\n */\n async switchCamera() {\n const currentFacingMode = this.getMediaTrackSettings().facingMode;\n if (!currentFacingMode || this.source !== 'regular') {\n HMSLogger.d(this.TAG, 'facingMode not supported');\n return;\n }\n const facingMode = currentFacingMode === HMSFacingMode.ENVIRONMENT ? HMSFacingMode.USER : HMSFacingMode.ENVIRONMENT;\n this.nativeTrack?.stop();\n const track = await this.replaceTrackWith(this.buildNewSettings({ facingMode: facingMode, deviceId: undefined }));\n await this.replaceSender(track, this.enabled);\n this.nativeTrack = track;\n this.videoHandler.updateSinks();\n this.settings = this.buildNewSettings({ deviceId: this.nativeTrack.getSettings().deviceId, facingMode });\n DeviceStorageManager.updateSelection('videoInput', {\n deviceId: this.settings.deviceId,\n groupId: this.nativeTrack.getSettings().groupId,\n });\n }\n\n /**\n * called when the video is unmuted\n * @private\n */\n private async replaceTrackWith(settings: HMSVideoTrackSettings) {\n const prevTrack = this.nativeTrack;\n /**\n * not stopping previous track results in device in use more frequently, as many devices will not allow even if\n * you are requesting for a new device.\n * Note: Do not change the order of this.\n */\n prevTrack?.stop();\n const newTrack = await getVideoTrack(settings);\n HMSLogger.d(this.TAG, 'replaceTrack, Previous track stopped', prevTrack, 'newTrack', newTrack);\n // Replace deviceId with actual deviceId when it is default\n if (this.settings.deviceId === 'default') {\n this.settings = this.buildNewSettings({ deviceId: this.nativeTrack.getSettings().deviceId });\n }\n return newTrack;\n }\n\n /**\n * called when the video is muted. A blank track is used to replace the original track. This is in order to\n * turn off the camera light and keep the bytes flowing to avoid av sync, timestamp issues.\n * @private\n */\n private async replaceTrackWithBlank() {\n const prevTrack = this.nativeTrack;\n const newTrack = LocalTrackManager.getEmptyVideoTrack(prevTrack);\n prevTrack?.stop();\n HMSLogger.d(this.TAG, 'replaceTrackWithBlank, Previous track stopped', prevTrack, 'newTrack', newTrack);\n return newTrack;\n }\n\n private async replaceSender(newTrack: MediaStreamTrack, enabled: boolean) {\n const localStream = this.stream as HMSLocalStream;\n if (enabled) {\n await localStream.replaceSenderTrack(this.nativeTrack, this.processedTrack || newTrack);\n } else {\n await localStream.replaceSenderTrack(this.processedTrack || this.nativeTrack, newTrack);\n }\n await localStream.replaceStreamTrack(this.nativeTrack, newTrack);\n }\n\n private buildNewSettings = (settings: Partial<HMSVideoTrackSettings>) => {\n const { width, height, codec, maxFramerate, maxBitrate, deviceId, advanced, facingMode } = {\n ...this.settings,\n ...settings,\n };\n const newSettings = new HMSVideoTrackSettings(\n width,\n height,\n codec,\n maxFramerate,\n deviceId,\n advanced,\n maxBitrate,\n facingMode,\n );\n return newSettings;\n };\n\n private handleSettingsChange = async (settings: HMSVideoTrackSettings) => {\n const stream = this.stream as HMSLocalStream;\n const hasPropertyChanged = generateHasPropertyChanged(settings, this.settings);\n if (hasPropertyChanged('maxBitrate') && settings.maxBitrate) {\n await stream.setMaxBitrateAndFramerate(this);\n }\n\n if (hasPropertyChanged('width') || hasPropertyChanged('height') || hasPropertyChanged('advanced')) {\n const track = await this.replaceTrackWith(settings);\n await this.replaceSender(track, this.enabled);\n this.nativeTrack = track;\n this.videoHandler.updateSinks();\n }\n };\n\n /**\n * Replace video track with new track on device change\n * @param settings - VideoSettings Object constructed with new settings\n * @param internal - whether the change was because of internal sdk call or external client call\n */\n private handleDeviceChange = async (settings: HMSVideoTrackSettings, internal = false) => {\n const hasPropertyChanged = generateHasPropertyChanged(settings, this.settings);\n\n if (hasPropertyChanged('deviceId') && this.source === 'regular') {\n if (this.enabled) {\n delete settings.facingMode;\n const track = await this.replaceTrackWith(settings);\n await this.replaceSender(track, this.enabled);\n this.nativeTrack = track;\n this.videoHandler.updateSinks();\n }\n if (!internal) {\n DeviceStorageManager.updateSelection('videoInput', {\n deviceId: settings.deviceId,\n groupId: this.nativeTrack.getSettings().groupId,\n });\n }\n }\n };\n\n /**\n * This will either remove or update the processedTrack value on the class instance.\n * It will also replace sender if the processedTrack is updated\n * @param {MediaStreamTrack|undefined}processedTrack\n */\n private removeOrReplaceProcessedTrack = async (processedTrack?: MediaStreamTrack) => {\n // if all plugins are removed reset everything back to native track\n if (!processedTrack) {\n if (this.processedTrack) {\n // remove, reset back to the native track\n await (this.stream as HMSLocalStream).replaceSenderTrack(this.processedTrack, this.nativeTrack);\n }\n this.processedTrack = undefined;\n } else if (processedTrack !== this.processedTrack) {\n if (this.processedTrack) {\n // replace previous processed track with new one\n await (this.stream as HMSLocalStream).replaceSenderTrack(this.processedTrack, processedTrack);\n } else {\n // there is no prev processed track, replace native with new one\n await (this.stream as HMSLocalStream).replaceSenderTrack(this.nativeTrack, processedTrack);\n }\n this.processedTrack = processedTrack;\n }\n };\n}\n", "export const RENEGOTIATION_CALLBACK_ID = 'renegotiation-callback-id';\nexport const API_DATA_CHANNEL = 'ion-sfu';\nexport const ANALYTICS_BUFFER_SIZE = 100;\n\n/**\n * Maximum number of retries that transport-layer will try\n * before giving up on the connection and returning a failure\n *\n * Refer https://100ms.atlassian.net/browse/HMS-2369\n */\nexport const MAX_TRANSPORT_RETRIES = 5;\nexport const MAX_TRANSPORT_RETRY_DELAY = 60;\n\nexport const DEFAULT_SIGNAL_PING_TIMEOUT = 12_000;\nexport const DEFAULT_SIGNAL_PING_INTERVAL = 3_000;\nexport const PONG_RESPONSE_TIMES_SIZE = 5;\n\nexport const SUBSCRIBE_ICE_CONNECTION_CALLBACK_ID = 'SUBSCRIBE_ICE_CONNECTION_CALLBACK_ID';\nexport const SUBSCRIBE_TIMEOUT = 60_000;\n\nexport const ICE_DISCONNECTION_TIMEOUT = 5_000;\n\nexport const RTC_STATS_MONITOR_INTERVAL = 1_000;\n\nexport const MAINTAIN_TRACK_HISTORY = false;\n\nexport const CLIENT_ANAYLTICS_PROD_ENDPOINT = 'https://event.100ms.live/v2/client/report';\nexport const CLIENT_ANAYLTICS_QA_ENDPOINT = 'https://event-nonprod.100ms.live/v2/client/report';\nexport const CLIENT_ANAYLTICS_STORAGE_LIMIT = 100;\n\nexport const PUBLISH_STATS_SAMPLE_WINDOW = 30;\nexport const PUBLISH_STATS_PUSH_INTERVAL = 300;\n\nexport const HMSEvents = {\n DEVICE_CHANGE: 'device-change',\n LOCAL_AUDIO_ENABLED: 'local-audio-enabled',\n LOCAL_VIDEO_ENABLED: 'local-video-enabled',\n STATS_UPDATE: 'stats-update', // emitted by HMSWebrtcInternals\n RTC_STATS_UPDATE: 'rtc-stats-update', // emitted by RTCStatsMonitor\n TRACK_DEGRADED: 'track-degraded',\n TRACK_RESTORED: 'track-restored',\n TRACK_AUDIO_LEVEL_UPDATE: 'track-audio-level-update',\n LOCAL_AUDIO_SILENCE: 'local-audio-silence',\n ANALYTICS: 'analytics',\n AUDIO_PLUGIN_FAILED: 'audio-plugin-failed',\n POLICY_CHANGE: 'policy-change',\n LOCAL_ROLE_UPDATE: 'local-role-update',\n AUDIO_TRACK_UPDATE: 'audio-track-update',\n AUDIO_TRACK_ADDED: 'audio-track-added',\n AUDIO_TRACK_REMOVED: 'audio-track-removed',\n AUTOPLAY_ERROR: 'autoplay-error',\n LEAVE: 'leave',\n};\n", "import { HMSVideoTrack } from './HMSVideoTrack';\nimport { VideoElementManager } from './VideoElementManager';\nimport { VideoTrackLayerUpdate } from '../../connection/channel-messages';\nimport {\n HMSPreferredSimulcastLayer,\n HMSSimulcastLayer,\n HMSSimulcastLayerDefinition,\n} from '../../interfaces/simulcast-layers';\nimport { MAINTAIN_TRACK_HISTORY } from '../../utils/constants';\nimport HMSLogger from '../../utils/logger';\nimport { isEmptyTrack } from '../../utils/track';\nimport { HMSRemoteStream } from '../streams';\n\nexport class HMSRemoteVideoTrack extends HMSVideoTrack {\n private _degraded = false;\n private _degradedAt: Date | null = null;\n private _layerDefinitions: HMSSimulcastLayerDefinition[] = [];\n private history = new TrackHistory();\n private preferredLayer: HMSPreferredSimulcastLayer = HMSSimulcastLayer.HIGH;\n private bizTrackId!: string;\n\n constructor(stream: HMSRemoteStream, track: MediaStreamTrack, source?: string) {\n super(stream, track, source);\n this.setVideoHandler(new VideoElementManager(this));\n }\n\n setTrackId(trackId: string) {\n this.bizTrackId = trackId;\n }\n\n get trackId(): string {\n return this.bizTrackId || super.trackId;\n }\n\n public get degraded() {\n return this._degraded;\n }\n\n public get degradedAt() {\n return this._degradedAt;\n }\n\n async setEnabled(value: boolean): Promise<void> {\n if (value === this.enabled) {\n return;\n }\n\n super.setEnabled(value);\n this.videoHandler.updateSinks(true);\n }\n\n async setPreferredLayer(layer: HMSPreferredSimulcastLayer) {\n //@ts-ignore\n if (layer === HMSSimulcastLayer.NONE) {\n HMSLogger.w(`layer ${HMSSimulcastLayer.NONE} will be ignored`);\n return;\n }\n this.preferredLayer = layer;\n if (!this.shouldSendVideoLayer(layer, 'preferLayer')) {\n return;\n }\n if (!this.hasSinks()) {\n HMSLogger.d(\n `[Remote Track] ${this.logIdentifier}\n streamId=${this.stream.id} \n trackId=${this.trackId}\n saving ${layer}, source=${this.source}\n Track does not have any sink`,\n );\n return;\n }\n await this.requestLayer(layer, 'preferLayer');\n this.pushInHistory(`uiPreferLayer-${layer}`);\n }\n\n /**\n * @deprecated\n * @returns {HMSSimulcastLayer}\n */\n getSimulcastLayer() {\n return (this.stream as HMSRemoteStream).getSimulcastLayer();\n }\n\n getLayer() {\n return (this.stream as HMSRemoteStream).getVideoLayer();\n }\n\n getPreferredLayer() {\n return this.preferredLayer;\n }\n\n replaceTrack(track: HMSRemoteVideoTrack) {\n this.nativeTrack = track.nativeTrack;\n if (track.transceiver) {\n this.transceiver = track.transceiver;\n // replace dummy streamId with actual streamId retaining all other properties\n this.stream.updateId(track.stream.id);\n }\n this.videoHandler.updateSinks();\n }\n\n async addSink(videoElement: HTMLVideoElement, shouldSendVideoLayer = true) {\n // if the native track is empty track, just request the preferred layer else attach it\n if (isEmptyTrack(this.nativeTrack)) {\n await this.requestLayer(this.preferredLayer, 'addSink');\n } else {\n super.addSink(videoElement);\n if (shouldSendVideoLayer) {\n await this.updateLayer('addSink');\n }\n }\n this.pushInHistory(`uiSetLayer-high`);\n }\n\n async removeSink(videoElement: HTMLVideoElement, shouldSendVideoLayer = true) {\n super.removeSink(videoElement);\n if (shouldSendVideoLayer) {\n await this.updateLayer('removeSink');\n }\n this._degraded = false;\n this.pushInHistory('uiSetLayer-none');\n }\n\n /**\n * Method to get available simulcast definitions for the track\n * @returns {HMSSimulcastLayerDefinition[]}\n */\n getSimulcastDefinitions() {\n // send a clone to store as it will freeze the object from further updates\n return [...this._layerDefinitions];\n }\n\n /** @internal */\n setSimulcastDefinitons(definitions: HMSSimulcastLayerDefinition[]) {\n this._layerDefinitions = definitions;\n }\n\n /**\n * @internal\n * SFU will change track's layer(degrade or restore) and tell the sdk to update\n * it locally.\n * @returns {boolean} isDegraded - returns true if degraded\n * */\n setLayerFromServer(layerUpdate: VideoTrackLayerUpdate) {\n this._degraded =\n this.enabled &&\n (layerUpdate.publisher_degraded || layerUpdate.subscriber_degraded) &&\n layerUpdate.current_layer === HMSSimulcastLayer.NONE;\n this._degradedAt = this._degraded ? new Date() : this._degradedAt;\n const currentLayer = layerUpdate.current_layer;\n HMSLogger.d(\n `[Remote Track] ${this.logIdentifier} \n streamId=${this.stream.id} \n trackId=${this.trackId}\n layer update from sfu\n currLayer=${layerUpdate.current_layer}\n preferredLayer=${layerUpdate.expected_layer}\n sub_degraded=${layerUpdate.subscriber_degraded}\n pub_degraded=${layerUpdate.publisher_degraded}\n isDegraded=${this._degraded}`,\n );\n // No need to send preferLayer update, as server has done it already\n (this.stream as HMSRemoteStream).setVideoLayerLocally(currentLayer, this.logIdentifier, 'setLayerFromServer');\n this.pushInHistory(`sfuLayerUpdate-${currentLayer}`);\n return this._degraded;\n }\n\n private async updateLayer(source: string) {\n const newLayer = this.degraded || !this.enabled || !this.hasSinks() ? HMSSimulcastLayer.NONE : this.preferredLayer;\n if (!this.shouldSendVideoLayer(newLayer, source)) {\n return;\n }\n await this.requestLayer(newLayer, source);\n }\n\n private pushInHistory(action: string) {\n if (MAINTAIN_TRACK_HISTORY) {\n this.history.push({ name: action, layer: this.getLayer(), degraded: this.degraded });\n }\n }\n\n private async requestLayer(layer: HMSSimulcastLayer, source: string) {\n try {\n const response = await (this.stream as HMSRemoteStream).setVideoLayer(\n layer,\n this.trackId,\n this.logIdentifier,\n source,\n );\n HMSLogger.d(\n `[Remote Track] ${this.logIdentifier} \n streamId=${this.stream.id}\n trackId=${this.trackId}\n Requested layer ${layer}, source=${source}`,\n );\n return response;\n } catch (error) {\n HMSLogger.d(\n `[Remote Track] ${this.logIdentifier} \n streamId=${this.stream.id}\n trackId=${this.trackId}\n Failed to set layer ${layer}, source=${source}\n error=${(error as Error).message}`,\n );\n throw error;\n }\n }\n\n /**\n * given the new layer, figure out if the update should be sent to server or not.\n * It won't be sent if the track is already on the targetLayer. If the track is\n * degraded though and the target layer is none, update will be sent.\n * If there are tracks degraded on a page and user paginates away to other page,\n * it's necessary to send the layer none message to SFU so it knows that the app\n * is no longer interested in the track and doesn't recover degraded tracks on non\n * visible pages.\n *\n * TODO: if track is degraded, send the update if target layer is lower than current layer\n * @private\n */\n private shouldSendVideoLayer(targetLayer: HMSSimulcastLayer, source: string) {\n const currLayer = this.getLayer();\n if (this.degraded && targetLayer === HMSSimulcastLayer.NONE) {\n return true;\n }\n if (currLayer === targetLayer) {\n HMSLogger.d(\n `[Remote Track] ${this.logIdentifier}`,\n `Not sending update, already on layer ${targetLayer}, source=${source}`,\n );\n return false;\n }\n return true;\n }\n}\n\n/**\n * to store history of everything that happened to a remote track which decides\n * it's current layer and degraded status.\n */\nclass TrackHistory {\n history: Record<string, any>[] = [];\n\n push(action: Record<string, any>) {\n action.time = new Date().toISOString().split('T')[1];\n this.history.push(action);\n }\n}\n", "import { HMSMediaStream } from './HMSMediaStream';\nimport HMSPublishConnection from '../../connection/publish/publishConnection';\nimport { SimulcastLayer } from '../../interfaces';\nimport { stringifyMediaStreamTrack } from '../../utils/json';\nimport HMSLogger from '../../utils/logger';\nimport { isNode } from '../../utils/support';\nimport { HMSLocalTrack, HMSLocalVideoTrack } from '../tracks';\n\nexport class HMSLocalStream extends HMSMediaStream {\n /** Connection set when publish is called for the first track */\n private readonly TAG = '[HMSLocalStream]';\n private connection: HMSPublishConnection | null = null;\n\n setConnection(connection: HMSPublishConnection) {\n this.connection = connection;\n }\n\n addTransceiver(track: HMSLocalTrack, simulcastLayers: SimulcastLayer[]) {\n const transceiver = this.connection!.addTransceiver(track.getTrackBeingSent(), {\n streams: [this.nativeStream],\n direction: 'sendonly',\n sendEncodings: this.getTrackEncodings(track, simulcastLayers),\n });\n this.setPreferredCodec(transceiver, track.nativeTrack.kind);\n track.transceiver = transceiver;\n return transceiver;\n }\n\n async setMaxBitrateAndFramerate(track: HMSLocalTrack): Promise<void> {\n await this.connection?.setMaxBitrateAndFramerate(track);\n }\n\n // @ts-ignore\n setPreferredCodec(_transceiver: RTCRtpTransceiver, _kind: string) {\n // TODO: Some browsers don't support setCodecPreferences, resort to SDPMunging?\n }\n\n replaceStreamTrack(track: MediaStreamTrack, withTrack: MediaStreamTrack) {\n this.nativeStream.addTrack(withTrack);\n this.nativeStream.removeTrack(track);\n HMSLogger.d(\n this.TAG,\n 'Native stream tracks after replace',\n this.nativeStream.getAudioTracks().map(stringifyMediaStreamTrack),\n `prev Track - ${stringifyMediaStreamTrack(track)}`,\n `new Track - ${stringifyMediaStreamTrack(withTrack)}`,\n );\n }\n\n /**\n * On mute and unmute of video tracks as well as for changing cameras, we replace the track with new track,\n * so as to avoid a renegotiation with the backend and reflect changes faster.\n * In case of video plugins we need to replace the track sent to remote without stopping the original one. As\n * if the original is stopped, plugin would stop getting input frames to process. So only the track in the\n * sender needs to be replaced.\n */\n async replaceSenderTrack(track: MediaStreamTrack, withTrack: MediaStreamTrack) {\n if (!this.connection || this.connection.connectionState === 'closed') {\n HMSLogger.d(this.TAG, `publish connection is not initialised or closed`);\n return;\n }\n const sender = this.connection.getSenders().find(sender => sender.track && sender.track.id === track.id);\n\n if (sender === undefined) {\n HMSLogger.w(this.TAG, `No sender found for trackId=${track.id}`);\n return;\n }\n await sender.replaceTrack(withTrack);\n }\n\n removeSender(track: HMSLocalTrack) {\n if (!this.connection || this.connection.connectionState === 'closed') {\n HMSLogger.d(this.TAG, `publish connection is not initialised or closed`);\n return;\n }\n const sender = track.transceiver?.sender;\n if (!sender) {\n HMSLogger.w(this.TAG, `No sender found for trackId=${track.trackId}`);\n return;\n }\n this.connection?.removeTrack(sender);\n const toRemoveLocalTrackIdx = this.tracks.indexOf(track);\n if (toRemoveLocalTrackIdx !== -1) {\n this.tracks.splice(toRemoveLocalTrackIdx, 1);\n } else {\n HMSLogger.w(this.TAG, `Cannot find ${track.trackId} in locally stored tracks`);\n }\n }\n\n private getTrackEncodings(track: HMSLocalTrack, simulcastLayers: SimulcastLayer[]) {\n const trackEncodings: RTCRtpEncodingParameters[] = [];\n if (track instanceof HMSLocalVideoTrack) {\n if (simulcastLayers.length > 0) {\n HMSLogger.d(this.TAG, 'Simulcast enabled with layers', simulcastLayers);\n trackEncodings.push(...simulcastLayers);\n } else {\n const encodings: RTCRtpEncodingParameters = { active: this.nativeStream.active };\n if (track.settings.maxBitrate && !isNode) {\n encodings.maxBitrate = track.settings.maxBitrate;\n }\n trackEncodings.push(encodings);\n }\n }\n return trackEncodings;\n }\n}\n", "import { HMSMediaStream } from './HMSMediaStream';\nimport HMSSubscribeConnection from '../../connection/subscribe/subscribeConnection';\nimport { HMSSimulcastLayer } from '../../interfaces';\nimport HMSLogger from '../../utils/logger';\n\n/** @internal */\nexport class HMSRemoteStream extends HMSMediaStream {\n private readonly connection: HMSSubscribeConnection;\n private audio = true;\n private video = HMSSimulcastLayer.NONE;\n\n constructor(nativeStream: MediaStream, connection: HMSSubscribeConnection) {\n super(nativeStream);\n this.connection = connection;\n }\n\n async setAudio(enabled: boolean, trackId: string, identifier?: string) {\n if (this.audio === enabled) {\n return;\n }\n\n this.audio = enabled;\n HMSLogger.d(\n `[Remote stream] ${identifier || ''} \n streamId=${this.id}\n trackId=${trackId}\n subscribing audio - ${this.audio}`,\n );\n await this.connection.sendOverApiDataChannelWithResponse({\n params: {\n subscribed: this.audio,\n track_id: trackId,\n },\n method: 'prefer-audio-track-state',\n });\n }\n\n /**\n * Sets the video layer after receiving new state from SFU. This is used when server side subscribe\n * degradation is ON.\n * @param layer is simulcast layer to be set\n * @param identifier is stream identifier to be printed in logs\n */\n setVideoLayerLocally(layer: HMSSimulcastLayer, identifier: string, source: string) {\n this.video = layer;\n HMSLogger.d(`[Remote stream] ${identifier}\n streamId=${this.id}\n source: ${source}\n Setting layer field to=${layer}`);\n }\n\n /**\n * Sets the video layer and updates the track state to SFU via api datachannel. This is used when client\n * side subscribe degradation is ON or client unsubscribes the current track.\n * @param layer is simulcast layer to be set\n * @param identifier is stream identifier to be printed in logs\n */\n setVideoLayer(layer: HMSSimulcastLayer, trackId: string, identifier: string, source: string) {\n HMSLogger.d(\n `[Remote stream] ${identifier} \n streamId=${this.id}\n trackId=${trackId} \n source: ${source} request ${layer} layer`,\n );\n this.setVideoLayerLocally(layer, identifier, source);\n return this.connection.sendOverApiDataChannelWithResponse({\n params: {\n max_spatial_layer: this.video,\n track_id: trackId,\n },\n method: 'prefer-video-track-state',\n });\n }\n\n /**\n * @deprecated\n * @returns {HMSSimulcastLayer}\n */\n getSimulcastLayer() {\n return this.video;\n }\n\n getVideoLayer() {\n return this.video;\n }\n\n isAudioSubscribed() {\n return this.audio;\n }\n}\n", "import { getLocalStream } from './media';\nimport { getAudioTrack, getVideoTrack } from './track';\nimport { HMSAction } from '../error/HMSAction';\nimport { HMSException } from '../error/HMSException';\nimport { HMSAudioTrackSettingsBuilder } from '../media/settings/HMSAudioTrackSettings';\nimport { HMSVideoTrackSettingsBuilder } from '../media/settings/HMSVideoTrackSettings';\n\n// Errors out when there's any device error, returns false when there are no device errors.\nexport async function validateDeviceAV() {\n const videoTrackSettings = new HMSVideoTrackSettingsBuilder().build();\n const audioTrackSettings = new HMSAudioTrackSettingsBuilder().build();\n /**\n * Check audio failure.\n * If audio failure - check AV failure.\n * If AV failure - throw AV failure.\n * If AV passed - throw audio failure.\n *\n * If audio passed - check video failure.\n * If video failure - throw video failure.\n * If video passed - no error - return false.\n */\n try {\n const track = await getAudioTrack(audioTrackSettings);\n track.stop();\n } catch (audioError) {\n if (isHMSDeviceError(audioError)) {\n const stream = await getLocalStream({ audio: false, video: true });\n stream.getTracks().forEach(track => track.stop());\n throw audioError;\n }\n }\n\n const track = await getVideoTrack(videoTrackSettings);\n track.stop();\n return false;\n}\n\nfunction isHMSDeviceError(error: any) {\n return error instanceof HMSException && error.action === HMSAction.TRACK;\n}\n", "import AnalyticsEventFactory from '../analytics/AnalyticsEventFactory';\nimport { ErrorFactory } from '../error/ErrorFactory';\nimport { HMSAction } from '../error/HMSAction';\nimport { EventBus } from '../events/EventBus';\nimport {\n HMSPeerStats,\n HMSTrackStats,\n MissingInboundStats,\n PeerConnectionType,\n RTCRemoteInboundRtpStreamStats,\n} from '../interfaces/webrtc-stats';\nimport { HMSLocalTrack, HMSRemoteTrack } from '../media/tracks';\nimport HMSLogger from '../utils/logger';\nimport { isPresent } from '../utils/validations';\n\nexport const getLocalTrackStats = async (\n eventBus: EventBus,\n track: HMSLocalTrack,\n peerName?: string,\n prevTrackStats?: Record<string, HMSTrackStats>,\n): Promise<Record<string, HMSTrackStats> | undefined> => {\n let trackReport: RTCStatsReport | undefined;\n const trackStats: Record<string, HMSTrackStats> = {};\n if (!track.transceiver?.sender.track) {\n return;\n }\n try {\n trackReport = await track.transceiver.sender.getStats();\n const mimeTypes: { [key: string]: string } = {}; // codecId -> mimeType\n const outbound: Record<string, RTCOutboundRtpStreamStats> = {};\n const inbound: Record<string, RTCInboundRtpStreamStats & MissingInboundStats> = {};\n trackReport?.forEach(stat => {\n switch (stat.type) {\n case 'outbound-rtp':\n outbound[stat.id] = stat;\n break;\n case 'remote-inbound-rtp':\n inbound[stat.ssrc] = stat;\n break;\n case 'codec':\n mimeTypes[stat.id] = stat.mimeType;\n break;\n default:\n break;\n }\n });\n\n Object.keys({ ...outbound }).forEach(stat => {\n const codecId = outbound[stat]?.codecId;\n const mimeType = codecId ? mimeTypes[codecId] : undefined;\n let codec: string | undefined;\n if (mimeType) {\n codec = mimeType.substring(mimeType.indexOf('/') + 1);\n }\n const out = outbound[stat];\n const inStats = inbound[out.ssrc];\n trackStats[stat] = {\n ...out,\n bitrate: computeBitrate('bytesSent', out, prevTrackStats?.[stat]),\n packetsLost: inStats?.packetsLost,\n jitter: inStats?.jitter,\n roundTripTime: inStats?.roundTripTime,\n totalRoundTripTime: inStats?.totalRoundTripTime,\n peerName,\n peerID: track.peerId,\n enabled: track.enabled,\n codec,\n };\n });\n } catch (err: any) {\n eventBus.analytics.publish(\n AnalyticsEventFactory.rtcStatsFailed(\n ErrorFactory.WebrtcErrors.StatsFailed(\n HMSAction.TRACK,\n `Error getting local track stats ${track.trackId} - ${err.message}`,\n ),\n ),\n );\n HMSLogger.w('[HMSWebrtcStats]', 'Error in getting local track stats', track, err, (err as Error).name);\n }\n return trackStats;\n};\n\nexport const getTrackStats = async (\n eventBus: EventBus,\n track: HMSRemoteTrack,\n peerName?: string,\n prevTrackStats?: HMSTrackStats,\n): Promise<HMSTrackStats | undefined> => {\n let trackReport: RTCStatsReport | undefined;\n try {\n trackReport = await track.transceiver?.receiver.getStats();\n } catch (err: any) {\n eventBus.analytics.publish(\n AnalyticsEventFactory.rtcStatsFailed(\n ErrorFactory.WebrtcErrors.StatsFailed(\n HMSAction.TRACK,\n `Error getting remote track stats ${track.trackId} - ${err.message}`,\n ),\n ),\n );\n HMSLogger.w('[HMSWebrtcStats]', 'Error in getting remote track stats', track, err);\n }\n const trackStats = getRelevantStatsFromTrackReport(trackReport);\n\n const bitrate = computeBitrate('bytesReceived', trackStats, prevTrackStats);\n\n const packetsLostRate = computeStatRate('packetsLost', trackStats, prevTrackStats);\n\n if (trackStats?.remote) {\n Object.assign(trackStats.remote, {\n packetsLostRate: computeStatRate('packetsLost', trackStats.remote, prevTrackStats?.remote),\n });\n }\n\n return (\n trackStats &&\n Object.assign(trackStats, {\n bitrate,\n packetsLostRate,\n peerId: track.peerId,\n enabled: track.enabled,\n peerName,\n codec: trackStats.codec,\n })\n );\n};\n\nconst getRelevantStatsFromTrackReport = (trackReport?: RTCStatsReport) => {\n let streamStats: RTCInboundRtpStreamStats | RTCOutboundRtpStreamStats | undefined;\n // Valid by Webrtc spec, not in TS\n // let remoteStreamStats: RTCRemoteInboundRtpStreamStats | RTCRemoteOutboundRtpStreamStats;\n let remoteStreamStats: RTCRemoteInboundRtpStreamStats | undefined;\n\n const mimeTypes: { [key: string]: string } = {}; // codecId -> mimeType\n trackReport?.forEach(stat => {\n switch (stat.type) {\n case 'inbound-rtp':\n streamStats = stat;\n break;\n case 'outbound-rtp':\n streamStats = stat;\n break;\n case 'remote-inbound-rtp':\n remoteStreamStats = stat;\n break;\n case 'codec':\n mimeTypes[stat.id] = stat.mimeType;\n break;\n default:\n break;\n }\n });\n\n const mimeType = streamStats?.codecId ? mimeTypes[streamStats.codecId] : undefined;\n let codec: string | undefined;\n if (mimeType) {\n codec = mimeType.substring(mimeType.indexOf('/') + 1);\n }\n\n return (\n streamStats &&\n Object.assign(streamStats, {\n remote: remoteStreamStats,\n codec: codec,\n })\n );\n};\n\nexport const getLocalPeerStatsFromReport = (\n type: PeerConnectionType,\n report?: RTCStatsReport,\n prevStats?: HMSPeerStats,\n): (RTCIceCandidatePairStats & { bitrate: number }) | undefined => {\n const activeCandidatePair = getActiveCandidatePairFromReport(report);\n const bitrate = computeBitrate(\n (type === 'publish' ? 'bytesSent' : 'bytesReceived') as any,\n activeCandidatePair,\n prevStats && prevStats[type],\n );\n\n return activeCandidatePair && Object.assign(activeCandidatePair, { bitrate });\n};\n\nexport const getActiveCandidatePairFromReport = (report?: RTCStatsReport): RTCIceCandidatePairStats | undefined => {\n let activeCandidatePair: RTCIceCandidatePairStats | undefined;\n report?.forEach(stat => {\n if (stat.type === 'transport') {\n // TS doesn't have correct types for RTCStatsReports\n // @ts-expect-error\n activeCandidatePair = report?.get(stat.selectedCandidatePairId);\n }\n });\n\n // Fallback for Firefox.\n if (!activeCandidatePair) {\n report?.forEach(stat => {\n if (stat.type === 'candidate-pair' && stat.selected) {\n activeCandidatePair = stat;\n }\n });\n }\n\n return activeCandidatePair;\n};\n\nexport const getPacketsLostAndJitterFromReport = (report?: RTCStatsReport): { packetsLost: number; jitter: number } => {\n const result = { packetsLost: 0, jitter: 0 };\n report?.forEach(stat => {\n if (stat.packetsLost) {\n result.packetsLost += stat.packetsLost;\n }\n if (stat.jitter > result.jitter) {\n result.jitter = stat.jitter;\n }\n });\n\n return result;\n};\n\nexport const union = <T>(arr1: T[], arr2: T[]): T[] => {\n return Array.from(new Set(arr1.concat(arr2)));\n};\n\n/**\n * Ref: https://github.dev/peermetrics/webrtc-stats/blob/b5c1fed68325543e6f563c6d3f4450a4b51e12b7/src/utils.ts#L62\n */\nexport const computeBitrate = <T extends HMSTrackStats>(\n statName: keyof T,\n newReport?: Partial<T>,\n oldReport?: Partial<T>,\n): number => computeStatRate(statName, newReport, oldReport) * 8; // Bytes to bits\n\nconst computeStatRate = <T extends HMSTrackStats>(\n statName: keyof T,\n newReport?: Partial<T>,\n oldReport?: Partial<T>,\n): number => {\n const newVal = newReport && newReport[statName];\n const oldVal = oldReport ? oldReport[statName] : null;\n const conditions = [newReport, oldReport, isPresent(newVal), isPresent(oldVal)];\n if (conditions.every(condition => !!condition)) {\n // Type not null checked in `isPresent`\n // * 1000 - ms to s\n return (\n computeNumberRate(\n newVal as unknown as number,\n oldVal as unknown as number,\n newReport?.timestamp,\n oldReport?.timestamp,\n ) * 1000\n );\n } else {\n return 0;\n }\n};\n\nexport const computeNumberRate = (newVal?: number, oldVal?: number, newTimestamp?: number, oldTimestamp?: number) => {\n if (isPresent(newVal) && isPresent(oldVal) && newTimestamp && oldTimestamp) {\n return ((newVal as number) - (oldVal as number)) / (newTimestamp - oldTimestamp);\n } else {\n return 0;\n }\n};\n", "import {\n computeNumberRate,\n getLocalPeerStatsFromReport,\n getLocalTrackStats,\n getPacketsLostAndJitterFromReport,\n getTrackStats,\n union,\n} from './utils';\nimport AnalyticsEventFactory from '../analytics/AnalyticsEventFactory';\nimport { ErrorFactory } from '../error/ErrorFactory';\nimport { HMSAction } from '../error/HMSAction';\nimport { EventBus } from '../events/EventBus';\nimport { HMSPeerStats, HMSTrackStats, PeerConnectionType } from '../interfaces/webrtc-stats';\nimport { HMSLocalTrack, HMSRemoteAudioTrack, HMSRemoteTrack, HMSRemoteVideoTrack } from '../media/tracks';\nimport { IStore } from '../sdk/store';\nimport HMSLogger from '../utils/logger';\n\nexport class HMSWebrtcStats {\n private readonly TAG = '[HMSWebrtcStats]';\n private localPeerID?: string;\n private peerStats: Record<string, HMSPeerStats> = {};\n private remoteTrackStats: Record<string, HMSTrackStats> = {};\n private localTrackStats: Record<string, Record<string, HMSTrackStats>> = {};\n\n /**\n * Removed localPeerID check in other places as it will be present before\n * this is initialized\n */\n constructor(\n private getStats: Record<PeerConnectionType, RTCPeerConnection['getStats'] | undefined>,\n private store: IStore,\n private readonly eventBus: EventBus,\n ) {\n this.localPeerID = this.store.getLocalPeer()?.peerId;\n }\n\n getLocalPeerStats = (): HMSPeerStats | undefined => {\n return this.peerStats[this.localPeerID!];\n };\n\n getRemoteTrackStats = (trackId: string): HMSTrackStats | undefined => {\n return this.remoteTrackStats[trackId];\n };\n\n getLocalTrackStats = () => {\n return this.localTrackStats;\n };\n\n /**\n * @internal\n */\n updateStats = async () => {\n await this.updateLocalPeerStats();\n await this.updateLocalTrackStats();\n await this.updateRemoteTrackStats();\n };\n\n private updateLocalPeerStats = async () => {\n const prevLocalPeerStats = this.getLocalPeerStats();\n let publishReport: RTCStatsReport | undefined;\n try {\n publishReport = await this.getStats.publish?.();\n } catch (err: any) {\n this.eventBus.analytics.publish(\n AnalyticsEventFactory.rtcStatsFailed(ErrorFactory.WebrtcErrors.StatsFailed(HMSAction.PUBLISH, err.message)),\n );\n HMSLogger.w(this.TAG, 'Error in getting publish stats', err);\n }\n const publishStats: HMSPeerStats['publish'] | undefined =\n publishReport && getLocalPeerStatsFromReport('publish', publishReport, prevLocalPeerStats);\n\n let subscribeReport: RTCStatsReport | undefined;\n try {\n subscribeReport = await this.getStats.subscribe?.();\n } catch (err: any) {\n this.eventBus.analytics.publish(\n AnalyticsEventFactory.rtcStatsFailed(ErrorFactory.WebrtcErrors.StatsFailed(HMSAction.SUBSCRIBE, err.message)),\n );\n HMSLogger.w(this.TAG, 'Error in getting subscribe stats', err);\n }\n const baseSubscribeStats =\n subscribeReport && getLocalPeerStatsFromReport('subscribe', subscribeReport, prevLocalPeerStats);\n const { packetsLost, jitter } = getPacketsLostAndJitterFromReport(subscribeReport);\n const packetsLostRate = computeNumberRate(\n packetsLost,\n prevLocalPeerStats?.subscribe?.packetsLost,\n baseSubscribeStats?.timestamp,\n prevLocalPeerStats?.subscribe?.timestamp,\n );\n\n const subscribeStats: HMSPeerStats['subscribe'] =\n baseSubscribeStats && Object.assign(baseSubscribeStats, { packetsLostRate, jitter, packetsLost });\n\n this.peerStats[this.localPeerID!] = { publish: publishStats, subscribe: subscribeStats };\n };\n\n private updateRemoteTrackStats = async () => {\n const tracks = Array.from(this.store.getTracksMap().values()).filter(\n track => track instanceof HMSRemoteVideoTrack || track instanceof HMSRemoteAudioTrack,\n );\n const trackIds = tracks.map(track => track.trackId);\n Object.keys(this.remoteTrackStats).forEach(trackId => {\n if (!trackIds.includes(trackId)) {\n delete this.remoteTrackStats[trackId];\n }\n });\n for (const track of tracks) {\n const peerName = track.peerId && this.store.getPeerById(track.peerId)?.name;\n const prevTrackStats = this.getRemoteTrackStats(track.trackId);\n const trackStats = await getTrackStats(this.eventBus, track as HMSRemoteTrack, peerName, prevTrackStats);\n if (trackStats) {\n this.remoteTrackStats[track.trackId] = trackStats;\n }\n }\n };\n\n private updateLocalTrackStats = async () => {\n const tracks = this.store.getLocalPeerTracks().reduce<Record<string, HMSLocalTrack>>((res, track) => {\n res[track.getTrackIDBeingSent()] = track;\n return res;\n }, {});\n const trackIDs = union(Object.keys(this.localTrackStats), Object.keys(tracks));\n for (const trackID of trackIDs) {\n const track = tracks[trackID] as HMSLocalTrack;\n if (track) {\n const peerName = this.store.getLocalPeer()?.name;\n const trackStats = await getLocalTrackStats(this.eventBus, track, peerName, this.localTrackStats[trackID]);\n if (trackStats) {\n this.localTrackStats[trackID] = trackStats;\n }\n } else {\n delete this.localTrackStats[trackID];\n }\n }\n };\n}\n", "import { HMSWebrtcStats } from './HMSWebrtcStats';\nimport AnalyticsEventFactory from '../analytics/AnalyticsEventFactory';\nimport { ErrorFactory } from '../error/ErrorFactory';\nimport { HMSAction } from '../error/HMSAction';\nimport { EventBus } from '../events/EventBus';\nimport { IStore } from '../sdk/store';\nimport { RTC_STATS_MONITOR_INTERVAL } from '../utils/constants';\nimport HMSLogger from '../utils/logger';\nimport { sleep } from '../utils/timer-utils';\n\nexport class HMSWebrtcInternals {\n private readonly TAG = '[HMSWebrtcInternals]';\n private readonly interval = RTC_STATS_MONITOR_INTERVAL;\n private isMonitored = false;\n private hmsStats?: HMSWebrtcStats;\n\n constructor(\n private readonly store: IStore,\n private readonly eventBus: EventBus,\n private publishConnection?: RTCPeerConnection,\n private subscribeConnection?: RTCPeerConnection,\n ) {}\n\n getPublishPeerConnection() {\n return this.publishConnection;\n }\n\n getSubscribePeerConnection() {\n return this.subscribeConnection;\n }\n\n getCurrentStats() {\n return this.hmsStats;\n }\n\n onStatsChange(statsChangeCb: (stats: HMSWebrtcStats) => void) {\n this.eventBus.statsUpdate.subscribe(statsChangeCb);\n return () => {\n this.eventBus.statsUpdate.unsubscribe(statsChangeCb);\n };\n }\n\n private handleStatsUpdate = async () => {\n await this.hmsStats?.updateStats();\n this.eventBus.statsUpdate.publish(this.hmsStats);\n };\n\n /**\n *\n * @internal\n */\n setPeerConnections({ publish, subscribe }: { publish?: RTCPeerConnection; subscribe?: RTCPeerConnection }) {\n this.publishConnection = publish;\n this.subscribeConnection = subscribe;\n\n this.hmsStats = new HMSWebrtcStats(\n {\n publish: this.publishConnection?.getStats.bind(this.publishConnection),\n subscribe: this.subscribeConnection?.getStats.bind(this.subscribeConnection),\n },\n this.store,\n this.eventBus,\n );\n }\n\n /**\n * @internal\n */\n async start() {\n if (this.isMonitored) {\n HMSLogger.d(this.TAG, 'Already started');\n return;\n }\n this.stop();\n this.isMonitored = true;\n HMSLogger.d(this.TAG, 'Starting Webrtc Stats Monitor');\n this.startLoop()\n .then(() => HMSLogger.d(this.TAG, 'Stopping Webrtc Stats Monitor'))\n .catch(e => {\n this.eventBus.analytics.publish(\n AnalyticsEventFactory.rtcStatsFailed(ErrorFactory.WebrtcErrors.StatsFailed(HMSAction.PUBLISH, e.message)),\n );\n HMSLogger.e(this.TAG, e.message);\n });\n }\n\n private stop() {\n this.isMonitored = false;\n }\n\n private async startLoop() {\n while (this.isMonitored) {\n await this.handleStatsUpdate();\n await sleep(this.interval);\n }\n }\n\n /**\n * @internal\n */\n cleanup() {\n this.stop();\n this.eventBus.statsUpdate.removeAllListeners();\n }\n}\n", "import AnalyticsEventFactory from '../analytics/AnalyticsEventFactory';\nimport { EventBus } from '../events/EventBus';\nimport { HMSUpdateListener } from '../interfaces';\nimport { NetworkHealth, ScoreMap } from '../signal/init/models';\nimport HMSLogger from '../utils/logger';\nimport { sleep } from '../utils/timer-utils';\n\nexport class NetworkTestManager {\n private readonly TAG = '[NetworkTestManager]';\n private controller = new AbortController();\n private score?: number;\n constructor(private eventBus: EventBus, private listener?: HMSUpdateListener) {}\n\n start = async (networkHealth: NetworkHealth) => {\n if (!networkHealth) {\n return;\n }\n const { url, timeout, scoreMap } = networkHealth;\n const signal = this.controller.signal;\n\n const startTime = Date.now();\n let downloadedSize = 0;\n const timeoutPromise = sleep(timeout).then(() => {\n this.controller.abort();\n });\n try {\n const res = await fetch(`${url}?${Date.now()}`, { signal });\n const reader = res.body?.getReader();\n if (!reader) {\n throw Error('unable to process request');\n }\n const readData = async () => {\n if (!reader) {\n return;\n }\n try {\n let completed = false;\n while (!completed) {\n const { value, done } = await reader.read();\n completed = done;\n if (value) {\n downloadedSize += value.byteLength;\n this.sendScore({ scoreMap, downloadedSize, startTime });\n }\n }\n } catch (error) {\n if ((error as Error).name !== 'AbortError') {\n HMSLogger.d(this.TAG, error);\n }\n }\n };\n\n return Promise.race([readData(), timeoutPromise])\n .then(() => {\n this.sendScore({ scoreMap, downloadedSize, startTime, finished: true });\n })\n .catch(error => {\n HMSLogger.d(this.TAG, error);\n this.updateScoreToListener(0);\n this.eventBus.analytics.publish(\n AnalyticsEventFactory.previewNetworkQuality({ error: (error as Error).message }),\n );\n });\n } catch (error) {\n if ((error as Error).name !== 'AbortError') {\n HMSLogger.d(this.TAG, error);\n this.updateScoreToListener(0);\n this.eventBus.analytics.publish(\n AnalyticsEventFactory.previewNetworkQuality({ error: (error as Error).message }),\n );\n } else {\n HMSLogger.d(this.TAG, error);\n }\n }\n };\n\n stop = () => {\n if (!this.controller.signal.aborted) {\n this.controller.abort();\n }\n };\n\n private sendScore = ({\n scoreMap,\n downloadedSize,\n startTime,\n finished = false,\n }: {\n scoreMap: ScoreMap;\n downloadedSize: number;\n startTime: number;\n finished?: boolean;\n }) => {\n const totalTimeInSecs = (Date.now() - startTime) / 1000;\n const sizeInKB = downloadedSize / 1024;\n const bitrate = (sizeInKB / totalTimeInSecs) * 8;\n let calculatedScore = -1;\n for (const score in scoreMap) {\n const thresholds = scoreMap[score];\n if (bitrate >= thresholds.low && (!thresholds.high || bitrate <= thresholds.high)) {\n calculatedScore = Number(score);\n }\n }\n this.updateScoreToListener(calculatedScore);\n if (finished) {\n this.eventBus.analytics.publish(\n AnalyticsEventFactory.previewNetworkQuality({ score: calculatedScore, downLink: bitrate.toFixed(2) }),\n );\n }\n };\n\n private updateScoreToListener(newQualityScore: number) {\n if (newQualityScore === this.score) {\n return;\n }\n this.score = newQualityScore;\n this.listener?.onNetworkQuality?.(newQualityScore);\n }\n}\n", "import { IStore } from './store';\nimport { DeviceManager } from '../device-manager';\nimport { HMSRole } from '../interfaces';\nimport InitialSettings from '../interfaces/settings';\nimport { SimulcastLayers } from '../interfaces/simulcast-layers';\nimport { HMSPeerUpdate, HMSTrackUpdate, HMSUpdateListener } from '../interfaces/update-listener';\nimport { HMSLocalTrack } from '../media/tracks';\nimport ITransport from '../transport/ITransport';\n\nexport default class RoleChangeManager {\n constructor(\n private store: IStore,\n private transport: ITransport,\n private deviceManager: DeviceManager,\n private publish: (settings: InitialSettings) => Promise<void>,\n private removeAuxiliaryTrack: (trackId: string) => void,\n private listener?: HMSUpdateListener,\n ) {}\n\n handleLocalPeerRoleUpdate = async ({ oldRole, newRole }: { oldRole: HMSRole; newRole: HMSRole }) => {\n const localPeer = this.store.getLocalPeer();\n\n if (!localPeer) {\n return;\n }\n\n await this.diffRolesAndPublishTracks({ oldRole, newRole });\n this.listener?.onPeerUpdate(HMSPeerUpdate.ROLE_UPDATED, localPeer);\n };\n\n diffRolesAndPublishTracks = async ({ oldRole, newRole }: { oldRole: HMSRole; newRole: HMSRole }) => {\n const wasPublishing = new Set(oldRole.publishParams.allowed);\n const isPublishing = new Set(newRole.publishParams.allowed);\n\n const removeVideo = this.removeTrack(wasPublishing, isPublishing, 'video');\n\n const removeAudio = this.removeTrack(wasPublishing, isPublishing, 'audio');\n const removeScreen = this.removeTrack(wasPublishing, isPublishing, 'screen');\n\n const videoHasSimulcastDifference = this.hasSimulcastDifference(\n oldRole.publishParams.simulcast?.video,\n newRole.publishParams.simulcast?.video,\n );\n const screenHasSimulcastDifference = this.hasSimulcastDifference(\n oldRole.publishParams.simulcast?.screen,\n newRole.publishParams.simulcast?.screen,\n );\n\n const prevVideoEnabled = this.store.getLocalPeer()?.videoTrack?.enabled;\n\n await this.removeAudioTrack(removeAudio);\n await this.removeVideoTracks(removeVideo || videoHasSimulcastDifference);\n await this.removeScreenTracks(removeScreen || screenHasSimulcastDifference);\n\n const initialSettings = this.store.getConfig()?.settings || {\n isAudioMuted: true,\n isVideoMuted: true,\n audioInputDeviceId: 'default',\n videoDeviceId: 'default',\n audioOutputDeviceId: 'default',\n };\n\n if (videoHasSimulcastDifference) {\n initialSettings.isVideoMuted = !prevVideoEnabled;\n }\n\n // call publish with new settings, local track manager will diff policies\n await this.publish(initialSettings);\n await this.syncDevices(initialSettings, newRole);\n };\n\n private async syncDevices(initialSettings: InitialSettings, newRole: HMSRole) {\n if ((!initialSettings.isAudioMuted || !initialSettings.isVideoMuted) && newRole.publishParams.allowed.length > 0) {\n await this.deviceManager.init(true);\n }\n }\n private async removeVideoTracks(removeVideo: boolean) {\n if (!removeVideo) {\n return;\n }\n const localPeer = this.store.getLocalPeer();\n // TODO check auxillary tracks for regular audio and video too\n if (localPeer?.videoTrack) {\n // TODO: stop processed track and cleanup plugins loop non async\n // vb can throw change role off otherwise\n if (localPeer.videoTrack.isPublished) {\n await this.transport.unpublish([localPeer.videoTrack]);\n } else {\n await localPeer.videoTrack.cleanup();\n }\n this.listener?.onTrackUpdate(HMSTrackUpdate.TRACK_REMOVED, localPeer.videoTrack, localPeer);\n localPeer.videoTrack = undefined;\n }\n await this.removeAuxTracks(track => track.source !== 'screen' && track.type === 'video');\n }\n\n private async removeAudioTrack(removeAudio: boolean) {\n if (!removeAudio) {\n return;\n }\n const localPeer = this.store.getLocalPeer();\n if (localPeer?.audioTrack) {\n if (localPeer.audioTrack.isPublished) {\n await this.transport.unpublish([localPeer.audioTrack]);\n } else {\n await localPeer.audioTrack.cleanup();\n }\n this.listener?.onTrackUpdate(HMSTrackUpdate.TRACK_REMOVED, localPeer.audioTrack, localPeer);\n localPeer.audioTrack = undefined;\n }\n await this.removeAuxTracks(track => track.source !== 'screen' && track.type === 'audio');\n }\n\n private async removeScreenTracks(removeScreen: boolean) {\n if (!removeScreen) {\n return;\n }\n await this.removeAuxTracks(track => track.source === 'screen');\n }\n\n private async removeAuxTracks(predicate: (track: HMSLocalTrack) => boolean) {\n const localPeer = this.store.getLocalPeer();\n if (localPeer?.auxiliaryTracks) {\n const localAuxTracks = [...localPeer.auxiliaryTracks];\n for (const track of localAuxTracks) {\n if (predicate(track)) {\n await this.removeAuxiliaryTrack(track.trackId);\n }\n }\n }\n }\n\n private removeTrack(wasPublishing: Set<string>, isPublishing: Set<string>, type: string) {\n return wasPublishing.has(type) && !isPublishing.has(type);\n }\n\n private hasSimulcastDifference(oldLayers?: SimulcastLayers, newLayers?: SimulcastLayers) {\n if (!oldLayers && !newLayers) {\n return false;\n }\n if (oldLayers?.layers?.length !== newLayers?.layers?.length) {\n return true;\n }\n\n // return true if anyone layer has different maxBitrate/maxFramerate\n return !!oldLayers?.layers?.some(layer => {\n const newLayer = newLayers?.layers?.find(newLayer => newLayer.rid === layer.rid);\n return newLayer?.maxBitrate !== layer.maxBitrate || newLayer?.maxFramerate !== layer.maxFramerate;\n });\n }\n}\n", "import AnalyticsEvent from './AnalyticsEvent';\nimport { IAnalyticsTransportProvider } from './IAnalyticsTransportProvider';\nimport {\n CLIENT_ANAYLTICS_PROD_ENDPOINT,\n CLIENT_ANAYLTICS_QA_ENDPOINT,\n CLIENT_ANAYLTICS_STORAGE_LIMIT,\n} from '../utils/constants';\nimport { LocalStorage } from '../utils/local-storage';\nimport HMSLogger from '../utils/logger';\nimport { ENV } from '../utils/support';\n\ninterface ClientEventBody {\n event: string;\n event_id: string;\n peer: {\n peer_id?: string;\n role?: string;\n joined_at?: number;\n left_at?: number;\n room_id?: string;\n room_name?: string;\n session_started_at?: number;\n user_data?: string;\n user_name?: string;\n template_id?: string;\n session_id?: string;\n };\n timestamp: number;\n cluster: {\n websocket_url: string;\n };\n payload: Record<string, any>;\n device_id: string;\n}\n\nclass ClientAnalyticsTransport implements IAnalyticsTransportProvider {\n readonly TAG = '[HTTPAnalyticsTransport]';\n private failedEvents = new LocalStorage<AnalyticsEvent[]>('client-events');\n isConnected = true;\n private env: null | ENV = null;\n private websocketURL = '';\n\n setEnv(env: ENV) {\n this.env = env;\n this.flushFailedEvents();\n }\n\n setWebsocketEndpoint(ws: string) {\n this.websocketURL = ws;\n }\n\n sendEvent(event: AnalyticsEvent) {\n if (!this.env || !this.websocketURL) {\n this.addEventToStorage(event);\n return;\n }\n const requestBody: ClientEventBody = {\n event: event.name,\n payload: event.properties,\n event_id: String(event.timestamp),\n peer: event.metadata.peer,\n timestamp: event.timestamp,\n device_id: event.device_id,\n cluster: {\n websocket_url: this.websocketURL,\n },\n };\n const url = this.env === ENV.PROD ? CLIENT_ANAYLTICS_PROD_ENDPOINT : CLIENT_ANAYLTICS_QA_ENDPOINT;\n fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${event.metadata.token}`,\n user_agent_v2: event.metadata.userAgent,\n },\n body: JSON.stringify(requestBody),\n })\n .then(response => {\n // Ignore invalid token or expired token messages\n if (response.status === 401) {\n this.removeFromStorage(event);\n return;\n }\n if (response.status !== 200) {\n throw Error(response.statusText);\n }\n this.removeFromStorage(event);\n })\n .catch(error => {\n HMSLogger.v(this.TAG, 'Failed to send event', error, event);\n this.addEventToStorage(event);\n });\n }\n flushFailedEvents() {\n const events = this.failedEvents.get();\n events?.forEach(event => this.sendEvent(event));\n }\n\n private addEventToStorage(event: AnalyticsEvent): void {\n const existingEvents = this.failedEvents.get() || [];\n if (!existingEvents.find(existingEvent => existingEvent.timestamp === event.timestamp)) {\n if (existingEvents.length === CLIENT_ANAYLTICS_STORAGE_LIMIT) {\n existingEvents.shift();\n }\n existingEvents.push(event);\n this.failedEvents.set(existingEvents);\n }\n }\n\n private removeFromStorage(event: AnalyticsEvent): void {\n const events = this.failedEvents.get() || [];\n const index = events.findIndex(storageEvent => storageEvent.timestamp === event.timestamp);\n if (index > -1) {\n events.splice(index, 1);\n this.failedEvents.set(events);\n }\n }\n}\n\nexport const HTTPAnalyticsTransport = new ClientAnalyticsTransport();\n", "import { IStore, KnownRoles, TrackStateEntry } from './IStore';\nimport { HTTPAnalyticsTransport } from '../../analytics/HTTPAnalyticsTransport';\nimport { DeviceStorageManager } from '../../device-manager/DeviceStorage';\nimport { ErrorFactory } from '../../error/ErrorFactory';\nimport { HMSAction } from '../../error/HMSAction';\nimport { HMSConfig, HMSFrameworkInfo, HMSPoll, HMSSpeaker } from '../../interfaces';\nimport { SelectedDevices } from '../../interfaces/devices';\nimport { IErrorListener } from '../../interfaces/error-listener';\nimport {\n HMSSimulcastLayerDefinition,\n RID,\n SimulcastLayer,\n SimulcastLayers,\n simulcastMapping,\n} from '../../interfaces/simulcast-layers';\nimport {\n HMSAudioTrack,\n HMSLocalTrack,\n HMSRemoteAudioTrack,\n HMSRemoteVideoTrack,\n HMSTrack,\n HMSTrackSource,\n HMSTrackType,\n HMSVideoTrack,\n} from '../../media/tracks';\nimport { PolicyParams } from '../../notification-manager';\nimport { ENV } from '../../utils/support';\nimport { createUserAgent } from '../../utils/user-agent';\nimport HMSRoom from '../models/HMSRoom';\nimport { HMSLocalPeer, HMSPeer, HMSRemotePeer } from '../models/peer';\n\nclass Store implements IStore {\n private room?: HMSRoom;\n private knownRoles: KnownRoles = {};\n private localPeerId?: string;\n private peers: Record<string, HMSPeer> = {};\n private tracks = new Map<HMSTrack, HMSTrack>();\n private templateAppData?: Record<string, string>;\n // Not used currently. Will be used exclusively for preview tracks.\n // private previewTracks: Record<string, HMSTrack> = {};\n private peerTrackStates: Record<string, TrackStateEntry> = {};\n private speakers: HMSSpeaker[] = [];\n private videoLayers?: SimulcastLayers;\n // private screenshareLayers?: SimulcastLayers;\n private config?: HMSConfig;\n private errorListener?: IErrorListener;\n private roleDetailsArrived = false;\n private env: ENV = ENV.PROD;\n private simulcastEnabled = false;\n private userAgent: string = createUserAgent(this.env);\n private polls = new Map<string, HMSPoll>();\n\n getConfig() {\n return this.config;\n }\n\n setSimulcastEnabled(enabled: boolean) {\n this.simulcastEnabled = enabled;\n }\n\n getEnv() {\n return this.env;\n }\n\n getPublishParams() {\n const peer = this.getLocalPeer();\n const role = peer?.asRole || peer?.role;\n return role?.publishParams;\n }\n\n getRoom() {\n return this.room;\n }\n\n getPolicyForRole(role: string) {\n return this.knownRoles[role];\n }\n\n getKnownRoles() {\n return this.knownRoles;\n }\n\n getTemplateAppData() {\n return this.templateAppData;\n }\n\n getLocalPeer() {\n if (this.localPeerId && this.peers[this.localPeerId]) {\n return this.peers[this.localPeerId] as HMSLocalPeer;\n }\n return undefined;\n }\n\n getRemotePeers() {\n return Object.values(this.peers).filter(peer => !peer.isLocal) as HMSRemotePeer[];\n }\n\n getPeers(): HMSPeer[] {\n return Object.values(this.peers);\n }\n\n getPeerMap() {\n return this.peers;\n }\n\n getPeerById(peerId: string) {\n if (this.peers[peerId]) {\n return this.peers[peerId];\n }\n return undefined;\n }\n\n getTracksMap() {\n return this.tracks;\n }\n\n getTracks() {\n return Array.from(this.tracks.values());\n }\n\n getVideoTracks() {\n return this.getTracks().filter(track => track.type === HMSTrackType.VIDEO) as HMSVideoTrack[];\n }\n\n getRemoteVideoTracks() {\n return this.getTracks().filter(track => track instanceof HMSRemoteVideoTrack) as HMSRemoteVideoTrack[];\n }\n\n getAudioTracks() {\n return this.getTracks().filter(track => track.type === HMSTrackType.AUDIO) as HMSAudioTrack[];\n }\n\n getPeerTracks(peerId?: string) {\n const peer = peerId ? this.peers[peerId] : undefined;\n const tracks: HMSTrack[] = [];\n peer?.videoTrack && tracks.push(peer.videoTrack);\n peer?.audioTrack && tracks.push(peer.audioTrack);\n return tracks.concat(peer?.auxiliaryTracks || []);\n }\n\n getLocalPeerTracks() {\n return this.getPeerTracks(this.localPeerId) as HMSLocalTrack[];\n }\n\n hasTrack(track: HMSTrack) {\n return this.tracks.has(track);\n }\n\n getTrackById(trackId: string) {\n const track = Array.from(this.tracks.values()).find(track => track.trackId === trackId);\n if (track) {\n return track;\n }\n const localPeer = this.getLocalPeer();\n /**\n * handle case of audio level coming from server for local peer's track where local peer\n * didn't initially gave audio permission. So track.firstTrackId is that of dummy track and\n * this.tracks[trackId] doesn't exist.\n * Example repro which this solves -\n * - call preview with audio muted, unmute audio in preview then join the room, now initial\n * track id is that from dummy track but the track id which server knows will be different\n */\n if (localPeer) {\n if (localPeer.audioTrack?.isPublishedTrackId(trackId)) {\n return localPeer.audioTrack;\n } else if (localPeer.videoTrack?.isPublishedTrackId(trackId)) {\n return localPeer.videoTrack;\n }\n }\n return undefined;\n }\n\n getPeerByTrackId(trackId: string) {\n const track = Array.from(this.tracks.values()).find(track => track.trackId === trackId);\n return track?.peerId ? this.peers[track.peerId] : undefined;\n }\n\n getSpeakers() {\n return this.speakers;\n }\n\n getSpeakerPeers() {\n return this.speakers.map(speaker => speaker.peer);\n }\n\n getUserAgent() {\n return this.userAgent;\n }\n\n createAndSetUserAgent(frameworkInfo?: HMSFrameworkInfo) {\n this.userAgent = createUserAgent(this.env, frameworkInfo);\n }\n\n setRoom(room: HMSRoom) {\n this.room = room;\n }\n\n setKnownRoles(params: PolicyParams) {\n this.knownRoles = params.known_roles;\n this.roleDetailsArrived = true;\n this.templateAppData = params.app_data;\n if (!this.simulcastEnabled) {\n return;\n }\n const publishParams = this.knownRoles[params.name]?.publishParams;\n this.videoLayers = this.convertSimulcastLayers(publishParams.simulcast?.video);\n // this.screenshareLayers = this.convertSimulcastLayers(publishParams.simulcast?.screen);\n this.updatePeersPolicy();\n }\n\n hasRoleDetailsArrived(): boolean {\n return this.roleDetailsArrived;\n }\n\n // eslint-disable-next-line complexity\n setConfig(config: HMSConfig) {\n DeviceStorageManager.rememberDevices(Boolean(config.rememberDeviceSelection));\n if (config.rememberDeviceSelection) {\n const devices: SelectedDevices | undefined = DeviceStorageManager.getSelection();\n if (devices) {\n if (!config.settings) {\n config.settings = {};\n }\n if (devices.audioInput?.deviceId) {\n config.settings.audioInputDeviceId = config.settings.audioInputDeviceId || devices.audioInput.deviceId;\n }\n if (devices.audioOutput?.deviceId) {\n config.settings.audioOutputDeviceId = config.settings.audioOutputDeviceId || devices.audioOutput.deviceId;\n }\n if (devices.videoInput?.deviceId) {\n config.settings.videoDeviceId = config.settings.videoDeviceId || devices.videoInput.deviceId;\n }\n }\n }\n config.autoManageVideo = config.autoManageVideo !== false;\n config.autoManageWakeLock = config.autoManageWakeLock !== false;\n this.config = config;\n this.setEnv();\n }\n\n addPeer(peer: HMSPeer) {\n this.peers[peer.peerId] = peer;\n if (peer.isLocal) {\n this.localPeerId = peer.peerId;\n }\n }\n\n /**\n * @param {HMSTrack} track the published track that has to be added\n *\n * Note: Only use this method to add published tracks not preview traks\n */\n addTrack(track: HMSTrack) {\n this.tracks.set(track, track);\n }\n\n getTrackState(trackId: string) {\n return this.peerTrackStates[trackId];\n }\n\n setTrackState(trackStateEntry: TrackStateEntry) {\n this.peerTrackStates[trackStateEntry.trackInfo.track_id] = trackStateEntry;\n }\n\n removePeer(peerId: string) {\n if (this.localPeerId === peerId) {\n this.localPeerId = undefined;\n }\n delete this.peers[peerId];\n }\n\n removeTrack(track: HMSTrack) {\n this.tracks.delete(track);\n }\n\n updateSpeakers(speakers: HMSSpeaker[]) {\n this.speakers = speakers;\n }\n\n async updateAudioOutputVolume(value: number) {\n for (const track of this.getAudioTracks()) {\n await track.setVolume(value);\n }\n }\n\n async updateAudioOutputDevice(device: MediaDeviceInfo) {\n const promises: Promise<void>[] = [];\n this.getAudioTracks().forEach(track => {\n if (track instanceof HMSRemoteAudioTrack) {\n promises.push(track.setOutputDevice(device));\n }\n });\n await Promise.all(promises);\n }\n\n getSimulcastLayers(source: HMSTrackSource): SimulcastLayer[] {\n // Enable only when backend enables and source is video or screen. ignore videoplaylist\n if (!this.simulcastEnabled || !['screen', 'regular'].includes(source)) {\n return [];\n }\n if (source === 'screen') {\n return []; //this.screenshareLayers?.layers || []; uncomment this when screenshare simulcast supported\n }\n return this.videoLayers?.layers || [];\n }\n\n /**\n * Convert maxBitrate from kbps to bps\n * @internal\n * @param simulcastLayers\n * @returns {SimulcastLayers}\n */\n private convertSimulcastLayers(simulcastLayers?: SimulcastLayers) {\n if (!simulcastLayers) {\n return;\n }\n return {\n ...simulcastLayers,\n layers: (simulcastLayers.layers || []).map(layer => {\n return {\n ...layer,\n maxBitrate: layer.maxBitrate * 1000,\n };\n }),\n };\n }\n\n getSimulcastDefinitionsForPeer(peer: HMSPeer, source: HMSTrackSource) {\n // TODO: remove screen check when screenshare simulcast is supported\n if ([!peer || !peer.role, source === 'screen', !this.simulcastEnabled].some(value => !!value)) {\n return [];\n }\n\n const publishParams = this.getPolicyForRole(peer.role!.name).publishParams;\n let simulcastLayers: SimulcastLayers | undefined;\n let width: number;\n let height: number;\n if (source === 'regular') {\n simulcastLayers = publishParams.simulcast?.video;\n width = publishParams.video.width;\n height = publishParams.video.height;\n } else if (source === 'screen') {\n simulcastLayers = publishParams.simulcast?.screen;\n width = publishParams.screen.width;\n height = publishParams.screen.height;\n }\n return (\n simulcastLayers?.layers?.map(value => {\n const layer = simulcastMapping[value.rid as RID];\n const resolution = {\n width: Math.floor(width / value.scaleResolutionDownBy),\n height: Math.floor(height / value.scaleResolutionDownBy),\n };\n return {\n layer,\n resolution,\n } as HMSSimulcastLayerDefinition;\n }) || []\n );\n }\n\n setPoll(poll: HMSPoll) {\n this.polls.set(poll.id, poll);\n }\n\n getPoll(id: string): HMSPoll | undefined {\n return this.polls.get(id);\n }\n\n getErrorListener() {\n return this.errorListener;\n }\n\n cleanup() {\n const tracks = this.getTracks();\n for (const track of tracks) {\n track.cleanup();\n }\n this.room = undefined;\n this.config = undefined;\n this.localPeerId = undefined;\n this.roleDetailsArrived = false;\n }\n\n setErrorListener(listener: IErrorListener) {\n this.errorListener = listener;\n }\n\n private updatePeersPolicy() {\n this.getPeers().forEach(peer => {\n if (!peer.role) {\n this.errorListener?.onError(ErrorFactory.GenericErrors.InvalidRole(HMSAction.VALIDATION, ''));\n return;\n }\n peer.role = this.getPolicyForRole(peer.role.name);\n });\n }\n\n private setEnv() {\n const endPoint = this.config?.initEndpoint!;\n const url = endPoint.split('https://')[1];\n let env: ENV = ENV.PROD;\n if (url.startsWith(ENV.PROD)) {\n env = ENV.PROD;\n } else if (url.startsWith(ENV.QA)) {\n env = ENV.QA;\n } else if (url.startsWith(ENV.DEV)) {\n env = ENV.DEV;\n }\n this.env = env;\n HTTPAnalyticsTransport.setEnv(env);\n }\n}\n\nexport { Store };\n", "import HMSLogger from '../utils/logger';\n\nexport class WakeLockManager {\n private readonly TAG = '[WakeLockManager]';\n private wakeLock: WakeLockSentinel | null = null;\n\n acquireLock = async () => {\n await this.requestWakeLock();\n document?.addEventListener('visibilitychange', this.visibilityHandler);\n };\n\n cleanup = async () => {\n if (this.wakeLock && !this.wakeLock.released) {\n try {\n await this.wakeLock.release();\n HMSLogger.d(this.TAG, 'Wake lock released');\n } catch (err) {\n const error = err as Error;\n HMSLogger.w(this.TAG, 'Error while releasing wake lock', `name=${error.name}, message=${error.message}`);\n }\n }\n this.wakeLock = null;\n };\n\n private visibilityHandler = async () => {\n if (document?.visibilityState === 'visible' && (!this.wakeLock || this.wakeLock.released)) {\n HMSLogger.d(this.TAG, 'Re-acquiring wake lock due to visibility change');\n await this.requestWakeLock();\n }\n };\n\n // Function that attempts to request a screen wake lock.\n private requestWakeLock = async () => {\n try {\n if (!('wakeLock' in navigator)) {\n HMSLogger.d(this.TAG, 'Wake lock feature not supported');\n return;\n }\n this.wakeLock = await navigator.wakeLock.request('screen');\n HMSLogger.d(this.TAG, 'Wake lock acquired');\n } catch (err) {\n const error = err as Error;\n HMSLogger.w(this.TAG, 'Error acquiring wake lock', `name=${error.name}, message=${error.message}`);\n }\n };\n}\n", "import AnalyticsEvent from './AnalyticsEvent';\nimport { HMSAnalyticsLevel } from './AnalyticsEventLevel';\nimport { AnalyticsTransport } from './AnalyticsTransport';\nimport { HTTPAnalyticsTransport } from './HTTPAnalyticsTransport';\nimport { IStore } from '../sdk/store';\nimport { ANALYTICS_BUFFER_SIZE } from '../utils/constants';\nimport HMSLogger from '../utils/logger';\n\nexport class AnalyticsEventsService {\n private bufferSize = ANALYTICS_BUFFER_SIZE;\n private readonly TAG = '[AnalyticsEventsService]';\n\n private transport: AnalyticsTransport | null = null;\n private pendingEvents: AnalyticsEvent[] = [];\n\n level: HMSAnalyticsLevel = HMSAnalyticsLevel.INFO;\n\n constructor(private store: IStore) {}\n\n setTransport(transport: AnalyticsTransport) {\n this.transport = transport;\n }\n\n reset() {\n this.transport = null;\n this.pendingEvents = [];\n }\n\n queue(event: AnalyticsEvent) {\n if (event.level >= this.level) {\n this.pendingEvents.push(event);\n\n if (this.pendingEvents.length > this.bufferSize) {\n const removedEvent = this.pendingEvents.shift();\n HMSLogger.d(this.TAG, 'Max buffer size reached', 'Removed event to accommodate new events', removedEvent);\n }\n }\n return this;\n }\n\n flushFailedClientEvents() {\n HTTPAnalyticsTransport.flushFailedEvents();\n }\n\n flush() {\n try {\n while (this.pendingEvents.length > 0) {\n const event = this.pendingEvents.shift();\n if (event) {\n event.metadata.peer.peer_id = this.store.getLocalPeer()?.peerId;\n event.metadata.userAgent = this.store.getUserAgent();\n if (this.transport && this.transport.transportProvider.isConnected) {\n this.transport.sendEvent(event);\n } else {\n this.sendClientEventOnHTTP(event);\n }\n }\n }\n } catch (error) {\n HMSLogger.w(this.TAG, 'Flush Failed', error);\n }\n }\n\n private sendClientEventOnHTTP(event: AnalyticsEvent) {\n const room = this.store.getRoom();\n const localPeer = this.store.getLocalPeer();\n event.metadata.token = this.store.getConfig()?.authToken;\n event.metadata.peer = {\n session_id: room?.sessionId,\n room_id: room?.id,\n room_name: room?.name,\n template_id: room?.templateId,\n joined_at: room?.joinedAt?.getTime(),\n session_started_at: room?.startedAt?.getTime(),\n role: localPeer?.role?.name,\n user_name: localPeer?.name,\n user_data: localPeer?.metadata,\n };\n HTTPAnalyticsTransport.sendEvent(event);\n }\n}\n", "import { v4 as uuid } from 'uuid';\nimport AnalyticsEventFactory from '../analytics/AnalyticsEventFactory';\nimport { DeviceManager } from '../device-manager';\nimport { ErrorFactory } from '../error/ErrorFactory';\nimport { HMSAction } from '../error/HMSAction';\nimport { EventBus } from '../events/EventBus';\nimport { HMSDeviceChangeEvent, HMSTrackUpdate, HMSUpdateListener } from '../interfaces';\nimport { HMSRemoteAudioTrack } from '../media/tracks';\nimport { HMSRemotePeer } from '../sdk/models/peer';\nimport { IStore } from '../sdk/store';\nimport HMSLogger from '../utils/logger';\nimport { isMobile } from '../utils/support';\nimport { sleep } from '../utils/timer-utils';\n\n/**\n * Following are the errors thrown when autoplay is blocked in different browsers\n * Firefox - DOMException: The play method is not allowed by the user agent or the platform in the current context, possibly because the user denied permission.\n * Safari - NotAllowedError: The request is not allowed by the user agent or the platform in the current context, possibly because the user denied permission.\n * Chrome - DOMException: play() failed because the user didn't interact with the document first.\n * Brave - DOMException: play() can only be initiated by a user gesture.\n */\ntype AudioSinkState = {\n autoplayFailed?: boolean;\n initialized: boolean;\n // this promise will be set for the first track. remaining tracks will be processed once it's know whether\n // autoplay is allowed or not\n autoplayCheckPromise?: Promise<void>;\n};\n\nconst INITIAL_STATE: AudioSinkState = {\n autoplayFailed: undefined,\n initialized: false,\n autoplayCheckPromise: undefined,\n};\n\nexport class AudioSinkManager {\n private audioSink?: HTMLElement;\n private autoPausedTracks: Set<HMSRemoteAudioTrack> = new Set();\n private readonly TAG = '[AudioSinkManager]:';\n private volume = 100;\n private state = { ...INITIAL_STATE };\n private listener?: HMSUpdateListener;\n\n constructor(private store: IStore, private deviceManager: DeviceManager, private eventBus: EventBus) {\n this.eventBus.audioTrackAdded.subscribe(this.handleTrackAdd);\n this.eventBus.audioTrackRemoved.subscribe(this.handleTrackRemove);\n this.eventBus.audioTrackUpdate.subscribe(this.handleTrackUpdate);\n this.eventBus.deviceChange.subscribe(this.handleAudioDeviceChange);\n }\n\n setListener(listener?: HMSUpdateListener) {\n this.listener = listener;\n }\n\n private get outputDevice() {\n return this.deviceManager.outputDevice;\n }\n\n getVolume() {\n return this.volume;\n }\n\n async setVolume(value: number) {\n await this.store.updateAudioOutputVolume(value);\n this.volume = value;\n }\n\n /**\n * This function is to be called only on user interaction when\n * autoplay error is received.\n */\n async unblockAutoplay() {\n if (this.autoPausedTracks.size > 0) {\n this.unpauseAudioTracks();\n }\n }\n\n init(elementId?: string) {\n if (this.state.initialized || this.audioSink) {\n return;\n }\n this.state.initialized = true;\n const audioSink = document.createElement('div');\n audioSink.id = `HMS-SDK-audio-sink-${uuid()}`;\n const userElement = elementId && document.getElementById(elementId);\n const audioSinkParent = userElement || document.body;\n audioSinkParent.append(audioSink);\n\n this.audioSink = audioSink;\n HMSLogger.d(this.TAG, 'audio sink created', this.audioSink);\n }\n\n cleanup() {\n this.audioSink?.remove();\n this.audioSink = undefined;\n this.eventBus.audioTrackAdded.unsubscribe(this.handleTrackAdd);\n this.eventBus.audioTrackRemoved.unsubscribe(this.handleTrackRemove);\n this.eventBus.audioTrackUpdate.unsubscribe(this.handleTrackUpdate);\n this.eventBus.deviceChange.unsubscribe(this.handleAudioDeviceChange);\n this.autoPausedTracks = new Set();\n this.state = { ...INITIAL_STATE };\n }\n\n private handleAudioPaused = async (event: any) => {\n const audioEl = event.target as HTMLAudioElement;\n //@ts-ignore\n const track = audioEl.srcObject?.getAudioTracks()[0];\n if (!track?.enabled) {\n // No need to play if already disabled\n return;\n }\n // this means the audio paused because of external factors(headset removal)\n HMSLogger.d(this.TAG, 'Audio Paused', event.target.id);\n const audioTrack = this.store.getTrackById(event.target.id);\n if (audioTrack) {\n if (isMobile()) {\n // Play after a delay since mobile devices don't call onDevice change event\n await sleep(500);\n this.playAudioFor(audioTrack as HMSRemoteAudioTrack);\n } else {\n this.autoPausedTracks.add(audioTrack as HMSRemoteAudioTrack);\n }\n }\n };\n\n private handleTrackUpdate = ({ track }: { track: HMSRemoteAudioTrack; enabled: boolean }) => {\n HMSLogger.d(this.TAG, 'Track updated', `${track}`);\n };\n\n private handleTrackAdd = async ({\n track,\n peer,\n callListener = true,\n }: {\n track: HMSRemoteAudioTrack;\n peer: HMSRemotePeer;\n callListener?: boolean;\n }) => {\n const audioEl = document.createElement('audio');\n audioEl.style.display = 'none';\n audioEl.id = track.trackId;\n audioEl.addEventListener('pause', this.handleAudioPaused);\n\n audioEl.onerror = async () => {\n HMSLogger.e(this.TAG, 'error on audio element', audioEl.error);\n const ex = ErrorFactory.TracksErrors.AudioPlaybackError(\n `Audio playback error for track - ${track.trackId} code - ${audioEl?.error?.code}`,\n );\n this.eventBus.analytics.publish(AnalyticsEventFactory.audioPlaybackError(ex));\n if (audioEl?.error?.code === MediaError.MEDIA_ERR_DECODE) {\n this.removeAudioElement(audioEl, track);\n await sleep(500);\n await this.handleTrackAdd({ track, peer, callListener: false });\n }\n };\n track.setAudioElement(audioEl);\n track.setVolume(this.volume);\n HMSLogger.d(this.TAG, 'Audio track added', `${track}`);\n this.init(); // call to create sink element if not already created\n this.audioSink?.append(audioEl);\n this.outputDevice && (await track.setOutputDevice(this.outputDevice));\n audioEl.srcObject = new MediaStream([track.nativeTrack]);\n callListener && this.listener?.onTrackUpdate(HMSTrackUpdate.TRACK_ADDED, track, peer);\n await this.handleAutoplayError(track);\n };\n\n private handleAutoplayError = async (track: HMSRemoteAudioTrack) => {\n /**\n * if it's not known whether autoplay will succeed, wait for it to be known\n */\n if (this.state.autoplayFailed === undefined) {\n if (!this.state.autoplayCheckPromise) {\n // it's the first track, try to play it, that'll tell us whether autoplay is allowed\n this.state.autoplayCheckPromise = new Promise<void>(resolve => {\n this.playAudioFor(track).then(resolve);\n });\n }\n // and wait for the result to be known\n await this.state.autoplayCheckPromise;\n }\n /**\n * Don't play the track if autoplay failed, add to paused list\n */\n if (this.state.autoplayFailed) {\n this.autoPausedTracks.add(track);\n return;\n }\n await this.playAudioFor(track);\n };\n\n private handleAudioDeviceChange = (event: HMSDeviceChangeEvent) => {\n // if there is no selection that means this is an init request. No need to do anything\n if (event.error || !event.selection || event.type === 'video') {\n return;\n }\n this.unpauseAudioTracks();\n };\n\n /**\n * try to play audio for the passed in track, assume autoplay error happened if play fails\n * @param track\n * @private\n */\n private async playAudioFor(track: HMSRemoteAudioTrack) {\n const audioEl = track.getAudioElement();\n if (!audioEl) {\n HMSLogger.w(this.TAG, 'No audio element found on track', track.trackId);\n return;\n }\n try {\n await audioEl.play();\n this.state.autoplayFailed = false;\n this.autoPausedTracks.delete(track);\n HMSLogger.d(this.TAG, 'Played track', `${track}`);\n } catch (err) {\n this.autoPausedTracks.add(track);\n HMSLogger.w(this.TAG, 'Failed to play track', `${track}`, err as Error);\n const error = err as Error;\n if (!this.state.autoplayFailed && error.name === 'NotAllowedError') {\n this.state.autoplayFailed = true;\n const ex = ErrorFactory.TracksErrors.AutoplayBlocked(HMSAction.AUTOPLAY, '');\n ex.addNativeError(error);\n this.eventBus.analytics.publish(AnalyticsEventFactory.autoplayError());\n this.eventBus.autoplayError.publish(ex);\n }\n }\n }\n\n private handleTrackRemove = (track: HMSRemoteAudioTrack) => {\n this.autoPausedTracks.delete(track);\n const audioEl = document.getElementById(track.trackId) as HTMLAudioElement;\n if (audioEl) {\n this.removeAudioElement(audioEl, track);\n }\n // Reset autoplay error thrown because if all tracks are removed and a new track is added\n // Autoplay error is thrown in safari\n if (this.audioSink && this.audioSink.childElementCount === 0) {\n this.state.autoplayCheckPromise = undefined;\n this.state.autoplayFailed = undefined;\n }\n HMSLogger.d(this.TAG, 'Audio track removed', `${track}`);\n };\n\n private unpauseAudioTracks = async () => {\n const promises: Promise<void>[] = [];\n this.autoPausedTracks.forEach(track => {\n promises.push(this.playAudioFor(track));\n });\n // Return after all pending tracks are played\n await Promise.all(promises);\n };\n\n private removeAudioElement = (audioEl: HTMLAudioElement, track: HMSRemoteAudioTrack) => {\n if (audioEl) {\n HMSLogger.d(this.TAG, 'removing audio element', `${track}`);\n audioEl.removeEventListener('pause', this.handleAudioPaused);\n audioEl.srcObject = null;\n audioEl.remove();\n track.setAudioElement(null);\n }\n };\n}\n", "import { DeviceStorageManager } from './DeviceStorage';\nimport AnalyticsEventFactory from '../analytics/AnalyticsEventFactory';\nimport { HMSException } from '../error/HMSException';\nimport { EventBus } from '../events/EventBus';\nimport { DeviceMap, HMSDeviceChangeEvent, SelectedDevices } from '../interfaces';\nimport { HMSAudioTrackSettingsBuilder, HMSVideoTrackSettingsBuilder } from '../media/settings';\nimport { HMSLocalAudioTrack, HMSLocalTrack, HMSLocalVideoTrack } from '../media/tracks';\nimport { IStore } from '../sdk/store';\nimport HMSLogger from '../utils/logger';\nimport { debounce } from '../utils/timer-utils';\n\ntype DeviceAndGroup = Partial<MediaTrackSettings>;\n\ninterface HMSDeviceManager extends DeviceMap {\n outputDevice?: MediaDeviceInfo;\n hasWebcamPermission: boolean;\n hasMicrophonePermission: boolean;\n}\n\nexport class DeviceManager implements HMSDeviceManager {\n audioInput: MediaDeviceInfo[] = [];\n audioOutput: MediaDeviceInfo[] = [];\n videoInput: MediaDeviceInfo[] = [];\n outputDevice?: MediaDeviceInfo;\n // true if user has allowed the permission\n // false if user has denied the permission or prompt was never shown or ignored\n // or if the camera/mic is not available in the device\n hasWebcamPermission = false;\n hasMicrophonePermission = false;\n\n private readonly TAG = '[Device Manager]:';\n private initialized = false;\n private videoInputChanged = false;\n private audioInputChanged = false;\n\n constructor(private store: IStore, private eventBus: EventBus) {\n const isLocalTrackEnabled = ({ enabled, track }: { enabled: boolean; track: HMSLocalTrack }) =>\n enabled && track.source === 'regular';\n this.eventBus.localVideoEnabled.waitFor(isLocalTrackEnabled).then(async () => {\n await this.enumerateDevices();\n if (this.videoInputChanged) {\n this.eventBus.deviceChange.publish({ devices: this.getDevices() } as HMSDeviceChangeEvent);\n }\n });\n this.eventBus.localAudioEnabled.waitFor(isLocalTrackEnabled).then(async () => {\n await this.enumerateDevices();\n if (this.audioInputChanged) {\n this.eventBus.deviceChange.publish({ devices: this.getDevices() } as HMSDeviceChangeEvent);\n }\n });\n }\n\n updateOutputDevice = async (deviceId?: string) => {\n const newDevice = this.audioOutput.find(device => device.deviceId === deviceId);\n if (newDevice) {\n this.outputDevice = newDevice;\n await this.store.updateAudioOutputDevice(newDevice);\n DeviceStorageManager.updateSelection('audioOutput', { deviceId: newDevice.deviceId, groupId: newDevice.groupId });\n }\n return newDevice;\n };\n\n async init(force = false) {\n if (this.initialized && !force) {\n return;\n }\n !this.initialized && navigator.mediaDevices.addEventListener('devicechange', this.handleDeviceChange);\n this.initialized = true;\n await this.enumerateDevices();\n this.logDevices('Init');\n await this.setOutputDevice();\n this.eventBus.deviceChange.publish({\n devices: this.getDevices(),\n } as HMSDeviceChangeEvent);\n this.eventBus.analytics.publish(\n AnalyticsEventFactory.deviceChange({\n selection: this.getCurrentSelection(),\n type: 'list',\n devices: this.getDevices(),\n }),\n );\n }\n\n getDevices(): DeviceMap {\n return {\n audioInput: this.audioInput,\n audioOutput: this.audioOutput,\n videoInput: this.videoInput,\n };\n }\n\n cleanup() {\n this.initialized = false;\n this.audioInput = [];\n this.audioOutput = [];\n this.videoInput = [];\n this.outputDevice = undefined;\n navigator.mediaDevices.removeEventListener('devicechange', this.handleDeviceChange);\n }\n\n getCurrentSelection = (): SelectedDevices => {\n const localPeer = this.store.getLocalPeer();\n const audioDevice = this.createIdentifier(localPeer?.audioTrack?.getMediaTrackSettings());\n const videoDevice = this.createIdentifier(localPeer?.videoTrack?.getMediaTrackSettings());\n const audioSelection = this.audioInput.find(device => {\n const id = this.createIdentifier(device);\n return id === audioDevice;\n });\n const videoSelection = this.videoInput.find(device => this.createIdentifier(device) === videoDevice);\n return {\n audioInput: audioSelection,\n videoInput: videoSelection,\n audioOutput: this.outputDevice,\n };\n };\n\n private createIdentifier(deviceInfo?: DeviceAndGroup) {\n if (!deviceInfo) {\n return '';\n }\n return `${deviceInfo.deviceId}${deviceInfo.groupId}`;\n }\n\n private computeChange = (prevDevices: string[], currentDevices: MediaDeviceInfo[]) => {\n if (prevDevices.length !== currentDevices.length) {\n return true;\n }\n return currentDevices.some(device => !prevDevices.includes(this.createIdentifier(device)));\n };\n\n private enumerateDevices = async () => {\n try {\n const devices = await navigator.mediaDevices.enumerateDevices();\n const prevVideoInput = this.videoInput.map(this.createIdentifier);\n const prevAudioInput = this.audioInput.map(this.createIdentifier);\n this.audioInput = [];\n this.audioOutput = [];\n this.videoInput = [];\n devices.forEach(device => {\n if (device.kind === 'audioinput' && device.label) {\n this.hasMicrophonePermission = true;\n this.audioInput.push(device as MediaDeviceInfo);\n } else if (device.kind === 'audiooutput') {\n this.audioOutput.push(device);\n } else if (device.kind === 'videoinput' && device.label) {\n this.hasWebcamPermission = true;\n this.videoInput.push(device as MediaDeviceInfo);\n }\n });\n this.videoInputChanged = this.computeChange(prevVideoInput, this.videoInput);\n this.audioInputChanged = this.computeChange(prevAudioInput, this.audioInput);\n DeviceStorageManager.setDevices({\n videoInput: [...this.videoInput],\n audioInput: [...this.audioInput],\n audioOutput: [...this.audioOutput],\n });\n this.logDevices('Enumerate Devices');\n } catch (error) {\n HMSLogger.e(this.TAG, 'Failed enumerating devices', error);\n }\n };\n\n private handleDeviceChange = debounce(async () => {\n await this.enumerateDevices();\n this.logDevices('After Device Change');\n const localPeer = this.store.getLocalPeer();\n await this.setOutputDevice(true);\n await this.handleAudioInputDeviceChange(localPeer?.audioTrack);\n await this.handleVideoInputDeviceChange(localPeer?.videoTrack);\n this.eventBus.analytics.publish(\n AnalyticsEventFactory.deviceChange({\n selection: this.getCurrentSelection(),\n type: 'change',\n devices: this.getDevices(),\n }),\n );\n }, 500).bind(this);\n\n /**\n * Function to get the device after device change\n * Chrome and Edge provide a default device from which we select the actual device\n * Firefox and safari give 0th device as system default\n * @returns {MediaDeviceInfo}\n */\n getNewAudioInputDevice() {\n const defaultDevice = this.audioInput.find(device => device.deviceId === 'default');\n if (defaultDevice) {\n // Selecting a non-default device so that the deviceId comparision does not give\n // false positives when device is removed, because the other available device\n // get's the deviceId as default once this device is removed\n const nextDevice = this.audioInput.find(device => {\n return device.deviceId !== 'default' && defaultDevice.label.includes(device.label);\n });\n return nextDevice;\n }\n return this.audioInput[0];\n }\n\n /**\n * This method is to select the input/output from same group\n * same group meaning both input/output are of same device\n * This method might override the default coming from browser and system so as to select options from same\n * device type. This is required in certain cases where browser's default is not correct.\n * Algo:\n * 1. find the non default input device if selected one is default by matching device label\n * 2. find the corresponding output device which has the same group id or same label\n * 3. select the previous selected device if nothing was found\n * 4. select the default one if no matching device was found and previous device doesn't exist anymore\n * 5. select the first option if there is no default\n */\n async setOutputDevice(deviceChange = false) {\n const inputDevice = this.getNewAudioInputDevice();\n const prevSelection = this.createIdentifier(this.outputDevice);\n this.outputDevice = this.getAudioOutputDeviceMatchingInput(inputDevice);\n if (!this.outputDevice) {\n // there is no matching device, let's revert back to the prev selected device\n this.outputDevice = this.audioOutput.find(device => this.createIdentifier(device) === prevSelection);\n if (!this.outputDevice) {\n // prev device doesn't exist as well, select default deviceId device if available, otherwise select 0th device\n this.outputDevice = this.audioOutput.find(device => device.deviceId === 'default') || this.audioOutput[0];\n }\n }\n await this.store.updateAudioOutputDevice(this.outputDevice);\n // send event only on device change and device is not same as previous\n if (deviceChange && prevSelection !== this.createIdentifier(this.outputDevice)) {\n this.eventBus.analytics.publish(\n AnalyticsEventFactory.deviceChange({\n selection: { audioOutput: this.outputDevice },\n devices: this.getDevices(),\n type: 'audioOutput',\n }),\n );\n this.eventBus.deviceChange.publish({\n selection: this.outputDevice,\n type: 'audioOutput',\n devices: this.getDevices(),\n } as HMSDeviceChangeEvent);\n }\n }\n\n private handleAudioInputDeviceChange = async (audioTrack?: HMSLocalAudioTrack) => {\n if (!audioTrack) {\n HMSLogger.d(this.TAG, 'No Audio track on local peer');\n return;\n }\n // no need to proceed further if input has not changed\n if (!this.audioInputChanged) {\n HMSLogger.d(this.TAG, 'No Change in AudioInput Device');\n return;\n }\n const newSelection = this.getNewAudioInputDevice();\n if (!newSelection || !newSelection.deviceId) {\n this.eventBus.analytics.publish(\n AnalyticsEventFactory.deviceChange({\n selection: { audioInput: newSelection },\n error: new Error('Audio device not found') as HMSException,\n devices: this.getDevices(),\n type: 'audioInput',\n }),\n );\n HMSLogger.w(this.TAG, 'Audio device not found');\n return;\n }\n const { settings } = audioTrack;\n const newAudioTrackSettings = new HMSAudioTrackSettingsBuilder()\n .codec(settings.codec)\n .maxBitrate(settings.maxBitrate)\n .deviceId(newSelection.deviceId)\n .build();\n try {\n await audioTrack.setSettings(newAudioTrackSettings, true);\n this.eventBus.deviceChange.publish({\n devices: this.getDevices(),\n selection: newSelection,\n type: 'audioInput',\n } as HMSDeviceChangeEvent);\n this.logDevices('Audio Device Change Success');\n } catch (error) {\n HMSLogger.e(this.TAG, '[Audio Device Change]', error);\n this.eventBus.analytics.publish(\n AnalyticsEventFactory.deviceChange({\n selection: { audioInput: newSelection },\n devices: this.getDevices(),\n type: 'audioInput',\n error: error as HMSException,\n }),\n );\n this.eventBus.deviceChange.publish({\n error,\n selection: newSelection,\n type: 'audioInput',\n devices: this.getDevices(),\n } as HMSDeviceChangeEvent);\n }\n };\n\n private handleVideoInputDeviceChange = async (videoTrack?: HMSLocalVideoTrack) => {\n if (!videoTrack) {\n HMSLogger.d(this.TAG, 'No video track on local peer');\n return;\n }\n // no need to proceed further if input has not changed\n if (!this.videoInputChanged) {\n HMSLogger.d(this.TAG, 'No Change in VideoInput Device');\n return;\n }\n const newSelection = this.videoInput[0];\n if (!newSelection || !newSelection.deviceId) {\n this.eventBus.analytics.publish(\n AnalyticsEventFactory.deviceChange({\n selection: { videoInput: newSelection },\n error: new Error('Video device not found') as HMSException,\n devices: this.getDevices(),\n type: 'video',\n }),\n );\n HMSLogger.w(this.TAG, 'Video device not found');\n return;\n }\n const { settings } = videoTrack;\n const newVideoTrackSettings = new HMSVideoTrackSettingsBuilder()\n .codec(settings.codec)\n .maxBitrate(settings.maxBitrate)\n .maxFramerate(settings.maxFramerate)\n .setWidth(settings.width)\n .setHeight(settings.height)\n .deviceId(newSelection.deviceId)\n .build();\n try {\n await (videoTrack as HMSLocalVideoTrack).setSettings(newVideoTrackSettings, true);\n // On replace track, enabled will be true. Need to be set to previous state\n // videoTrack.setEnabled(enabled); // TODO: remove this once verified on qa.\n this.eventBus.deviceChange.publish({\n devices: this.getDevices(),\n selection: newSelection,\n type: 'video',\n } as HMSDeviceChangeEvent);\n this.logDevices('Video Device Change Success');\n } catch (error) {\n HMSLogger.e(this.TAG, '[Video Device Change]', error);\n this.eventBus.analytics.publish(\n AnalyticsEventFactory.deviceChange({\n selection: { videoInput: newSelection },\n devices: this.getDevices(),\n type: 'video',\n error: error as HMSException,\n }),\n );\n this.eventBus.deviceChange.publish({\n error: error as Error,\n type: 'video',\n selection: newSelection,\n devices: this.getDevices(),\n } as HMSDeviceChangeEvent);\n }\n };\n\n private getAudioOutputDeviceMatchingInput(inputDevice?: MediaDeviceInfo) {\n const blacklist = this.store.getConfig()?.settings?.speakerAutoSelectionBlacklist || [];\n if (blacklist === 'all') {\n return;\n }\n\n const inputLabel = inputDevice?.label.toLowerCase() || '';\n if (blacklist.some(label => inputLabel.includes(label.toLowerCase()))) {\n return;\n }\n\n if (inputDevice?.groupId) {\n // only check for label because if groupId check is added it will select speaker\n // when an external earphone without microphone is added\n return this.audioOutput.find(device => inputDevice.deviceId !== 'default' && device.label === inputDevice.label);\n }\n\n return;\n }\n\n private logDevices(label = '') {\n HMSLogger.d(\n this.TAG,\n label,\n JSON.stringify(\n {\n videoInput: [...this.videoInput],\n audioInput: [...this.audioInput],\n audioOutput: [...this.audioOutput],\n selected: this.getCurrentSelection(),\n },\n null,\n 4,\n ),\n );\n }\n}\n", "import { DeviceManager } from './DeviceManager';\nimport { AudioSinkManager } from '../audio-sink-manager';\nimport { HMSAudioContextHandler } from '../utils/media';\n\nexport interface IAudioOutputManager {\n getDevice(): MediaDeviceInfo | undefined;\n setDevice(deviceId: string): Promise<MediaDeviceInfo | undefined>;\n getVolume(): number;\n setVolume(value: number): void;\n}\n\nexport class AudioOutputManager implements IAudioOutputManager {\n constructor(private deviceManager: DeviceManager, private audioSinkManager: AudioSinkManager) {}\n\n getVolume() {\n return this.audioSinkManager.getVolume();\n }\n\n setVolume(value: number) {\n if (value < 0 || value > 100) {\n throw Error('Please pass a valid number between 0-100');\n }\n this.audioSinkManager.setVolume(value);\n }\n\n getDevice() {\n return this.deviceManager.outputDevice;\n }\n\n setDevice(deviceId?: string) {\n return this.deviceManager.updateOutputDevice(deviceId);\n }\n\n async unblockAutoplay() {\n await this.audioSinkManager.unblockAutoplay();\n /**\n * similar to autoplay error when there's no user interaction,\n * audio context is paused due to which empty audio tracks do not send any data and therefore it doesn't reach SFU.\n * resume audio context on user interaction to enable empty audio tracks to send data and be forwarded to remote peers\n */\n await HMSAudioContextHandler.resumeContext();\n }\n}\n", "import { EventEmitter2 as EventEmitter } from 'eventemitter2';\nimport { HMSInternalEvent } from './HMSInternalEvent';\nimport AnalyticsEvent from '../analytics/AnalyticsEvent';\nimport { HMSException } from '../error/HMSException';\nimport { HMSDeviceChangeEvent, HMSRole } from '../interfaces';\nimport {\n HMSLocalAudioTrack,\n HMSLocalVideoTrack,\n HMSRemoteAudioTrack,\n HMSRemoteVideoTrack,\n HMSWebrtcStats,\n} from '../internal';\nimport { PolicyParams } from '../notification-manager/HMSNotifications';\nimport { HMSRemotePeer } from '../sdk/models/peer';\nimport { HMSEvents } from '../utils/constants';\nimport { ITrackAudioLevelUpdate } from '../utils/track-audio-level-monitor';\n\nexport class EventBus {\n private eventEmitter: EventEmitter = new EventEmitter();\n readonly deviceChange = new HMSInternalEvent<HMSDeviceChangeEvent>(HMSEvents.DEVICE_CHANGE, this.eventEmitter);\n readonly localAudioEnabled = new HMSInternalEvent<{ enabled: boolean; track: HMSLocalAudioTrack }>(\n HMSEvents.LOCAL_AUDIO_ENABLED,\n this.eventEmitter,\n );\n readonly localVideoEnabled = new HMSInternalEvent<{ enabled: boolean; track: HMSLocalVideoTrack }>(\n HMSEvents.LOCAL_VIDEO_ENABLED,\n this.eventEmitter,\n );\n\n /**\n * Emitter which processes raw RTC stats from rtcStatsUpdate and calls client callback\n */\n readonly statsUpdate = new HMSInternalEvent<HMSWebrtcStats>(HMSEvents.STATS_UPDATE, this.eventEmitter);\n\n readonly trackDegraded = new HMSInternalEvent<HMSRemoteVideoTrack>(HMSEvents.TRACK_DEGRADED, this.eventEmitter);\n readonly trackRestored = new HMSInternalEvent<HMSRemoteVideoTrack>(HMSEvents.TRACK_RESTORED, this.eventEmitter);\n\n /**\n * Emits audio level updates for audio tracks(used with local track in preview)\n */\n readonly trackAudioLevelUpdate = new HMSInternalEvent<ITrackAudioLevelUpdate>(\n HMSEvents.TRACK_AUDIO_LEVEL_UPDATE,\n this.eventEmitter,\n );\n\n readonly audioPluginFailed = new HMSInternalEvent<HMSException>(HMSEvents.AUDIO_PLUGIN_FAILED, this.eventEmitter);\n\n readonly localAudioSilence = new HMSInternalEvent<{ track: HMSLocalAudioTrack }>(\n HMSEvents.LOCAL_AUDIO_SILENCE,\n this.eventEmitter,\n );\n\n readonly analytics = new HMSInternalEvent<AnalyticsEvent>(HMSEvents.ANALYTICS, this.eventEmitter);\n\n readonly policyChange = new HMSInternalEvent<PolicyParams>(HMSEvents.POLICY_CHANGE, this.eventEmitter);\n\n readonly localRoleUpdate = new HMSInternalEvent<{ oldRole: HMSRole; newRole: HMSRole }>(\n HMSEvents.LOCAL_ROLE_UPDATE,\n this.eventEmitter,\n );\n\n readonly audioTrackUpdate = new HMSInternalEvent<{ track: HMSRemoteAudioTrack; enabled: boolean }>(\n HMSEvents.AUDIO_TRACK_UPDATE,\n this.eventEmitter,\n );\n\n readonly audioTrackAdded = new HMSInternalEvent<{ track: HMSRemoteAudioTrack; peer: HMSRemotePeer }>(\n HMSEvents.AUDIO_TRACK_ADDED,\n this.eventEmitter,\n );\n\n readonly audioTrackRemoved = new HMSInternalEvent<HMSRemoteAudioTrack>(\n HMSEvents.AUDIO_TRACK_REMOVED,\n this.eventEmitter,\n );\n\n readonly autoplayError = new HMSInternalEvent<HMSException>(HMSEvents.AUTOPLAY_ERROR, this.eventEmitter);\n\n readonly leave = new HMSInternalEvent<HMSException | undefined>(HMSEvents.LEAVE, this.eventEmitter);\n}\n", "import type { WaitForFilter, WaitForOptions } from 'eventemitter2';\nimport { EventEmitter2 as EventEmitter } from 'eventemitter2';\n\nexport class HMSInternalEvent<T> {\n constructor(private eventName: string, private eventEmitter: EventEmitter) {}\n\n publish = (event?: T) => {\n this.eventEmitter.emit(this.eventName, event);\n };\n subscribe = (fn: (event: T) => void | Promise<void>) => {\n this.eventEmitter.on(this.eventName, fn);\n };\n subscribeOnce = (fn: (event: T) => void | Promise<void>) => {\n this.eventEmitter.once(this.eventName, fn);\n };\n unsubscribe = (fn: (event: T) => void | Promise<void>) => {\n this.eventEmitter.off(this.eventName, fn);\n };\n waitFor = (predicate: WaitForFilter) => {\n return this.eventEmitter.waitFor(this.eventName, {\n filter: predicate,\n } as WaitForOptions);\n };\n removeAllListeners = () => {\n this.eventEmitter.removeAllListeners(this.eventName);\n };\n}\n", "import { VideoTrackLayerUpdate } from '../connection/channel-messages';\nimport { HMSRole } from '../interfaces/role';\nimport { HMSLocalTrack } from '../media/tracks';\nimport { HMSTrack, HMSTrackSource } from '../media/tracks/HMSTrack';\nimport { PollInfoParams, PollResult, Track } from '../signal/interfaces';\n\n/**\n * Interfaces for message received from BIZ Signal through Websocket.\n * These messages are handled by NotificationManager\n * which will call the corresponding HMSUpdateListener callbacks.\n */\n\nexport interface ServerError {\n code: number;\n message?: string;\n}\n\nexport interface TrackStateNotification {\n tracks: {\n [track_id: string]: TrackState;\n };\n peer: PeerNotificationInfo;\n}\n\nexport interface OnTrackLayerUpdateNotification {\n tracks: {\n [track_id: string]: VideoTrackLayerUpdate;\n };\n}\n\nexport interface PeerNotificationInfo {\n peer_id: string;\n info: Info;\n}\n\nexport interface Info {\n name: string;\n data: string;\n user_id: string;\n}\n\nexport interface PolicyParams {\n name: string;\n known_roles: {\n [role: string]: HMSRole;\n };\n template_id: string;\n app_data?: Record<string, string>;\n}\n\n/**\n * This is in a format biz sends/received the track metadata\n */\nexport class TrackState implements Track {\n mute: boolean;\n type: 'audio' | 'video';\n source: HMSTrackSource;\n description: string;\n track_id: string;\n stream_id: string;\n\n constructor(track: HMSLocalTrack | Track) {\n this.type = track.type;\n this.source = track.source || 'regular';\n this.description = '';\n if (track instanceof HMSTrack) {\n this.mute = !track.enabled;\n this.track_id = track.publishedTrackId!;\n this.stream_id = track.stream.id;\n } else {\n this.mute = track.mute;\n this.track_id = track.track_id;\n this.stream_id = track.stream_id;\n }\n }\n}\n\nexport interface PeerNotification {\n peer_id: string;\n info: Info;\n role: string;\n joined_at?: number;\n tracks: {\n [track_id: string]: TrackState;\n };\n is_from_room_state?: boolean;\n}\n\nexport interface RoomState {\n name: string;\n session_id?: string;\n started_at?: number;\n recording?: {\n sfu: {\n started_at?: number;\n enabled: boolean;\n };\n browser: {\n started_at?: number;\n enabled: boolean;\n };\n hls: {\n started_at?: number;\n enabled: boolean;\n config?: {\n hls_vod: boolean;\n single_file_per_layer: boolean;\n };\n };\n };\n streaming?: {\n enabled: boolean;\n rtmp: { enabled: boolean; started_at?: number };\n hls: HLSNotification;\n };\n}\n\nexport interface PeerListNotification {\n peers: {\n [peer_id: string]: PeerNotification;\n };\n room: RoomState;\n}\n\nexport interface PeriodicRoomState {\n peer_count: number;\n room: RoomState;\n peers?: {\n [peer_id: string]: PeerNotification;\n };\n}\n\ninterface Speaker {\n peer_id: string;\n track_id: string;\n level: number;\n}\n\nexport interface SpeakerList {\n 'speaker-list': Speaker[];\n}\n\ninterface ConnectionQuality {\n peer_id: string;\n downlink_score: number;\n}\n\nexport interface ConnectionQualityList {\n peers: ConnectionQuality[];\n}\n\n/**\n * Represents the role change request received from the server\n */\nexport interface RoleChangeRequestParams {\n requested_by?: string;\n role: string;\n token: string;\n}\n\nexport interface TrackUpdateRequestNotification {\n requested_by?: string;\n track_id: string;\n stream_id: string;\n mute: boolean;\n}\n\nexport interface ChangeTrackMuteStateNotification {\n requested_by?: string;\n roles?: string[];\n type?: 'audio' | 'video';\n source?: HMSTrackSource;\n value: boolean;\n}\n\nexport interface PeerLeaveRequestNotification {\n requested_by?: string;\n reason: string;\n room_end: boolean;\n}\n\nexport interface MessageNotification {\n peer?: {\n peer_id: string;\n info: {\n name: string;\n data: any;\n user_id: string;\n };\n };\n roles?: string[];\n message_id: string;\n private: boolean;\n timestamp: number;\n info: MessageNotificationInfo;\n}\n\nexport interface SendMessage {\n info: MessageNotificationInfo;\n roles?: string[];\n peer_id?: string;\n}\n\nexport interface MessageNotificationInfo {\n message: any;\n type: string;\n}\n\nexport interface RecordingNotification {\n type: 'sfu' | 'Browser';\n started_at?: number;\n peer?: PeerNotificationInfo;\n error?: ServerError;\n}\n\nexport interface RTMPNotification {\n peer?: PeerNotificationInfo;\n started_at?: number;\n error?: ServerError;\n}\n\nexport interface HLSNotification {\n enabled: boolean;\n variants?: Array<HLSVariantInfo>;\n error?: ServerError;\n hls_recording?: {\n hls_vod: boolean;\n single_file_per_layer: boolean;\n };\n}\n\nexport interface HLSVariantInfo {\n url: string;\n meeting_url?: string;\n metadata?: string;\n started_at?: number;\n}\n\nexport interface MetadataChangeNotification {\n values: {\n change_version?: number;\n updated_by?: string;\n data: any;\n key: string;\n updated_at?: number;\n }[];\n}\n\nexport interface PollStartNotification {\n polls: PollInfoParams[];\n}\n\nexport type PollStopNotification = PollStartNotification;\n\nexport interface PollStats extends PollResult {\n poll_id: string;\n}\nexport interface PollStatsNotification {\n polls: PollStats[];\n}\n", "import { HMSAudioListener, HMSPeerUpdate, HMSSpeaker, HMSUpdateListener } from '../../interfaces';\nimport { HMSAudioTrack } from '../../media/tracks';\nimport { IStore } from '../../sdk/store';\nimport { SpeakerList } from '../HMSNotifications';\n\nexport class ActiveSpeakerManager {\n constructor(private store: IStore, public listener?: HMSUpdateListener, public audioListener?: HMSAudioListener) {}\n\n handleActiveSpeakers(speakerList: SpeakerList) {\n const speakers = speakerList['speaker-list'];\n const hmsSpeakers: HMSSpeaker[] = speakers.map(speaker => ({\n audioLevel: speaker.level,\n peer: this.store.getPeerById(speaker.peer_id)!,\n track: this.store.getTrackById(speaker.track_id) as HMSAudioTrack,\n }));\n\n this.audioListener?.onAudioLevelUpdate(hmsSpeakers);\n this.store.updateSpeakers(hmsSpeakers);\n const dominantSpeaker = speakers[0];\n\n if (dominantSpeaker) {\n const dominantSpeakerPeer = this.store.getPeerById(dominantSpeaker.peer_id);\n this.listener?.onPeerUpdate(HMSPeerUpdate.BECAME_DOMINANT_SPEAKER, dominantSpeakerPeer!);\n } else {\n this.listener?.onPeerUpdate(HMSPeerUpdate.RESIGNED_DOMINANT_SPEAKER, null);\n }\n }\n}\n", "import { HMSUpdateListener } from '../../interfaces';\nimport Message from '../../sdk/models/HMSMessage';\nimport { HMSPeer } from '../../sdk/models/peer';\nimport { IStore } from '../../sdk/store';\nimport HMSLogger from '../../utils/logger';\nimport { HMSNotificationMethod } from '../HMSNotificationMethod';\nimport { MessageNotification } from '../HMSNotifications';\n\nexport class BroadcastManager {\n private readonly TAG = '[BroadcastManager]';\n constructor(private store: IStore, public listener?: HMSUpdateListener) {}\n\n handleNotification(method: string, notification: any) {\n if (method !== HMSNotificationMethod.BROADCAST) {\n return;\n }\n this.handleBroadcast(notification);\n }\n\n private handleBroadcast(messageNotification: MessageNotification) {\n const notifPeer = messageNotification.peer;\n const notifMessage = messageNotification.info;\n const notifRoles = messageNotification.roles;\n\n const sender = this.getSender(notifPeer);\n const recipientPeer = messageNotification.private ? this.store.getLocalPeer() : undefined;\n const recipientRoles = [];\n\n if (notifRoles?.length) {\n const knownRoles = this.store.getKnownRoles();\n for (const role of notifRoles) {\n knownRoles[role] && recipientRoles.push(knownRoles[role]);\n }\n }\n\n const hmsMessage = new Message({\n ...notifMessage,\n sender,\n recipientRoles,\n recipientPeer,\n time: new Date(messageNotification.timestamp),\n id: messageNotification.message_id,\n });\n HMSLogger.d(this.TAG, `Received Message from sender=${notifPeer?.peer_id}: ${hmsMessage}`);\n this.listener?.onMessageReceived(hmsMessage);\n }\n\n private getSender(notifPeer?: MessageNotification['peer']) {\n // If sender peerId is available in store, use that peer.\n let sender = notifPeer ? this.store.getPeerById(notifPeer.peer_id) : undefined;\n // If not available in store, use peer data from received broadcast message from Biz\n // notifPeer can be undefined when message is sent via api\n if (!sender && notifPeer) {\n sender = new HMSPeer({\n peerId: notifPeer.peer_id,\n name: notifPeer.info.name,\n isLocal: false,\n customerUserId: notifPeer.info.user_id,\n metadata: notifPeer.info.data,\n });\n }\n return sender;\n }\n}\n", "import { HMSConnectionQuality, HMSConnectionQualityListener } from '../../interfaces';\nimport { ConnectionQualityList } from '../HMSNotifications';\n\nexport class ConnectionQualityManager {\n constructor(public listener?: HMSConnectionQualityListener) {}\n\n handleQualityUpdate(qualityList: ConnectionQualityList) {\n const peers = qualityList.peers;\n const hmsPeers: HMSConnectionQuality[] = peers.map(peer => {\n return {\n peerID: peer.peer_id,\n downlinkQuality: peer.downlink_score,\n };\n });\n this.listener?.onConnectionQualityUpdate(hmsPeers);\n }\n}\n", "import { VideoTrackLayerUpdate } from '../../connection/channel-messages';\nimport { EventBus } from '../../events/EventBus';\nimport { HMSPeer, HMSRemotePeer, HMSTrackUpdate, HMSUpdateListener } from '../../interfaces';\nimport { HMSRemoteAudioTrack, HMSRemoteTrack, HMSRemoteVideoTrack, HMSTrackType } from '../../media/tracks';\nimport { IStore } from '../../sdk/store';\nimport HMSLogger from '../../utils/logger';\nimport { OnTrackLayerUpdateNotification, TrackState, TrackStateNotification } from '../HMSNotifications';\n\n/**\n * Handles:\n * - Incoming track meta-data from BIZ(signal) to match a track to a peer.\n * - Incoming MediaStreamTracks(wrapped in HMSTracks) from RTCMediaChannel.\n * - Mute/unmute track meta-data updates from BIZ.\n *\n * Since track meta-data and RTC tracks come in asynchronously,\n * we store the track meta-data(TrackState) in SDK Store and tracks temporarily here in tracksToProcess.\n *\n * Once we have both TrackState and track,\n * we add it to peer, send listener.onTrackUpdate and remove it from tracksToProcess.\n *\n * Gotchas:\n * - TRACK_UPDATE comes before TRACK_ADD -> update state, process pending tracks when TRACK_ADD arrives.\n */\nexport class TrackManager {\n public TAG = '[TrackManager]';\n private tracksToProcess: Map<string, HMSRemoteTrack> = new Map();\n\n constructor(public store: IStore, public eventBus: EventBus, public listener?: HMSUpdateListener) {}\n\n /**\n * Add event from biz on track-add\n * @param params TrackStateNotification\n */\n handleTrackMetadataAdd(params: TrackStateNotification) {\n HMSLogger.d(this.TAG, `TRACK_METADATA_ADD`, JSON.stringify(params, null, 2));\n\n for (const trackId in params.tracks) {\n const trackInfo = params.tracks[trackId];\n this.store.setTrackState({\n peerId: params.peer.peer_id,\n trackInfo,\n });\n }\n this.processPendingTracks();\n }\n\n /**\n * Sets the tracks to peer and returns the peer\n */\n handleTrackAdd = (track: HMSRemoteTrack) => {\n HMSLogger.d(this.TAG, `ONTRACKADD`, `${track}`);\n this.tracksToProcess.set(track.trackId, track);\n this.processPendingTracks();\n };\n\n handleTrackRemovedPermanently = (notification: TrackStateNotification) => {\n HMSLogger.d(this.TAG, `ONTRACKREMOVE`, notification);\n const trackIds = Object.keys(notification.tracks);\n\n trackIds.forEach(trackId => {\n const trackStateEntry = this.store.getTrackState(trackId);\n\n if (!trackStateEntry) {\n return;\n }\n\n const track = this.store.getTrackById(trackId);\n if (!track) {\n HMSLogger.d(this.TAG, 'Track not found in store');\n return;\n }\n\n // emit this event here as peer will already be removed(if left the room) by the time this event is received\n track.type === HMSTrackType.AUDIO && this.eventBus.audioTrackRemoved.publish(track as HMSRemoteAudioTrack);\n this.store.removeTrack(track);\n const hmsPeer = this.store.getPeerById(trackStateEntry.peerId);\n if (!hmsPeer) {\n return;\n }\n this.removePeerTracks(hmsPeer, track as HMSRemoteTrack);\n this.listener?.onTrackUpdate(HMSTrackUpdate.TRACK_REMOVED, track, hmsPeer);\n });\n };\n\n /**\n * Sets the track of corresponding peer to null and returns the peer\n */\n handleTrackRemove(track: HMSRemoteTrack) {\n HMSLogger.d(this.TAG, `ONTRACKREMOVE`, `${track}`);\n\n const trackStateEntry = this.store.getTrackState(track.trackId);\n\n if (!trackStateEntry) {\n return;\n }\n\n const storeHasTrack = this.store.hasTrack(track);\n if (!storeHasTrack) {\n HMSLogger.d(this.TAG, 'Track not found in store');\n return;\n }\n\n // emit this event here as peer will already be removed(if left the room) by the time this event is received\n track.type === HMSTrackType.AUDIO && this.eventBus.audioTrackRemoved.publish(track as HMSRemoteAudioTrack);\n }\n\n handleTrackLayerUpdate = (params: OnTrackLayerUpdateNotification) => {\n for (const trackId in params.tracks) {\n const trackEntry = params.tracks[trackId];\n const track = this.store.getTrackById(trackId);\n if (!track) {\n continue;\n }\n\n const peer = this.store.getPeerByTrackId(trackId)!;\n if (!peer) {\n continue;\n }\n\n if (track instanceof HMSRemoteVideoTrack) {\n this.setLayer(track, trackEntry);\n }\n }\n };\n\n handleTrackUpdate = (params: TrackStateNotification, callListener = true) => {\n const hmsPeer = this.store.getPeerById(params.peer.peer_id);\n if (!hmsPeer) {\n HMSLogger.d(this.TAG, 'Track Update ignored - Peer not added to store');\n return;\n }\n\n for (const trackId in params.tracks) {\n const currentTrackStateInfo = Object.assign({}, this.store.getTrackState(trackId)?.trackInfo);\n\n const trackEntry = params.tracks[trackId];\n const track = this.store.getTrackById(trackId);\n\n this.store.setTrackState({\n peerId: params.peer.peer_id,\n trackInfo: { ...currentTrackStateInfo, ...trackEntry },\n });\n\n // TRACK_UPDATE came before TRACK_ADD -> update state, process pending tracks when TRACK_ADD arrives.\n if (!track || this.tracksToProcess.has(trackId)) {\n this.processTrackInfo(trackEntry, params.peer.peer_id, callListener);\n this.processPendingTracks();\n } else {\n track.setEnabled(!trackEntry.mute);\n const eventType = this.processTrackUpdate(track as HMSRemoteTrack, currentTrackStateInfo, trackEntry);\n if (eventType) {\n this.listener?.onTrackUpdate(eventType, track, hmsPeer);\n }\n }\n }\n };\n\n processTrackInfo = (_trackInfo: TrackState, _peerId: string, _callListener?: boolean) => {};\n\n processPendingTracks = () => {\n const tracksCopy = new Map(this.tracksToProcess);\n tracksCopy.forEach(track => {\n const state = this.store.getTrackState(track.trackId);\n if (!state) {\n HMSLogger.d(this.TAG, 'TrackState not added to store', `peerId - ${track.peerId}`, `trackId -${track.trackId}`);\n return;\n }\n\n const hmsPeer = this.store.getPeerById(state.peerId);\n if (!hmsPeer) {\n HMSLogger.d(this.TAG, 'Peer not added to store, peerId', state.peerId);\n return;\n }\n\n track.source = state.trackInfo.source;\n track.peerId = hmsPeer.peerId;\n // set log identifier to initial name of the peer\n track.logIdentifier = hmsPeer.name;\n track.setEnabled(!state.trackInfo.mute);\n this.addAudioTrack(hmsPeer, track);\n this.addVideoTrack(hmsPeer, track);\n /**\n * Don't call onTrackUpdate for audio elements immediately because the operations(eg: setVolume) performed\n * on onTrackUpdate can be overriden in AudioSinkManager when audio element is created\n **/\n track.type === HMSTrackType.AUDIO\n ? this.eventBus.audioTrackAdded.publish({ track: track as HMSRemoteAudioTrack, peer: hmsPeer as HMSRemotePeer })\n : this.listener?.onTrackUpdate(HMSTrackUpdate.TRACK_ADDED, track, hmsPeer);\n this.tracksToProcess.delete(track.trackId);\n });\n };\n\n private setLayer(track: HMSRemoteVideoTrack, layerUpdate: VideoTrackLayerUpdate) {\n const peer = this.store.getPeerByTrackId(track.trackId);\n if (!peer) {\n return;\n }\n const isDegraded = track.setLayerFromServer(layerUpdate);\n if (isDegraded) {\n this.listener?.onTrackUpdate(HMSTrackUpdate.TRACK_DEGRADED, track, peer);\n } else {\n this.listener?.onTrackUpdate(HMSTrackUpdate.TRACK_RESTORED, track, peer);\n }\n }\n\n removePeerTracks(hmsPeer: HMSPeer, track: HMSRemoteTrack) {\n const auxiliaryTrackIndex = hmsPeer.auxiliaryTracks.indexOf(track);\n if (auxiliaryTrackIndex > -1) {\n hmsPeer.auxiliaryTracks.splice(auxiliaryTrackIndex, 1);\n HMSLogger.d(this.TAG, 'auxiliary track removed', `${track}`);\n } else {\n if (track.type === HMSTrackType.AUDIO && hmsPeer.audioTrack === track) {\n hmsPeer.audioTrack = undefined;\n HMSLogger.d(this.TAG, 'audio track removed', `${track}`);\n } else if (track.type === HMSTrackType.VIDEO && hmsPeer.videoTrack === track) {\n hmsPeer.videoTrack = undefined;\n HMSLogger.d(this.TAG, 'video track removed', `${track}`);\n }\n }\n }\n\n private addAudioTrack(hmsPeer: HMSPeer, track: HMSRemoteTrack) {\n if (track.type !== HMSTrackType.AUDIO) {\n return;\n }\n if (track.source === 'regular' && (!hmsPeer.audioTrack || hmsPeer.audioTrack?.trackId === track.trackId)) {\n hmsPeer.audioTrack = track as HMSRemoteAudioTrack;\n } else {\n hmsPeer.auxiliaryTracks.push(track);\n }\n this.store.addTrack(track);\n HMSLogger.d(this.TAG, 'audio track added', `${track}`);\n }\n\n addVideoTrack(hmsPeer: HMSPeer, track: HMSRemoteTrack) {\n if (track.type !== HMSTrackType.VIDEO) {\n return;\n }\n const remoteTrack = track as HMSRemoteVideoTrack;\n const simulcastDefinitions = this.store.getSimulcastDefinitionsForPeer(hmsPeer, remoteTrack.source!);\n remoteTrack.setSimulcastDefinitons(simulcastDefinitions);\n if (this.addAsPrimaryVideoTrack(hmsPeer, remoteTrack)) {\n if (!hmsPeer.videoTrack) {\n hmsPeer.videoTrack = remoteTrack;\n } else {\n (hmsPeer.videoTrack as HMSRemoteVideoTrack).replaceTrack(remoteTrack);\n }\n this.store.addTrack(hmsPeer.videoTrack);\n } else {\n const index = hmsPeer.auxiliaryTracks.findIndex(track => track.trackId === remoteTrack.trackId);\n if (index === -1) {\n hmsPeer.auxiliaryTracks.push(remoteTrack);\n this.store.addTrack(remoteTrack);\n } else {\n (hmsPeer.auxiliaryTracks[index] as HMSRemoteVideoTrack).replaceTrack(remoteTrack);\n this.store.addTrack(hmsPeer.auxiliaryTracks[index]);\n }\n }\n HMSLogger.d(this.TAG, 'video track added', `${track}`);\n }\n\n addAsPrimaryVideoTrack(hmsPeer: HMSPeer, track: HMSRemoteTrack) {\n return track.source === 'regular' && (!hmsPeer.videoTrack || hmsPeer.videoTrack?.trackId === track.trackId);\n }\n\n private processTrackUpdate(track: HMSRemoteTrack, currentTrackState: TrackState, trackState: TrackState) {\n let eventType;\n if (currentTrackState.mute !== trackState.mute) {\n eventType = trackState.mute ? HMSTrackUpdate.TRACK_MUTED : HMSTrackUpdate.TRACK_UNMUTED;\n track.type === HMSTrackType.AUDIO &&\n this.eventBus.audioTrackUpdate.publish({ track: track as HMSRemoteAudioTrack, enabled: !trackState.mute });\n } else if (currentTrackState.description !== trackState.description) {\n eventType = HMSTrackUpdate.TRACK_DESCRIPTION_CHANGED;\n }\n return eventType;\n }\n}\n", "import { TrackManager } from './TrackManager';\nimport { EventBus } from '../../events/EventBus';\nimport { HMSPeer, HMSTrackUpdate, HMSUpdateListener } from '../../interfaces';\nimport { HMSRemoteStream } from '../../media/streams/HMSRemoteStream';\nimport { HMSRemoteTrack, HMSRemoteVideoTrack } from '../../media/tracks';\nimport { LocalTrackManager } from '../../sdk/LocalTrackManager';\nimport { IStore } from '../../sdk/store';\nimport HMSTransport from '../../transport';\nimport HMSLogger from '../../utils/logger';\nimport { isEmptyTrack } from '../../utils/track';\nimport { TrackState, TrackStateNotification } from '../HMSNotifications';\n\nexport class OnDemandTrackManager extends TrackManager {\n TAG = '[OnDemandTrackManager]';\n\n constructor(store: IStore, eventBus: EventBus, private transport: HMSTransport, listener?: HMSUpdateListener) {\n super(store, eventBus, listener);\n }\n\n handleTrackMetadataAdd(params: TrackStateNotification) {\n super.handleTrackMetadataAdd(params);\n for (const trackId in params.tracks) {\n if (params.tracks[trackId].type === 'video') {\n this.processTrackInfo(params.tracks[trackId], params.peer.peer_id);\n }\n }\n }\n\n handleTrackRemove(track: HMSRemoteTrack) {\n super.handleTrackRemove(track);\n if (track.type === 'video' && track.source === 'regular') {\n this.processTrackInfo(\n {\n track_id: track.trackId,\n mute: !track.enabled,\n type: track.type,\n source: track.source,\n stream_id: track.stream.id,\n } as TrackState,\n track.peerId!,\n false,\n );\n }\n }\n\n /**\n * Add a blank track for the track received from biz so that the UI can render and show video element\n * which will trigger the prefer-video-track-state request which results in the actual track being\n * received from the sfu.\n * This will also be called when track is removed, as it can be removed when none is sent to sfu to\n * reduce the overall offer size\n * @param {TrackState} trackInfo\n * @param {string} peerId\n * @param {boolean} callListener\n * @returns\n */\n processTrackInfo = (trackInfo: TrackState, peerId: string, callListener = true) => {\n if (trackInfo.type !== 'video') {\n return;\n }\n const hmsPeer = this.store.getPeerById(peerId);\n if (!hmsPeer || !this.isPeerRoleSubscribed(peerId)) {\n HMSLogger.d(this.TAG, `no peer in store for peerId: ${peerId}`);\n return;\n }\n const remoteStream = new HMSRemoteStream(new MediaStream(), this.transport.getSubscribeConnection()!);\n const emptyTrack = LocalTrackManager.getEmptyVideoTrack();\n emptyTrack.enabled = !trackInfo.mute;\n const track = new HMSRemoteVideoTrack(remoteStream, emptyTrack, trackInfo.source);\n track.setTrackId(trackInfo.track_id);\n track.peerId = hmsPeer.peerId;\n track.logIdentifier = hmsPeer.name;\n this.addVideoTrack(hmsPeer, track);\n if (callListener) {\n this.listener?.onTrackUpdate(HMSTrackUpdate.TRACK_ADDED, hmsPeer.videoTrack!, hmsPeer);\n }\n };\n\n addAsPrimaryVideoTrack(hmsPeer: HMSPeer, track: HMSRemoteTrack) {\n if (track.source !== 'regular') {\n return false;\n }\n if (!hmsPeer.videoTrack) {\n return true;\n }\n if (hmsPeer.videoTrack.trackId === track.trackId) {\n return true;\n }\n return hmsPeer.videoTrack.enabled && isEmptyTrack(hmsPeer.videoTrack.nativeTrack);\n }\n\n private isPeerRoleSubscribed(peerId?: string) {\n if (!peerId) {\n return true;\n }\n const localPeer = this.store.getLocalPeer();\n const peer = this.store.getPeerById(peerId);\n return peer && localPeer?.role?.subscribeParams?.subscribeToRoles?.includes(peer.role?.name!);\n }\n}\n", "import { PeerManager } from './PeerManager';\nimport { TrackManager } from './TrackManager';\nimport { HMSUpdateListener } from '../..';\nimport { HMSTrackUpdate } from '../../interfaces';\nimport { HMSPeer } from '../../sdk/models/peer';\nimport { IStore } from '../../sdk/store';\nimport HMSLogger from '../../utils/logger';\nimport { HMSNotificationMethod } from '../HMSNotificationMethod';\nimport { PeerListNotification, PeerNotification, PeriodicRoomState } from '../HMSNotifications';\n\n/**\n * Handles:\n * - Initial Peer List - get peer and track meta-data for peers who are already in the room when you join\n * - Reconnect Peer List - Handle peer and track changes in the room missed out due to reconnection\n *\n * Cases to handle in reconnect peer list:\n * - Add additional peers as peer join\n * - Remove missing peers as peer leave\n * - For existing peers:\n * - Add new tracks as track add\n * - Remove missing tracks as track remove\n * - Track state change(enabled) as track update\n */\nexport class PeerListManager {\n private readonly TAG = '[PeerListManager]';\n constructor(\n private store: IStore,\n private peerManager: PeerManager,\n private trackManager: TrackManager,\n public listener?: HMSUpdateListener,\n ) {}\n\n handleNotification(method: string, notification: any, isReconnecting: boolean) {\n if (method === HMSNotificationMethod.PEER_LIST) {\n const peerList = notification as PeerListNotification;\n if (isReconnecting) {\n HMSLogger.d(this.TAG, `RECONNECT_PEER_LIST event`, JSON.stringify(peerList, null, 2));\n this.handleReconnectPeerList(peerList);\n } else {\n // TODO: Don't call initial peerlist if atleast 1room state had happen\n HMSLogger.d(this.TAG, `PEER_LIST event`, JSON.stringify(peerList, null, 2));\n this.handleInitialPeerList(peerList);\n }\n } else if (method === HMSNotificationMethod.ROOM_STATE) {\n const roomState = notification as PeriodicRoomState;\n this.handlePreviewRoomState(roomState);\n }\n }\n\n private handleInitialPeerList = (peerList: PeerListNotification) => {\n const peers = Object.values(peerList.peers);\n this.peerManager.handlePeerList(peers);\n };\n\n private handleReconnectPeerList = (peerList: PeerListNotification) => {\n this.handleRepeatedPeerList(peerList.peers);\n };\n\n private handlePreviewRoomState = (roomState: PeriodicRoomState) => {\n if (!this.store.hasRoleDetailsArrived()) {\n // we can't process the peers yet we don't know enough about them(role info)\n return;\n }\n const roomPeers = roomState.peers;\n if (roomPeers === null || roomPeers === undefined) {\n // in this case, room state doesn't say anything about the peers,\n // there can be optimisations in place to not send this field when it's unchanged from previously sent value.\n // If there are no peers either roomState.peers will be empty object\n // or peer_count will be 0(handled below)\n if (roomState.peer_count === 0) {\n this.handleRepeatedPeerList({});\n }\n return;\n }\n // we don't get tracks inside the peer object in room state, we're adding\n // an empty value here so rest of the code flow can ignore this change, the below\n // can be changed when tracks will be sent as a separate object in future\n Object.keys(roomPeers).forEach(peer => {\n roomPeers[peer].tracks = {};\n roomPeers[peer].is_from_room_state = true;\n });\n this.handleRepeatedPeerList(roomPeers);\n };\n\n private handleRepeatedPeerList = (peersMap: Record<string, PeerNotification>) => {\n const currentPeerList = this.store.getRemotePeers();\n const peers = Object.values(peersMap);\n const peersToRemove = currentPeerList.filter(hmsPeer => !peersMap[hmsPeer.peerId]);\n if (peersToRemove.length > 0) {\n HMSLogger.d(this.TAG, `${peersToRemove}`);\n }\n\n // Send peer-leave updates to all the missing peers\n peersToRemove.forEach(peer => {\n const peerNotification: PeerNotification = {\n peer_id: peer.peerId,\n role: peer.role?.name || '',\n info: {\n name: peer.name,\n data: peer.metadata || '',\n user_id: peer.customerUserId || '',\n },\n tracks: {},\n };\n\n this.peerManager.handlePeerLeave(peerNotification);\n });\n\n const peerList: PeerNotification[] = [];\n // Check for any tracks which are added/removed\n peers.forEach(newPeerNotification => {\n const oldPeer = this.store.getPeerById(newPeerNotification.peer_id);\n const newPeerTrackStates = Object.values(newPeerNotification.tracks);\n\n if (oldPeer) {\n // Peer already present in room, we take diff between the tracks\n const tracks = this.store.getPeerTracks(oldPeer.peerId);\n\n // Remove all the tracks which are not present in the peer.tracks\n tracks.forEach(track => {\n if (!newPeerNotification.tracks[track.trackId]) {\n this.removePeerTrack(oldPeer, track.trackId);\n this.listener?.onTrackUpdate(HMSTrackUpdate.TRACK_REMOVED, track, oldPeer);\n }\n });\n\n // Add track-metadata for all the new tracks\n newPeerTrackStates.forEach(trackData => {\n if (!this.store.getTrackById(trackData.track_id)) {\n // NOTE: We assume that, once the connection is re-established,\n // transport layer will send a native onTrackAdd\n this.store.setTrackState({\n peerId: oldPeer.peerId,\n trackInfo: trackData,\n });\n }\n });\n\n // Handle RTC track add and track state change.\n this.trackManager.handleTrackUpdate(\n {\n peer: { info: newPeerNotification.info, peer_id: newPeerNotification.peer_id },\n tracks: newPeerNotification.tracks,\n },\n false,\n );\n\n // Update peer's role locally, new role is received from the reconnect peer-list\n this.peerManager.handlePeerUpdate(newPeerNotification);\n peerList.push(newPeerNotification);\n } else {\n // New peer joined while reconnecting\n peerList.push(newPeerNotification);\n }\n });\n if (peerList.length > 0) {\n this.peerManager.handlePeerList(peerList);\n }\n };\n\n private removePeerTrack(peer: HMSPeer, trackId: string) {\n HMSLogger.d(this.TAG, `removing track - ${trackId} from ${peer}`);\n if (peer.audioTrack?.trackId === trackId) {\n peer.audioTrack = undefined;\n } else if (peer.videoTrack?.trackId === trackId) {\n peer.videoTrack = undefined;\n } else {\n const trackIndex = peer.auxiliaryTracks.findIndex(track => track.trackId === trackId);\n trackIndex >= 0 && peer.auxiliaryTracks.splice(trackIndex, 1);\n }\n }\n}\n", "export const convertDateNumToDate = (dateNum?: number): Date | undefined => {\n return dateNum ? new Date(dateNum) : undefined;\n};\n", "import { TrackManager } from './TrackManager';\nimport { HMSPeer, HMSPeerUpdate, HMSTrackUpdate, HMSUpdateListener } from '../../interfaces';\nimport { HMSRemoteVideoTrack } from '../../media/tracks';\nimport { HMSRemotePeer } from '../../sdk/models/peer';\nimport { IStore } from '../../sdk/store';\nimport { convertDateNumToDate } from '../../utils/date';\nimport HMSLogger from '../../utils/logger';\nimport { HMSNotificationMethod } from '../HMSNotificationMethod';\nimport { PeerNotification } from '../HMSNotifications';\n\n/**\n * Handles:\n * - New peer join\n * - Peer Leave\n * - Role update for peer\n *\n * Notes:\n * - Peer join comes with track meta-data,\n * we add it to the store and call TrackManager to process it when RTC Track comes in.\n */\nexport class PeerManager {\n private readonly TAG = '[PeerManager]';\n constructor(private store: IStore, private trackManager: TrackManager, public listener?: HMSUpdateListener) {}\n\n handleNotification(method: string, notification: any) {\n switch (method) {\n case HMSNotificationMethod.PEER_JOIN: {\n const peer = notification as PeerNotification;\n this.handlePeerJoin(peer);\n break;\n }\n\n case HMSNotificationMethod.PEER_LEAVE: {\n const peer = notification as PeerNotification;\n this.handlePeerLeave(peer);\n break;\n }\n case HMSNotificationMethod.PEER_UPDATE:\n this.handlePeerUpdate(notification as PeerNotification);\n break;\n default:\n break;\n }\n }\n\n handlePeerList = (peers: PeerNotification[]) => {\n if (peers.length === 0) {\n this.listener?.onPeerUpdate(HMSPeerUpdate.PEER_LIST, []);\n return;\n }\n const hmsPeers: HMSRemotePeer[] = [];\n const newPeers = new Set(peers.map(peer => peer.peer_id));\n this.store.getRemotePeers().forEach(({ peerId, fromRoomState }) => {\n /**\n * Remove only if the peer join happened from preview roomstate update. This will prevent the peer joined\n * from peer-join event post join from being removed from the store.\n */\n if (!newPeers.has(peerId) && fromRoomState) {\n this.store.removePeer(peerId);\n }\n });\n for (const peer of peers) {\n hmsPeers.push(this.makePeer(peer));\n }\n this.listener?.onPeerUpdate(HMSPeerUpdate.PEER_LIST, hmsPeers);\n this.trackManager.processPendingTracks();\n };\n\n handlePeerJoin = (peer: PeerNotification) => {\n const hmsPeer = this.makePeer(peer);\n\n this.listener?.onPeerUpdate(HMSPeerUpdate.PEER_JOINED, hmsPeer);\n this.trackManager.processPendingTracks();\n };\n\n handlePeerLeave = (peer: PeerNotification) => {\n const hmsPeer = this.store.getPeerById(peer.peer_id);\n this.store.removePeer(peer.peer_id);\n HMSLogger.d(this.TAG, `PEER_LEAVE`, peer.peer_id, `remainingPeers=${this.store.getPeers().length}`);\n\n if (!hmsPeer) {\n return;\n }\n\n if (hmsPeer.audioTrack) {\n this.listener?.onTrackUpdate(HMSTrackUpdate.TRACK_REMOVED, hmsPeer.audioTrack, hmsPeer);\n }\n\n if (hmsPeer.videoTrack) {\n this.listener?.onTrackUpdate(HMSTrackUpdate.TRACK_REMOVED, hmsPeer.videoTrack, hmsPeer);\n }\n\n hmsPeer.auxiliaryTracks?.forEach(track => {\n this.listener?.onTrackUpdate(HMSTrackUpdate.TRACK_REMOVED, track, hmsPeer);\n });\n\n this.listener?.onPeerUpdate(HMSPeerUpdate.PEER_LEFT, hmsPeer);\n };\n\n handlePeerUpdate(notification: PeerNotification) {\n const peer = this.store.getPeerById(notification.peer_id);\n\n if (!peer) {\n return;\n }\n\n if (peer.role && peer.role.name !== notification.role) {\n const newRole = this.store.getPolicyForRole(notification.role);\n peer.updateRole(newRole);\n this.updateSimulcastLayersForPeer(peer);\n this.listener?.onPeerUpdate(HMSPeerUpdate.ROLE_UPDATED, peer);\n }\n this.handlePeerInfoUpdate({ peer, ...notification.info });\n }\n\n handlePeerInfoUpdate({ peer, name, data }: { peer?: HMSPeer; name?: string; data?: string }) {\n if (!peer) {\n return;\n }\n if (name && peer.name !== name) {\n peer.updateName(name);\n this.listener?.onPeerUpdate(HMSPeerUpdate.NAME_UPDATED, peer);\n }\n if (data && peer.metadata !== data) {\n peer.updateMetadata(data);\n this.listener?.onPeerUpdate(HMSPeerUpdate.METADATA_UPDATED, peer);\n }\n }\n\n private makePeer(peer: PeerNotification) {\n let hmsPeer = this.store.getPeerById(peer.peer_id) as HMSRemotePeer;\n if (!hmsPeer) {\n hmsPeer = new HMSRemotePeer({\n peerId: peer.peer_id,\n name: peer.info.name,\n customerUserId: peer.info.user_id,\n metadata: peer.info.data,\n role: this.store.getPolicyForRole(peer.role),\n joinedAt: convertDateNumToDate(peer.joined_at),\n fromRoomState: !!peer.is_from_room_state,\n });\n this.store.addPeer(hmsPeer);\n HMSLogger.d(this.TAG, `adding to the peerList`, `${hmsPeer}`);\n }\n\n for (const trackId in peer.tracks) {\n const trackInfo = peer.tracks[trackId];\n this.store.setTrackState({\n peerId: peer.peer_id,\n trackInfo,\n });\n if (trackInfo.type === 'video') {\n this.trackManager.processTrackInfo(trackInfo, peer.peer_id, false);\n }\n }\n return hmsPeer;\n }\n\n private updateSimulcastLayersForPeer(peer: HMSPeer) {\n this.store.getPeerTracks(peer.peerId).forEach(track => {\n if (track.type === 'video' && ['regular', 'screen'].includes(track.source!)) {\n const remoteTrack = track as HMSRemoteVideoTrack;\n const simulcastDefinitions = this.store.getSimulcastDefinitionsForPeer(peer, remoteTrack.source!);\n remoteTrack.setSimulcastDefinitons(simulcastDefinitions);\n }\n });\n }\n}\n", "import { EventBus } from '../../events/EventBus';\nimport { IStore } from '../../sdk/store';\nimport HMSLogger from '../../utils/logger';\nimport { PolicyParams } from '../HMSNotifications';\n\n/**\n * Handles:\n * - Set policy with publishParams, simulcast layers to store\n * - Emit 'local-peer-role-update' to trigger RoleChangeManager to publish/unpublish local tracks\n * - Emit 'policy-change' to finish preview before calling listener.onPreview\n */\nexport class PolicyChangeManager {\n constructor(private store: IStore, private eventBus: EventBus) {}\n\n handlePolicyChange(params: PolicyParams) {\n const localPeer = this.store.getLocalPeer();\n\n if (localPeer && !localPeer.role) {\n const newRole = params.known_roles[params.name];\n localPeer.updateRole(newRole);\n }\n\n this.store.setKnownRoles(params);\n const room = this.store.getRoom();\n if (room) {\n room.templateId = params.template_id;\n } else {\n HMSLogger.w('[PolicyChangeManager]', 'on policy change - room not present');\n }\n // handle when role is not present in known_roles\n // const publishParams = params.known_roles[params.name]?.publishParams;\n // this.store.setPublishParams(publishParams);\n\n if (localPeer?.role && localPeer.role.name !== params.name) {\n const newRole = this.store.getPolicyForRole(params.name);\n const oldRole = localPeer.role;\n localPeer.updateRole(newRole);\n this.eventBus.localRoleUpdate.publish({ oldRole, newRole });\n }\n this.eventBus.policyChange.publish(params);\n }\n}\n", "import { HMSPoll, HMSPollQuestionResponse, HMSPollsUpdate, HMSUpdateListener } from '../../interfaces';\nimport { IStore } from '../../sdk/store';\nimport { PollResult } from '../../signal/interfaces';\nimport HMSTransport from '../../transport';\nimport { convertDateNumToDate } from '../../utils/date';\nimport { HMSNotificationMethod } from '../HMSNotificationMethod';\nimport { PollStartNotification, PollStatsNotification, PollStopNotification } from '../HMSNotifications';\n\nexport class PollsManager {\n constructor(private store: IStore, private transport: HMSTransport, public listener?: HMSUpdateListener) {}\n\n handleNotification(method: string, notification: any) {\n switch (method) {\n case HMSNotificationMethod.POLL_START: {\n this.handlePollStart(notification as PollStartNotification);\n break;\n }\n\n case HMSNotificationMethod.POLL_STOP: {\n this.handlePollStop(notification as PollStopNotification);\n break;\n }\n case HMSNotificationMethod.POLL_STATS:\n this.handlePollStats(notification as PollStatsNotification);\n\n break;\n default:\n break;\n }\n }\n\n private async handlePollStart(notification: PollStartNotification) {\n const polls: HMSPoll[] = [];\n for (const pollParams of notification.polls) {\n if (this.store.getPoll(pollParams.poll_id)) {\n return;\n }\n\n const questions = await this.transport.getPollQuestions({ poll_id: pollParams.poll_id, index: 0, count: 50 });\n const poll: HMSPoll = {\n id: pollParams.poll_id,\n title: pollParams.title,\n startedBy: pollParams.started_by,\n createdBy: pollParams.created_by,\n anonymous: pollParams.anonymous,\n type: pollParams.type,\n duration: pollParams.duration,\n locked: pollParams.locked, // poll is locked automatically when it starts\n mode: pollParams.mode as HMSPoll['mode'],\n visibility: pollParams.visibility,\n rolesThatCanVote: pollParams.vote || [],\n rolesThatCanViewResponses: pollParams.responses || [],\n state: pollParams.state,\n stoppedBy: pollParams.stopped_by,\n startedAt: convertDateNumToDate(pollParams.started_at),\n stoppedAt: convertDateNumToDate(pollParams.stopped_at),\n createdAt: convertDateNumToDate(pollParams.created_at),\n\n questions: questions.questions.map(({ question, options, answer }) => ({ ...question, options, answer })),\n };\n\n polls.push(poll);\n this.store.setPoll(poll);\n }\n this.listener?.onPollsUpdate(HMSPollsUpdate.POLL_STARTED, polls);\n }\n\n private async handlePollStop(notification: PollStopNotification) {\n const stoppedPolls: HMSPoll[] = [];\n\n for (const poll of notification.polls) {\n const savedPoll = this.store.getPoll(poll.poll_id);\n if (savedPoll) {\n savedPoll.state = 'stopped';\n savedPoll.stoppedAt = convertDateNumToDate(poll.stopped_at);\n savedPoll.stoppedBy = poll.stopped_by;\n\n const pollResult = await this.transport.getPollResult({ poll_id: poll.poll_id });\n this.updatePollResult(savedPoll, pollResult);\n stoppedPolls.push(savedPoll);\n }\n }\n\n if (stoppedPolls.length > 0) {\n this.listener?.onPollsUpdate(HMSPollsUpdate.POLL_STOPPED, stoppedPolls);\n }\n }\n\n private async handlePollStats(notification: PollStatsNotification) {\n const updatedPolls: HMSPoll[] = [];\n for (const updatedPoll of notification.polls) {\n const savedPoll = this.store.getPoll(updatedPoll.poll_id);\n if (!savedPoll) {\n return;\n }\n\n this.updatePollResult(savedPoll, updatedPoll);\n\n const serverResponseParams = await this.transport.getPollResponses({\n poll_id: updatedPoll.poll_id,\n index: 0,\n count: 50,\n self: false,\n });\n\n serverResponseParams.responses?.forEach(({ response, peer, final }) => {\n const question = savedPoll?.questions?.find(question => question.index === response.question);\n if (!question) {\n return;\n }\n const pollResponse: HMSPollQuestionResponse = {\n id: response.response_id,\n questionIndex: response.question,\n option: response.option,\n options: response.options,\n text: response.text,\n responseFinal: final,\n peer: { peerid: peer.peerid, userHash: peer.hash, userid: peer.userid, username: peer.username },\n skipped: response.skipped,\n type: response.type,\n update: response.update,\n };\n\n if (Array.isArray(question.responses) && question.responses.length > 0) {\n if (!question.responses.find(({ id }) => id === pollResponse.id)) {\n question.responses.push(pollResponse);\n }\n } else {\n question.responses = [pollResponse];\n }\n });\n\n updatedPolls.push(savedPoll);\n }\n\n if (updatedPolls.length > 0) {\n this.listener?.onPollsUpdate(HMSPollsUpdate.POLL_STATS_UPDATED, updatedPolls);\n }\n }\n\n private updatePollResult(savedPoll: HMSPoll, pollResult: PollResult) {\n savedPoll.result = { ...savedPoll.result };\n savedPoll.result.totalUsers = pollResult.user_count;\n savedPoll.result.maxUsers = pollResult.max_user;\n savedPoll.result.totalResponses = pollResult.total_response;\n\n pollResult.questions?.forEach(updatedQuestion => {\n const savedQuestion = savedPoll.questions?.find(question => question.index === updatedQuestion.question);\n if (!savedQuestion) {\n return;\n }\n savedQuestion.result = { ...savedQuestion.result };\n savedQuestion.result.correctResponses = updatedQuestion.correct;\n savedQuestion.result.skippedCount = updatedQuestion.skipped;\n savedQuestion.result.totalResponses = updatedQuestion.total;\n\n updatedQuestion.options?.forEach((updatedVoteCount, index) => {\n const savedOption = savedQuestion.options?.[index];\n if (savedOption && savedOption.voteCount !== updatedVoteCount) {\n savedOption.voteCount = updatedVoteCount;\n }\n });\n });\n }\n}\n", "import { HMSRemotePeer, HMSRoleChangeRequest, HMSUpdateListener } from '../../interfaces';\nimport { HMSLocalTrack, HMSTrackSource } from '../../media/tracks';\nimport { IStore } from '../../sdk/store';\nimport { HMSNotificationMethod } from '../HMSNotificationMethod';\nimport {\n ChangeTrackMuteStateNotification,\n RoleChangeRequestParams,\n TrackUpdateRequestNotification,\n} from '../HMSNotifications';\n\n/**\n * Handles request from remote peers to change something on the local side. For eg. role change, track mute/unmute.\n */\nexport class RequestManager {\n constructor(private store: IStore, public listener?: HMSUpdateListener) {}\n\n handleNotification(method: string, notification: any) {\n switch (method) {\n case HMSNotificationMethod.ROLE_CHANGE_REQUEST:\n this.handleRoleChangeRequest(notification as RoleChangeRequestParams);\n break;\n\n case HMSNotificationMethod.TRACK_UPDATE_REQUEST:\n this.handleTrackUpdateRequest(notification as TrackUpdateRequestNotification);\n break;\n\n case HMSNotificationMethod.CHANGE_TRACK_MUTE_STATE_UPDATE:\n this.handleChangeTrackStateRequest(notification as ChangeTrackMuteStateNotification);\n break;\n default:\n return;\n }\n }\n\n private handleRoleChangeRequest(notification: RoleChangeRequestParams) {\n const request: HMSRoleChangeRequest = {\n requestedBy: notification.requested_by\n ? (this.store.getPeerById(notification.requested_by) as HMSRemotePeer)\n : undefined,\n role: this.store.getPolicyForRole(notification.role),\n token: notification.token,\n };\n\n this.listener?.onRoleChangeRequest(request);\n }\n\n // eslint-disable-next-line complexity\n private handleTrackUpdateRequest(trackUpdateRequest: TrackUpdateRequestNotification) {\n const { requested_by, track_id, mute } = trackUpdateRequest;\n const peer = requested_by ? this.store.getPeerById(requested_by) : undefined;\n const track = this.store.getLocalPeerTracks().find(track => track.publishedTrackId === track_id);\n\n if (!track) {\n return;\n }\n\n const sendNotification = () => {\n this.listener?.onChangeTrackStateRequest({ requestedBy: peer as HMSRemotePeer, track, enabled: !mute });\n };\n\n if (mute) {\n // if track is already in the same state as change state, do nothing\n if (track.enabled === !mute) {\n return;\n }\n /**\n * Directly mute track when request arrives\n */\n track.setEnabled(!mute).then(sendNotification);\n } else {\n /**\n * Notify UI to unmute for requesting consent\n */\n sendNotification();\n }\n }\n\n private handleChangeTrackStateRequest(request: ChangeTrackMuteStateNotification) {\n const { type, source, value, requested_by } = request;\n const peer = requested_by ? this.store.getPeerById(requested_by) : undefined;\n\n // value true means the track has to be muted\n const enabled = !value;\n const tracksToBeUpdated = this.getTracksToBeUpdated({ type, source, enabled });\n //Do nothing if all tracks are already in same state as the request\n if (tracksToBeUpdated.length === 0) {\n return;\n }\n // if track is to be muted, mute and send the notification, otherwise send notification\n if (!enabled) {\n const promises: Promise<void>[] = [];\n\n for (const track of tracksToBeUpdated) {\n promises.push(track.setEnabled(false));\n }\n Promise.all(promises).then(() => {\n this.listener?.onChangeMultiTrackStateRequest({\n requestedBy: peer as HMSRemotePeer,\n tracks: tracksToBeUpdated,\n enabled: false,\n });\n });\n } else {\n this.listener?.onChangeMultiTrackStateRequest({\n requestedBy: peer as HMSRemotePeer,\n tracks: tracksToBeUpdated,\n type,\n source,\n enabled: true,\n });\n }\n }\n\n /**\n * Filter the local tracks based on type, source and enabled state\n * @returns {HMSLocalTrack[]}\n */\n private getTracksToBeUpdated({\n type,\n source,\n enabled,\n }: {\n type?: 'audio' | 'video';\n source?: HMSTrackSource;\n enabled: boolean;\n }) {\n const localPeerTracks = this.store.getLocalPeerTracks();\n let tracks: HMSLocalTrack[] = localPeerTracks;\n if (type) {\n tracks = tracks.filter(track => track.type === type);\n }\n if (source) {\n tracks = tracks.filter(track => track.source === source);\n }\n // filter out tracks which are already in the desired state\n return tracks.filter(track => track.enabled !== enabled);\n }\n}\n", "import { HMSAction } from '../../error/HMSAction';\nimport { HMSException } from '../../error/HMSException';\nimport { HMSHLS, HMSHLSRecording, HMSRoomUpdate, HMSUpdateListener } from '../../interfaces';\nimport { ServerError } from '../../interfaces/internal';\nimport { IStore } from '../../sdk/store';\nimport { convertDateNumToDate } from '../../utils/date';\nimport HMSLogger from '../../utils/logger';\nimport { HMSNotificationMethod } from '../HMSNotificationMethod';\nimport {\n HLSNotification,\n PeerListNotification,\n PeriodicRoomState,\n RecordingNotification,\n RoomState,\n RTMPNotification,\n} from '../HMSNotifications';\n\nexport class RoomUpdateManager {\n private readonly TAG = '[RoomUpdateManager]';\n\n constructor(private store: IStore, public listener?: HMSUpdateListener) {}\n\n // eslint-disable-next-line complexity\n handleNotification(method: HMSNotificationMethod, notification: any) {\n switch (method) {\n case HMSNotificationMethod.PEER_LIST:\n this.onRoomState((notification as PeerListNotification).room);\n break;\n case HMSNotificationMethod.RTMP_START:\n this.onRTMPStart(notification as RTMPNotification);\n break;\n case HMSNotificationMethod.RTMP_STOP:\n this.onRTMPStop(notification as RTMPNotification);\n break;\n case HMSNotificationMethod.RECORDING_START:\n this.onRecordingStart(notification as RecordingNotification);\n break;\n case HMSNotificationMethod.RECORDING_STOP:\n this.onRecordingStop(notification as RecordingNotification);\n break;\n case HMSNotificationMethod.ROOM_STATE:\n this.handlePreviewRoomState(notification as PeriodicRoomState);\n break;\n default:\n this.onHLS(method, notification as HLSNotification);\n break;\n }\n }\n\n private handlePreviewRoomState(notification: PeriodicRoomState) {\n const { room } = notification;\n this.onRoomState(room, notification.peer_count);\n }\n\n private onRoomState(roomNotification: RoomState, peerCount?: number) {\n const { recording, streaming, session_id, started_at, name } = roomNotification;\n const room = this.store.getRoom();\n if (!room) {\n HMSLogger.w(this.TAG, 'on room state - room not present');\n return;\n }\n\n room.peerCount = peerCount;\n room.name = name;\n room.recording.server.running = !!recording?.sfu.enabled;\n room.recording.browser.running = !!recording?.browser.enabled;\n room.rtmp.running = !!streaming?.rtmp?.enabled;\n room.rtmp.startedAt = convertDateNumToDate(streaming?.rtmp?.started_at);\n room.recording.server.startedAt = convertDateNumToDate(recording?.sfu.started_at);\n room.recording.browser.startedAt = convertDateNumToDate(recording?.browser.started_at);\n room.recording.hls = this.getPeerListHLSRecording(recording);\n room.hls = this.convertHls(streaming?.hls);\n room.sessionId = session_id;\n room.startedAt = convertDateNumToDate(started_at);\n this.listener?.onRoomUpdate(HMSRoomUpdate.RECORDING_STATE_UPDATED, room);\n }\n\n private onRTMPStart(notification: RTMPNotification) {\n this.setRTMPStatus(!notification.error?.code, notification);\n }\n\n private onRTMPStop(notification: RTMPNotification) {\n this.setRTMPStatus(false, notification);\n }\n\n private onRecordingStart(notification: RecordingNotification) {\n this.setRecordingStatus(!notification.error?.code, notification);\n }\n\n private onRecordingStop(notification: RecordingNotification) {\n this.setRecordingStatus(false, notification);\n }\n\n private onHLS(method: string, notification: HLSNotification) {\n if (![HMSNotificationMethod.HLS_START, HMSNotificationMethod.HLS_STOP].includes(method as HMSNotificationMethod)) {\n return;\n }\n const room = this.store.getRoom();\n if (!room) {\n HMSLogger.w(this.TAG, 'on hls - room not present');\n return;\n }\n\n notification.enabled = method === HMSNotificationMethod.HLS_START && !notification.error?.code;\n room.hls = this.convertHls(notification);\n room.recording.hls = this.getHLSRecording(notification);\n this.listener?.onRoomUpdate(HMSRoomUpdate.HLS_STREAMING_STATE_UPDATED, room);\n }\n\n private convertHls(hlsNotification?: HLSNotification) {\n const hls: HMSHLS = {\n running: !!hlsNotification?.enabled,\n variants: [],\n error: this.toSdkError(hlsNotification?.error),\n };\n hlsNotification?.variants?.forEach(variant => {\n hls.variants.push({\n meetingURL: variant.meeting_url,\n url: variant.url,\n metadata: variant.metadata,\n startedAt: convertDateNumToDate(variant.started_at),\n });\n });\n return hls;\n }\n\n private getHLSRecording(hlsNotification?: HLSNotification): HMSHLSRecording {\n let hlsRecording: HMSHLSRecording = { running: false };\n if (hlsNotification?.hls_recording) {\n hlsRecording = {\n running: !!hlsNotification?.enabled,\n singleFilePerLayer: !!hlsNotification.hls_recording?.single_file_per_layer,\n hlsVod: !!hlsNotification.hls_recording?.hls_vod,\n startedAt: convertDateNumToDate(hlsNotification?.variants?.[0].started_at),\n error: this.toSdkError(hlsNotification.error),\n };\n }\n return hlsRecording;\n }\n\n private getPeerListHLSRecording(recording?: RoomState['recording']): HMSHLSRecording {\n const hlsNotification = recording?.hls;\n return {\n running: !!hlsNotification?.enabled,\n startedAt: convertDateNumToDate(hlsNotification?.started_at),\n singleFilePerLayer: !!hlsNotification?.config?.single_file_per_layer,\n hlsVod: !!hlsNotification?.config?.hls_vod,\n };\n }\n\n private setRecordingStatus(running: boolean, notification: RecordingNotification) {\n const room = this.store.getRoom();\n if (!room) {\n HMSLogger.w(this.TAG, `set recording status running=${running} - room not present`);\n return;\n }\n\n let action: HMSRoomUpdate;\n if (notification.type === 'sfu') {\n room.recording.server = {\n running,\n startedAt: running ? convertDateNumToDate(notification.started_at) : undefined,\n error: this.toSdkError(notification.error),\n };\n action = HMSRoomUpdate.SERVER_RECORDING_STATE_UPDATED;\n } else {\n room.recording.browser = {\n running,\n startedAt: running ? convertDateNumToDate(notification.started_at) : undefined,\n error: this.toSdkError(notification.error),\n };\n action = HMSRoomUpdate.BROWSER_RECORDING_STATE_UPDATED;\n }\n this.listener?.onRoomUpdate(action, room);\n }\n\n private setRTMPStatus(running: boolean, notification: RTMPNotification) {\n const room = this.store.getRoom();\n if (!room) {\n HMSLogger.w(this.TAG, 'on policy change - room not present');\n return;\n }\n\n room.rtmp = {\n running,\n startedAt: running ? convertDateNumToDate(notification.started_at) : undefined,\n error: this.toSdkError(notification.error),\n };\n this.listener?.onRoomUpdate(HMSRoomUpdate.RTMP_STREAMING_STATE_UPDATED, room);\n }\n\n private toSdkError(error?: ServerError): HMSException | undefined {\n if (!error?.code) {\n return undefined;\n }\n const errMsg = error.message || 'error in streaming/recording';\n const sdkError = new HMSException(error.code, 'ServerErrors', HMSAction.NONE, errMsg, errMsg);\n HMSLogger.e(this.TAG, 'error in streaming/recording', sdkError);\n return sdkError;\n }\n}\n", "import { HMSUpdateListener, SessionStoreUpdate } from '../../interfaces';\nimport { IStore } from '../../sdk/store';\nimport { convertDateNumToDate } from '../../utils/date';\nimport { HMSNotificationMethod } from '../HMSNotificationMethod';\nimport { MetadataChangeNotification } from '../HMSNotifications';\n\nexport class SessionMetadataManager {\n constructor(private store: IStore, public listener?: HMSUpdateListener) {}\n\n handleNotification(method: string, notification: any) {\n if (method !== HMSNotificationMethod.METADATA_CHANGE) {\n return;\n }\n this.handleMetadataChange(notification);\n }\n\n private handleMetadataChange(notification: MetadataChangeNotification) {\n const updates: SessionStoreUpdate[] = notification.values.map(update => ({\n key: update.key,\n value: update.data,\n updatedAt: convertDateNumToDate(update.updated_at),\n updatedBy: update.updated_by ? this.store.getPeerById(update.updated_by) : undefined,\n }));\n this.listener?.onSessionStoreUpdate(updates);\n }\n}\n", "import { ActiveSpeakerManager } from './managers/ActiveSpeakerManager';\nimport { BroadcastManager } from './managers/BroadcastManager';\nimport { ConnectionQualityManager } from './managers/ConnectionQualityManager';\nimport { OnDemandTrackManager } from './managers/onDemandTrackManager';\nimport { PeerListManager } from './managers/PeerListManager';\nimport { PeerManager } from './managers/PeerManager';\nimport { PolicyChangeManager } from './managers/PolicyChangeManager';\nimport { PollsManager } from './managers/PollsManager';\nimport { RequestManager } from './managers/RequestManager';\nimport { RoomUpdateManager } from './managers/RoomUpdateManager';\nimport { SessionMetadataManager } from './managers/SessionMetadataManager';\nimport { TrackManager } from './managers/TrackManager';\nimport { HMSNotificationMethod } from './HMSNotificationMethod';\nimport {\n ConnectionQualityList,\n OnTrackLayerUpdateNotification,\n PolicyParams,\n SpeakerList,\n TrackStateNotification,\n} from './HMSNotifications';\nimport { EventBus } from '../events/EventBus';\nimport { HMSAudioListener, HMSConnectionQualityListener, HMSUpdateListener } from '../interfaces';\nimport { HMSRemoteTrack } from '../media/tracks';\nimport { IStore } from '../sdk/store';\nimport { InitFlags } from '../signal/init/models';\nimport HMSTransport from '../transport';\nimport HMSLogger from '../utils/logger';\n\nexport class NotificationManager {\n private readonly TAG = '[HMSNotificationManager]';\n private trackManager: TrackManager;\n private peerManager: PeerManager;\n private peerListManager: PeerListManager;\n private activeSpeakerManager: ActiveSpeakerManager;\n private connectionQualityManager: ConnectionQualityManager;\n private broadcastManager: BroadcastManager;\n private policyChangeManager: PolicyChangeManager;\n private requestManager: RequestManager;\n private roomUpdateManager: RoomUpdateManager;\n private sessionMetadataManager: SessionMetadataManager;\n private pollsManager: PollsManager;\n /**\n * room state can be sent before join in preview stage as well but that is outdated, based on\n * eventual consistency and doesn't have all data. If we get at least one consistent room update\n * from that point onwards we can rely on live server updates and ignore periodic room state messages\n */\n private hasConsistentRoomStateArrived = false;\n\n constructor(\n private store: IStore,\n eventBus: EventBus,\n private transport: HMSTransport,\n private listener?: HMSUpdateListener,\n private audioListener?: HMSAudioListener,\n private connectionQualityListener?: HMSConnectionQualityListener,\n ) {\n const isOnDemandTracksEnabled = this.transport.isFlagEnabled(InitFlags.FLAG_ON_DEMAND_TRACKS);\n this.trackManager = isOnDemandTracksEnabled\n ? new OnDemandTrackManager(this.store, eventBus, this.transport, this.listener)\n : new TrackManager(this.store, eventBus, this.listener);\n\n this.peerManager = new PeerManager(this.store, this.trackManager, this.listener);\n this.peerListManager = new PeerListManager(this.store, this.peerManager, this.trackManager, this.listener);\n this.broadcastManager = new BroadcastManager(this.store, this.listener);\n this.policyChangeManager = new PolicyChangeManager(this.store, eventBus);\n this.requestManager = new RequestManager(this.store, this.listener);\n this.activeSpeakerManager = new ActiveSpeakerManager(this.store, this.listener, this.audioListener);\n this.connectionQualityManager = new ConnectionQualityManager(this.connectionQualityListener);\n this.roomUpdateManager = new RoomUpdateManager(this.store, this.listener);\n this.sessionMetadataManager = new SessionMetadataManager(this.store, this.listener);\n this.pollsManager = new PollsManager(this.store, this.transport, this.listener);\n }\n\n setListener(listener?: HMSUpdateListener) {\n this.listener = listener;\n this.trackManager.listener = listener;\n this.peerManager.listener = listener;\n this.peerListManager.listener = listener;\n this.broadcastManager.listener = listener;\n this.requestManager.listener = listener;\n this.activeSpeakerManager.listener = listener;\n this.roomUpdateManager.listener = listener;\n this.sessionMetadataManager.listener = listener;\n this.pollsManager.listener = listener;\n }\n\n setAudioListener(audioListener?: HMSAudioListener) {\n this.audioListener = audioListener;\n this.activeSpeakerManager.audioListener = audioListener;\n }\n\n setConnectionQualityListener(qualityListener?: HMSConnectionQualityListener) {\n this.connectionQualityListener = qualityListener;\n this.connectionQualityManager.listener = qualityListener;\n }\n\n handleNotification(message: { method: string; params: any }, isReconnecting = false) {\n const method = message.method as HMSNotificationMethod;\n const notification = message.params;\n\n if (\n ![\n HMSNotificationMethod.ACTIVE_SPEAKERS,\n HMSNotificationMethod.SFU_STATS,\n HMSNotificationMethod.CONNECTION_QUALITY,\n undefined, // this is is to ignore notifications without any method\n ].includes(method)\n ) {\n HMSLogger.d(this.TAG, `Received notification - ${method}`, { notification });\n }\n if (method === HMSNotificationMethod.SFU_STATS) {\n if (window.HMS?.ON_SFU_STATS && typeof window.HMS?.ON_SFU_STATS === 'function') {\n window.HMS.ON_SFU_STATS(message.params);\n }\n }\n\n if (this.ignoreNotification(method)) {\n return;\n }\n\n this.roomUpdateManager.handleNotification(method, notification);\n this.peerManager.handleNotification(method, notification);\n this.requestManager.handleNotification(method, notification);\n this.peerListManager.handleNotification(method, notification, isReconnecting);\n this.broadcastManager.handleNotification(method, notification);\n this.sessionMetadataManager.handleNotification(method, notification);\n this.pollsManager.handleNotification(method, notification);\n this.handleIsolatedMethods(method, notification);\n }\n\n // eslint-disable-next-line complexity\n handleIsolatedMethods(method: string, notification: any) {\n switch (method) {\n case HMSNotificationMethod.TRACK_METADATA_ADD: {\n this.trackManager.handleTrackMetadataAdd(notification as TrackStateNotification);\n break;\n }\n case HMSNotificationMethod.TRACK_UPDATE: {\n this.trackManager.handleTrackUpdate(notification as TrackStateNotification);\n break;\n }\n case HMSNotificationMethod.TRACK_REMOVE: {\n if (!notification.peer) {\n HMSLogger.d(this.TAG, `Ignoring sfu notification - ${method}`, { notification });\n return;\n }\n this.trackManager.handleTrackRemovedPermanently(notification as TrackStateNotification);\n break;\n }\n case HMSNotificationMethod.ON_SFU_TRACK_LAYER_UPDATE: {\n this.trackManager.handleTrackLayerUpdate(notification as OnTrackLayerUpdateNotification);\n break;\n }\n case HMSNotificationMethod.ACTIVE_SPEAKERS:\n this.activeSpeakerManager.handleActiveSpeakers(notification as SpeakerList);\n break;\n\n case HMSNotificationMethod.CONNECTION_QUALITY:\n this.connectionQualityManager.handleQualityUpdate(notification as ConnectionQualityList);\n break;\n\n case HMSNotificationMethod.POLICY_CHANGE:\n this.policyChangeManager.handlePolicyChange(notification as PolicyParams);\n break;\n\n default:\n break;\n }\n }\n\n ignoreNotification = (method: string): boolean => {\n if (method === HMSNotificationMethod.PEER_LIST) {\n this.hasConsistentRoomStateArrived = true;\n } else if (method === HMSNotificationMethod.ROOM_STATE) {\n // ignore periodic inconsistent room state if consistent one has arrived at least once\n return this.hasConsistentRoomStateArrived;\n }\n return false;\n };\n\n handleTrackAdd = (track: HMSRemoteTrack) => {\n this.trackManager.handleTrackAdd(track);\n };\n\n handleTrackRemove = (track: HMSRemoteTrack) => {\n this.trackManager.handleTrackRemove(track);\n };\n\n updateLocalPeer = ({ name, metadata }: { name?: string; metadata?: string }) => {\n const peer = this.store.getLocalPeer();\n this.peerManager.handlePeerInfoUpdate({ peer, name, data: metadata });\n };\n}\n", "import HMSLogger from '../utils/logger';\n\nexport class AudioContextManager {\n private audioContext: AudioContext;\n private destinationNode?: MediaStreamAudioDestinationNode;\n private source: MediaElementAudioSourceNode;\n private readonly TAG = '[AudioContextManager]';\n\n constructor(element: HTMLMediaElement) {\n this.audioContext = new AudioContext();\n this.source = this.audioContext.createMediaElementSource(element);\n this.source.connect(this.audioContext.destination);\n }\n\n /**\n * Resume AudioContext if it is suspended\n * Note: when the browser tab is muted by default, AudioContext will be in suspended state\n * It has to be resumed for the video/audio to be played.\n */\n async resumeContext() {\n if (this.audioContext.state === 'suspended') {\n await this.audioContext.resume();\n HMSLogger.d(this.TAG, 'AudioContext is resumed');\n }\n }\n\n getAudioTrack() {\n // Always create a destinationNode to get new audio track id\n if (this.destinationNode) {\n this.source.disconnect(this.destinationNode);\n }\n this.destinationNode = this.audioContext.createMediaStreamDestination();\n this.source.connect(this.destinationNode);\n return this.destinationNode.stream.getAudioTracks()[0];\n }\n\n cleanup() {\n if (this.audioContext.state !== 'closed') {\n this.audioContext.close().catch(e => {\n HMSLogger.d(this.TAG, 'AudioContext close error', e.message);\n });\n }\n }\n}\n", "import { EventEmitter2 as EventEmitter } from 'eventemitter2';\n\n/**\n * Typed Event Emitter Reference:\n * https://rjzaworski.com/2019/10/event-emitters-in-typescript#a-typescript-event-emitter-interface\n */\n\nexport type EventMap = Record<string, any>;\n\nexport type EventKey<T extends EventMap> = string & keyof T;\nexport type EventReceiver<T> = (params: T) => void;\n\n// interface Emitter<T extends EventMap> {\n// on<K extends EventKey<T>>(eventName: K, fn: EventReceiver<T[K]>): void;\n// off<K extends EventKey<T>>(eventName: K, fn: EventReceiver<T[K]>): void;\n// emit<K extends EventKey<T>>(eventName: K, params: T[K]): void;\n// }\n\nexport abstract class TypedEventEmitter<T extends EventMap> extends EventEmitter {\n on<K extends EventKey<T>>(eventName: K, fn: EventReceiver<T[K]>) {\n return super.on(eventName, fn);\n }\n\n off<K extends EventKey<T>>(eventName: K, fn: EventReceiver<T[K]>) {\n return super.off(eventName, fn);\n }\n\n emit<K extends EventKey<T>>(eventName: K, params: T[K]) {\n return super.emit(eventName, params);\n }\n\n listeners<K extends EventKey<T>>(eventName: K): EventReceiver<T[K]>[] {\n return super.listeners(eventName) as EventReceiver<T[K]>[];\n }\n}\n", "import { AudioContextManager } from './AudioContextManager';\nimport HMSLogger from '../utils/logger';\nimport { TypedEventEmitter } from '../utils/typed-event-emitter';\n\n/**\n * This class handles audio playlist management\n * - An audio element and audio context which processes audio from audio element is created\n * in the constructor\n * It handles playback in the following steps\n * - set's the url on the audio element created in the constructor\n * - oncanplaythrough event of the audio element\n * - resume the audio context if it is suspended\n * - play the audio element\n * - Get audio track from the audio context manager\n * - The track is passed to playlist manager to publish\n */\nexport class PlaylistAudioManager extends TypedEventEmitter<{ ended: null; progress: Event }> {\n private audioElement: HTMLAudioElement | null = null;\n private track?: MediaStreamTrack;\n private audioContextManager!: AudioContextManager;\n private readonly TAG = '[PlaylistAudioManager]';\n // This is to handle audio playing when seekTo is called when audio is paused\n private seeked = false;\n\n async play(url: string) {\n this.audioElement = this.getAudioElement();\n return new Promise<MediaStreamTrack[]>((resolve, reject) => {\n this.audioElement = this.getAudioElement();\n this.audioElement.src = url;\n this.seeked = false;\n this.audioElement.onerror = () => {\n const error = `Error loading ${url}`;\n HMSLogger.e(this.TAG, error);\n this.stop();\n reject(error);\n };\n // oncanplaythrough is called when enough media is loaded for play to be possible in two cases -\n // * when play is called for the first time\n // * when user seeks jumps to any mid track timestamp\n this.audioElement.oncanplaythrough = async () => {\n try {\n if (!this.audioElement) {\n return;\n }\n this.audioContextManager.resumeContext();\n // Create audio track only once and reuse, it will be updated with current content\n if (!this.track) {\n await this.audioElement.play();\n const audioTrack = this.audioContextManager.getAudioTrack();\n this.track = audioTrack;\n resolve([audioTrack]);\n } else {\n if (!this.seeked) {\n // if this was called in response to a play call\n await this.audioElement.play();\n resolve([this.track]);\n } else {\n // if seek happened, there is no play call/promise to be resolved, just reset seeked\n this.seeked = false;\n }\n }\n } catch (err) {\n HMSLogger.e(this.TAG, 'Error playing audio', url, (err as ErrorEvent).message);\n reject(err);\n }\n };\n this.audioElement.onseeked = () => {\n this.seeked = true;\n };\n });\n }\n\n getTracks() {\n return this.track ? [this.track.id] : [];\n }\n\n getElement() {\n if (!this.audioElement) {\n this.audioElement = this.getAudioElement();\n }\n return this.audioElement;\n }\n\n stop() {\n this.audioElement?.pause();\n this.audioElement?.removeAttribute('src');\n this.audioElement = null;\n this.audioContextManager?.cleanup();\n this.track = undefined;\n }\n\n private getAudioElement() {\n if (this.audioElement) {\n return this.audioElement;\n }\n const audioElement = document.createElement('audio');\n audioElement.crossOrigin = 'anonymous';\n audioElement.addEventListener('timeupdate', event => this.emit('progress', event));\n audioElement.addEventListener('ended', () => {\n this.emit('ended', null);\n });\n this.audioContextManager = new AudioContextManager(audioElement);\n return audioElement;\n }\n}\n", "import { AudioContextManager } from './AudioContextManager';\nimport HMSLogger from '../utils/logger';\nimport { TypedEventEmitter } from '../utils/typed-event-emitter';\n\n/**\n * This class handles video playlist management\n * - An video element, canvas and audio context which processes audio from video element is created\n * in the constructor\n * It handles playback in the following steps\n * - set's the url on the video element created in the constructor\n * - oncanplaythrough event of the video element\n * - resume the audio context if it is suspended\n * - set width/height on canvas\n * - captureStream on canvas element if not already captured\n * - play the video element\n * - on video element is played, it is drawn to canvas\n * - Get audio track from the audio context manager\n * - add audioTrack to canvas stream\n * - The audio and video tracks are passed to playlist manager to publish\n */\nexport class PlaylistVideoManager extends TypedEventEmitter<{ ended: null; progress: Event }> {\n private readonly TAG = '[PlaylistVideoManager]';\n private videoElement: HTMLVideoElement | null = null;\n private canvasContext: CanvasRenderingContext2D | null = null;\n private canvas!: HTMLCanvasElement;\n private timer: any;\n private tracks: MediaStreamTrack[] = [];\n private audioContextManager!: AudioContextManager;\n private DEFAUL_FPS = 24;\n // This is to handle video playing when seekTo is called when video is paused\n private seeked = false;\n\n play(url: string) {\n this.videoElement = this.getVideoElement();\n this.createCanvas();\n return new Promise<MediaStreamTrack[]>((resolve, reject) => {\n this.videoElement = this.getVideoElement();\n this.videoElement.src = url;\n this.seeked = false;\n this.videoElement.onerror = () => {\n const error = `Error loading ${url}`;\n HMSLogger.e(this.TAG, error);\n this.stop();\n reject(error);\n };\n // oncanplaythrough is called when enough media is loaded for play to be possible in two cases -\n // * when play is called for the first time\n // * when user jumps to any mid track timestamp using seekTo\n this.videoElement.oncanplaythrough = async () => {\n try {\n if (!this.videoElement) {\n return;\n }\n this.canvas.width = this.videoElement.videoWidth;\n this.canvas.height = this.videoElement.videoHeight;\n // Capture stream only once and reuse the same tracks. it will be autoupdated with the selected video\n if (this.tracks.length === 0) {\n this.clearCanvasAndTracks();\n //@ts-ignore\n const stream = this.canvas.captureStream();\n if (!stream) {\n HMSLogger.e(this.TAG, 'Browser does not support captureStream');\n return;\n }\n this.videoElement.onplay = this.drawImage;\n await this.audioContextManager.resumeContext();\n await this.videoElement.play();\n const audioTrack = this.audioContextManager.getAudioTrack();\n stream.addTrack(audioTrack);\n stream.getTracks().forEach((track: MediaStreamTrack) => {\n this.tracks.push(track);\n });\n resolve(this.tracks);\n } else {\n // No need to capture canvas stream/get audio track. They wull be auto updated\n if (!this.seeked) {\n // if this was called in response to a play call\n await this.videoElement.play();\n resolve(this.tracks);\n } else {\n // if seek happened, there is no play call/promise to be resolved, just reset seeked\n this.seeked = false;\n // This event will be called on seekTo when paused. Just draw the one frame on canvas.\n this.canvasContext?.drawImage(this.videoElement, 0, 0, this.canvas?.width, this.canvas?.height);\n }\n }\n } catch (err) {\n HMSLogger.e(this.TAG, 'Error playing video', url, (err as ErrorEvent).message);\n reject(err);\n }\n };\n this.videoElement.onseeked = () => {\n this.seeked = true;\n };\n });\n }\n\n getTracks() {\n return this.tracks.map(track => track.id);\n }\n\n getElement() {\n if (!this.videoElement) {\n this.videoElement = this.getVideoElement();\n }\n return this.videoElement;\n }\n\n stop() {\n this.videoElement?.pause();\n this.videoElement?.removeAttribute('src');\n this.videoElement = null;\n this.audioContextManager?.cleanup();\n this.clearCanvasAndTracks();\n }\n\n private clearCanvasAndTracks() {\n this.tracks = [];\n // clear canvas before playing new video\n this.canvasContext?.clearRect(0, 0, this.canvas.width, this.canvas.height);\n clearTimeout(this.timer);\n }\n\n private drawImage = () => {\n if (this.videoElement && !this.videoElement.paused && !this.videoElement.ended) {\n this.canvasContext?.drawImage(this.videoElement, 0, 0, this.canvas?.width, this.canvas?.height);\n this.timer = setTimeout(() => {\n this.drawImage();\n }, 1000 / this.DEFAUL_FPS);\n }\n };\n\n private getVideoElement() {\n if (this.videoElement) {\n return this.videoElement;\n }\n const videoElement = document.createElement('video');\n videoElement.crossOrigin = 'anonymous';\n videoElement.addEventListener('timeupdate', event => this.emit('progress', event));\n videoElement.addEventListener('ended', () => {\n this.emit('ended', null);\n });\n this.audioContextManager = new AudioContextManager(videoElement);\n return videoElement;\n }\n\n private createCanvas() {\n if (!this.canvas) {\n this.canvas = document.createElement('canvas');\n this.canvasContext = this.canvas.getContext('2d');\n }\n }\n}\n", "import { PlaylistAudioManager } from './PlaylistAudioManager';\nimport { PlaylistVideoManager } from './PlaylistVideoManager';\nimport { ErrorFactory } from '../error/ErrorFactory';\nimport { HMSAction } from '../error/HMSAction';\nimport { EventBus } from '../events/EventBus';\nimport { HMSSdk } from '../index';\nimport { HMSPlaylistItem, HMSPlaylistManager, HMSPlaylistProgressEvent, HMSPlaylistType } from '../interfaces';\nimport { HMSLocalTrack } from '../media/tracks';\nimport { stringifyMediaStreamTrack } from '../utils/json';\nimport HMSLogger from '../utils/logger';\nimport { TypedEventEmitter } from '../utils/typed-event-emitter';\n\ntype PlaylistManagerState<T> = {\n audio: {\n list: HMSPlaylistItem<T>[];\n currentIndex: number;\n isAutoplayOn: boolean;\n };\n video: {\n list: HMSPlaylistItem<T>[];\n currentIndex: number;\n isAutoplayOn: boolean;\n };\n};\n\nconst INITIAL_STATE: PlaylistManagerState<any> = {\n audio: {\n list: [],\n currentIndex: -1,\n isAutoplayOn: true,\n },\n video: {\n list: [],\n currentIndex: -1,\n isAutoplayOn: true,\n },\n};\n\nexport class PlaylistManager\n extends TypedEventEmitter<{\n newTrackStart: HMSPlaylistItem<any>;\n playlistEnded: HMSPlaylistType;\n currentTrackEnded: HMSPlaylistItem<any>;\n }>\n implements HMSPlaylistManager\n{\n private state = { audio: { ...INITIAL_STATE.audio }, video: { ...INITIAL_STATE.video } };\n private audioManager: PlaylistAudioManager;\n private videoManager: PlaylistVideoManager;\n private readonly TAG = '[PlaylistManager]';\n\n constructor(private sdk: HMSSdk, private eventBus: EventBus) {\n super();\n this.audioManager = new PlaylistAudioManager();\n this.videoManager = new PlaylistVideoManager();\n this.addListeners();\n }\n\n getList<T>(type: HMSPlaylistType = HMSPlaylistType.audio): HMSPlaylistItem<T>[] {\n return this.state[type].list;\n }\n\n setList<T>(list: HMSPlaylistItem<T>[]): void {\n if (!list || list.length === 0) {\n HMSLogger.w(this.TAG, `Please pass in a list of HMSPlaylistItem's`);\n return;\n }\n list.forEach((item: HMSPlaylistItem<T>) => {\n if (!this.state[item.type].list.find(_item => _item.id === item.id)) {\n this.state[item.type].list.push(item);\n }\n });\n }\n\n async clearList(type: HMSPlaylistType): Promise<void> {\n if (this.isPlaying(type)) {\n await this.stop(type);\n }\n this.state[type].list = [];\n }\n\n async removeItem(id: string, type: HMSPlaylistType): Promise<boolean> {\n const { list, currentIndex } = this.state[type];\n const index = list.findIndex(playItem => id === playItem.id);\n if (index > -1) {\n // stop if the item is playing\n if (currentIndex === index && this.isPlaying(type)) {\n await this.stop(type);\n }\n list.splice(index, 1);\n return true;\n }\n return false;\n }\n\n seek(value: number, type: HMSPlaylistType = HMSPlaylistType.audio): void {\n const { currentIndex } = this.state[type];\n if (currentIndex === -1) {\n throw ErrorFactory.PlaylistErrors.NoEntryToPlay(HMSAction.PLAYLIST, 'No item is currently playing');\n }\n const element = this.getElement(type);\n if (element) {\n const updatedValue = Math.max(element.currentTime + value, 0);\n element.currentTime = Math.min(updatedValue, element.duration);\n }\n }\n\n seekTo(value: number, type: HMSPlaylistType = HMSPlaylistType.audio): void {\n const { currentIndex } = this.state[type];\n if (currentIndex === -1) {\n throw ErrorFactory.PlaylistErrors.NoEntryToPlay(HMSAction.PLAYLIST, 'No item is currently playing');\n }\n if (value < 0) {\n throw Error('value cannot be negative');\n }\n const element = this.getElement(type);\n if (element) {\n element.currentTime = Math.min(value, element.duration);\n }\n }\n\n setVolume(value: number, type: HMSPlaylistType = HMSPlaylistType.audio): void {\n if (value < 0 || value > 100) {\n throw Error('Please pass a valid number between 0-100');\n }\n const element = this.getElement(type);\n if (element) {\n element.volume = value * 0.01;\n }\n }\n\n getVolume(type: HMSPlaylistType = HMSPlaylistType.audio): number {\n const element = this.getElement(type);\n if (element) {\n return Math.floor(element.volume * 100);\n }\n return 0;\n }\n\n getCurrentTime(type: HMSPlaylistType = HMSPlaylistType.audio) {\n const element = this.getElement(type);\n return element?.currentTime || 0;\n }\n\n getCurrentIndex(type: HMSPlaylistType = HMSPlaylistType.audio) {\n return this.state[type].currentIndex;\n }\n\n getCurrentProgress(type: HMSPlaylistType = HMSPlaylistType.audio) {\n const { list, currentIndex } = this.state[type];\n const activeUrl = list[currentIndex]?.url;\n const element = this.getElement(type);\n if (!activeUrl || !element) {\n return 0;\n }\n return Math.floor(100 * (element.currentTime / element.duration));\n }\n\n getCurrentSelection(type: HMSPlaylistType = HMSPlaylistType.audio) {\n const { list, currentIndex } = this.state[type];\n if (currentIndex === -1) {\n return undefined;\n }\n return list[currentIndex];\n }\n\n isPlaying(type: HMSPlaylistType = HMSPlaylistType.audio) {\n const element = this.getElement(type);\n return !!element && !element.paused;\n }\n\n setIsAutoplayOn(type: HMSPlaylistType = HMSPlaylistType.audio, autoplay: boolean) {\n this.state[type].isAutoplayOn = autoplay;\n }\n\n getPlaybackRate(type: HMSPlaylistType = HMSPlaylistType.audio) {\n const element = this.getElement(type);\n return element ? element.playbackRate : 1.0;\n }\n\n setPlaybackRate(type: HMSPlaylistType = HMSPlaylistType.audio, value: number) {\n if (value < 0.25 || value > 2.0) {\n throw Error('Please pass a value between 0.25 and 2.0');\n }\n const element = this.getElement(type);\n if (element) {\n element.playbackRate = value;\n }\n }\n\n async setEnabled(\n enabled: boolean,\n { id, type = HMSPlaylistType.audio }: { id: string; type: HMSPlaylistType },\n ): Promise<void> {\n const list = this.state[type].list;\n const currentIndex = list.findIndex(item => item.id === id);\n if (!id || currentIndex === -1) {\n HMSLogger.w(this.TAG, 'Pass a valid id');\n return;\n }\n const url = this.state[type].list[currentIndex].url;\n if (enabled) {\n await this.play(url, type);\n } else {\n await this.pause(url, type);\n }\n this.state[type].currentIndex = currentIndex;\n this.setDuration(type);\n }\n\n async playNext(type: HMSPlaylistType = HMSPlaylistType.audio): Promise<void> {\n const { list, currentIndex } = this.state[type];\n if (currentIndex >= list.length - 1) {\n throw ErrorFactory.PlaylistErrors.NoEntryToPlay(HMSAction.PLAYLIST, 'Reached end of playlist');\n }\n await this.play(list[currentIndex + 1].url, type);\n this.state[type].currentIndex = currentIndex + 1;\n this.setDuration(type);\n }\n\n async playPrevious(type: HMSPlaylistType = HMSPlaylistType.audio): Promise<void> {\n const { list, currentIndex } = this.state[type];\n if (currentIndex <= 0) {\n throw ErrorFactory.PlaylistErrors.NoEntryToPlay(HMSAction.PLAYLIST, 'Reached start of playlist');\n }\n await this.play(list[currentIndex - 1].url, type);\n this.state[type].currentIndex = currentIndex - 1;\n this.setDuration(type);\n }\n\n async stop(type: HMSPlaylistType = HMSPlaylistType.audio): Promise<void> {\n const manager = type === HMSPlaylistType.audio ? this.audioManager : this.videoManager;\n manager.getElement()?.pause(); //pause local video/audio and remove tracks in next step\n await this.removeTracks(type);\n manager.stop();\n this.state[type].currentIndex = -1;\n }\n\n cleanup() {\n this.state = { audio: { ...INITIAL_STATE.audio }, video: { ...INITIAL_STATE.video } };\n this.eventBus.localAudioEnabled.unsubscribe(this.handlePausePlaylist);\n this.eventBus.localVideoEnabled.unsubscribe(this.handlePausePlaylist);\n this.audioManager.stop();\n this.videoManager.stop();\n }\n\n onProgress(fn: (progress: HMSPlaylistProgressEvent) => void) {\n this.videoManager.on('progress', () => {\n try {\n fn({\n type: HMSPlaylistType.video,\n progress: this.getCurrentProgress(HMSPlaylistType.video),\n });\n } catch (error) {\n HMSLogger.e(this.TAG, 'Error in onProgress callback');\n }\n });\n this.audioManager.on('progress', () => {\n try {\n fn({\n type: HMSPlaylistType.audio,\n progress: this.getCurrentProgress(HMSPlaylistType.audio),\n });\n } catch (error) {\n HMSLogger.e(this.TAG, 'Error in onProgress callback');\n }\n });\n }\n\n onNewTrackStart<T>(fn: (item: HMSPlaylistItem<T>) => void) {\n this.on('newTrackStart', fn);\n }\n\n onPlaylistEnded(fn: (type: HMSPlaylistType) => void) {\n this.on('playlistEnded', fn);\n }\n\n onCurrentTrackEnded<T>(fn: (item: HMSPlaylistItem<T>) => void) {\n this.on('currentTrackEnded', fn);\n }\n\n private getElement(type: HMSPlaylistType = HMSPlaylistType.audio) {\n return type === HMSPlaylistType.audio ? this.audioManager.getElement() : this.videoManager.getElement();\n }\n\n private async removeTracks(type: HMSPlaylistType = HMSPlaylistType.audio) {\n const manager = type === HMSPlaylistType.audio ? this.audioManager : this.videoManager;\n const tracks = manager.getTracks();\n for (const trackId of tracks) {\n await this.removeTrack(trackId);\n }\n }\n\n private async play(url: string, type: HMSPlaylistType = HMSPlaylistType.audio): Promise<void> {\n const manager = type === HMSPlaylistType.audio ? this.audioManager : this.videoManager;\n const element = manager.getElement();\n if (this.isItemCurrentlyPlaying(url, type)) {\n HMSLogger.w(this.TAG, `The ${type} is currently playing`);\n return;\n }\n if (element?.src.includes(url)) {\n await element.play();\n } else {\n element?.pause();\n const tracks: MediaStreamTrack[] = await manager.play(url);\n for (const track of tracks) {\n await this.addTrack(track, type === HMSPlaylistType.audio ? 'audioplaylist' : 'videoplaylist');\n }\n }\n }\n\n private isItemCurrentlyPlaying(url: string, type: HMSPlaylistType): boolean {\n const element = this.getElement(type);\n return !!(element && !element.paused && element.src.includes(url));\n }\n\n private setDuration(type: HMSPlaylistType = HMSPlaylistType.audio) {\n const element = this.getElement(type);\n const { list, currentIndex } = this.state[type];\n if (list[currentIndex]) {\n list[currentIndex].duration = element?.duration || 0;\n }\n this.emit('newTrackStart', list[currentIndex]);\n }\n\n private async pause(url: string, type: HMSPlaylistType = HMSPlaylistType.audio): Promise<void> {\n const el = this.getElement(type);\n if (el && !el.paused && el.src.includes(url)) {\n el.pause();\n HMSLogger.d(this.TAG, 'paused url', url);\n } else {\n HMSLogger.w(this.TAG, 'The passed in url is not currently playing');\n }\n }\n\n private handlePausePlaylist = async ({ enabled, track }: { enabled: boolean; track: HMSLocalTrack }) => {\n if (enabled) {\n return;\n }\n let type: HMSPlaylistType | undefined = undefined;\n if (track.source === 'audioplaylist') {\n type = HMSPlaylistType.audio;\n }\n if (track.source === 'videoplaylist') {\n type = HMSPlaylistType.video;\n }\n if (!type) {\n return;\n }\n this.getElement(type)?.pause();\n };\n\n private addListeners() {\n this.audioManager.on('ended', () => this.handleEnded(HMSPlaylistType.audio));\n this.videoManager.on('ended', () => this.handleEnded(HMSPlaylistType.video));\n this.eventBus.localAudioEnabled.subscribe(this.handlePausePlaylist);\n this.eventBus.localVideoEnabled.subscribe(this.handlePausePlaylist);\n }\n\n /**\n * Remove tracks if reached the end of list otherwise play next\n * @param {HMSPlaylistType} type\n */\n private async handleEnded(type: HMSPlaylistType = HMSPlaylistType.audio) {\n const { list, currentIndex, isAutoplayOn } = this.state[type];\n if (currentIndex === list.length - 1) {\n await this.stop(type);\n this.emit('playlistEnded', type);\n } else {\n if (isAutoplayOn) {\n this.playNext(type);\n } else {\n // when autoplay not allowed, pause the media element\n await this.pause(list[currentIndex].url, type);\n }\n }\n this.emit('currentTrackEnded', list[currentIndex]);\n }\n\n private addTrack = async (track: MediaStreamTrack, source: string) => {\n await this.sdk.addTrack(track, source);\n HMSLogger.d(this.TAG, 'Playlist track added', stringifyMediaStreamTrack(track));\n };\n\n private removeTrack = async (trackId: string) => {\n await this.sdk.removeTrack(trackId, true);\n HMSLogger.d(this.TAG, 'Playlist track removed', trackId);\n };\n}\n", "import { HMSSessionStore } from '../interfaces';\nimport ITransport from '../transport/ITransport';\nimport { convertDateNumToDate } from '../utils/date';\n\nexport class SessionStore implements HMSSessionStore {\n private observedKeys: Set<string> = new Set();\n\n constructor(private transport: ITransport) {}\n\n async get(key: string) {\n const { data, updated_at } = await this.transport.getSessionMetadata(key);\n\n return { value: data, updatedAt: convertDateNumToDate(updated_at) };\n }\n\n async set(key: string, data: any) {\n const { data: value, updated_at } = await this.transport.setSessionMetadata({ key, data });\n const updatedAt = convertDateNumToDate(updated_at);\n return { value, updatedAt };\n }\n\n async observe(keys: string[]) {\n const prevObservedKeys = new Set(this.observedKeys);\n keys.forEach(key => this.observedKeys.add(key));\n\n if (this.observedKeys.size !== prevObservedKeys.size) {\n try {\n await this.transport.listenMetadataChange(Array.from(this.observedKeys));\n } catch (e) {\n this.observedKeys = prevObservedKeys;\n throw e;\n }\n }\n }\n\n async unobserve(keys: string[]) {\n const prevObservedKeys = new Set(this.observedKeys);\n this.observedKeys = new Set([...this.observedKeys].filter(key => !keys.includes(key)));\n\n if (this.observedKeys.size !== prevObservedKeys.size) {\n try {\n await this.transport.listenMetadataChange(Array.from(this.observedKeys));\n } catch (e) {\n this.observedKeys = prevObservedKeys;\n throw e;\n }\n }\n }\n}\n", "import { HMSPollQuestionCreateParams, HMSPollsUpdate, PollsListener } from '../../interfaces';\nimport { HMSInteractivityCenter } from '../../interfaces/session-store/interactivity-center';\nimport {\n HMSPoll,\n HMSPollCreateParams,\n HMSPollQuestionAnswer,\n HMSPollQuestionOption,\n HMSPollQuestionResponse,\n HMSPollQuestionResponseCreateParams,\n HMSPollQuestionType,\n} from '../../interfaces/session-store/polls';\nimport { IStore } from '../../sdk/store';\nimport { PollQuestionParams, PollResponseParams } from '../../signal/interfaces';\nimport HMSTransport from '../../transport';\nimport { convertDateNumToDate } from '../../utils/date';\n\nexport class InteractivityCenter implements HMSInteractivityCenter {\n constructor(private transport: HMSTransport, private store: IStore, private listener?: PollsListener) {}\n\n setListener(listener?: PollsListener) {\n this.listener = listener;\n }\n\n async createPoll(pollParams: HMSPollCreateParams) {\n const { poll_id: serverPollID } = await this.transport.setPollInfo({\n ...pollParams,\n poll_id: pollParams.id,\n vote: pollParams.rolesThatCanVote,\n responses: pollParams.rolesThatCanViewResponses,\n });\n\n if (!pollParams.id) {\n pollParams.id = serverPollID;\n }\n\n if (Array.isArray(pollParams.questions)) {\n await this.addQuestionsToPoll(pollParams.id, pollParams.questions);\n }\n\n const questions = await this.transport.getPollQuestions({ poll_id: pollParams.id, index: 0, count: 50 });\n\n const poll: HMSPoll = {\n id: pollParams.id,\n title: pollParams.title,\n anonymous: pollParams.anonymous,\n type: pollParams.type,\n duration: pollParams.duration,\n locked: pollParams.locked, // poll is locked automatically when it starts\n mode: pollParams.mode,\n visibility: pollParams.visibility,\n rolesThatCanVote: pollParams.rolesThatCanVote || [],\n rolesThatCanViewResponses: pollParams.rolesThatCanViewResponses || [],\n state: 'created',\n createdBy: this.store.getLocalPeer()?.peerId,\n questions: questions.questions.map(({ question, options, answer }) => ({ ...question, options, answer })),\n };\n\n this.listener?.onPollsUpdate(HMSPollsUpdate.POLL_CREATED, [poll]);\n }\n\n async startPoll(poll: string | HMSPollCreateParams): Promise<void> {\n if (typeof poll === 'string') {\n await this.transport.startPoll({ poll_id: poll });\n } else {\n await this.createPoll(poll);\n await this.transport.startPoll({ poll_id: poll.id });\n }\n }\n\n async addQuestionsToPoll(pollID: string, questions: HMSPollQuestionCreateParams[]): Promise<void> {\n if (questions.length > 0) {\n await this.transport.setPollQuestions({\n poll_id: pollID,\n questions: questions.map((question, index) => this.createQuestionSetParams(question, index)),\n });\n }\n }\n\n async stopPoll(pollID: string): Promise<void> {\n await this.transport.stopPoll({ poll_id: pollID });\n }\n\n async addResponsesToPoll(pollID: string, responses: HMSPollQuestionResponseCreateParams[]) {\n const poll = this.store.getPoll(pollID);\n if (!poll) {\n throw new Error('Invalid poll ID - Poll not found');\n }\n const responsesParams: PollResponseParams[] = responses.map(response => {\n const question = this.getQuestionInPoll(poll, response.questionIndex);\n if (question.type === HMSPollQuestionType.SINGLE_CHOICE) {\n response.option = response.option || response.options?.[0] || -1;\n delete response.text;\n delete response.options;\n } else if (question.type === HMSPollQuestionType.MULTIPLE_CHOICE) {\n response.options?.sort();\n delete response.text;\n delete response.option;\n } else {\n delete response.option;\n delete response.options;\n }\n\n if (response.skipped) {\n delete response.option;\n delete response.options;\n delete response.text;\n }\n\n return { duration: 0, type: question.type, question: response.questionIndex, ...response };\n });\n\n await this.transport.setPollResponses({ poll_id: pollID, responses: responsesParams });\n }\n\n async getPolls(): Promise<HMSPoll[]> {\n const pollsList = await this.transport.getPollsList({ count: 50 });\n const polls: HMSPoll[] = [];\n for (const pollParams of pollsList.polls) {\n const questions = await this.transport.getPollQuestions({ poll_id: pollParams.poll_id, index: 0, count: 50 });\n const poll: HMSPoll = {\n id: pollParams.poll_id,\n title: pollParams.title,\n startedBy: pollParams.started_by,\n createdBy: pollParams.created_by,\n anonymous: pollParams.anonymous,\n type: pollParams.type,\n duration: pollParams.duration,\n locked: pollParams.locked, // poll is locked automatically when it starts\n mode: pollParams.mode as HMSPoll['mode'],\n visibility: pollParams.visibility,\n rolesThatCanVote: pollParams.vote || [],\n rolesThatCanViewResponses: pollParams.responses || [],\n state: pollParams.state,\n stoppedBy: pollParams.stopped_by,\n startedAt: convertDateNumToDate(pollParams.started_at),\n stoppedAt: convertDateNumToDate(pollParams.stopped_at),\n createdAt: convertDateNumToDate(pollParams.created_at),\n\n questions: questions.questions.map(({ question, options, answer }) => ({ ...question, options, answer })),\n };\n\n polls.push(poll);\n this.store.setPoll(poll);\n }\n\n return polls;\n }\n\n getResponses(_pollID: string): Promise<HMSPollQuestionResponse[]> {\n throw new Error('Method not implemented.');\n }\n\n private createQuestionSetParams(questionParams: HMSPollQuestionCreateParams, index: number): PollQuestionParams {\n const question: PollQuestionParams['question'] = { ...questionParams, index: index + 1 };\n let options: HMSPollQuestionOption[] | undefined;\n const answer: HMSPollQuestionAnswer = questionParams.answer || { hidden: false };\n if (\n Array.isArray(questionParams.options) &&\n [HMSPollQuestionType.SINGLE_CHOICE, HMSPollQuestionType.MULTIPLE_CHOICE].includes(questionParams.type)\n ) {\n options = questionParams.options?.map((option, index) => ({\n index: index + 1,\n text: option.text,\n weight: option.weight,\n }));\n\n delete answer?.text;\n if (questionParams.type === HMSPollQuestionType.SINGLE_CHOICE) {\n answer.option = questionParams.options.findIndex(option => option.isCorrectAnswer) + 1 || undefined;\n } else {\n answer.options = questionParams.options\n .map((option, index) => (option.isCorrectAnswer ? index + 1 : undefined))\n .filter((val): val is number => !!val);\n }\n } else {\n delete answer?.options;\n delete answer?.option;\n }\n\n return { question, options, answer };\n }\n\n private getQuestionInPoll(poll: HMSPoll, questionIndex: number) {\n const question = poll?.questions?.find(question => question.index === questionIndex);\n if (!question) {\n throw new Error('Invalid question index - Question not found in poll');\n }\n\n return question;\n }\n}\n", "export class JoinParameters {\n constructor(\n public authToken: string,\n public peerId: string,\n public peerName: string = '',\n public data: string = '',\n public endpoint: string = 'https://prod-init.100ms.live/init',\n public autoSubscribeVideo: boolean = false,\n ) {}\n}\n", "export enum TransportFailureCategory {\n ConnectFailed,\n SignalDisconnect,\n JoinWSMessageFailed,\n PublishIceConnectionFailed,\n SubscribeIceConnectionFailed,\n}\n\nexport const Dependencies = {\n [TransportFailureCategory.ConnectFailed]: [],\n [TransportFailureCategory.SignalDisconnect]: [],\n [TransportFailureCategory.JoinWSMessageFailed]: [TransportFailureCategory.SignalDisconnect],\n [TransportFailureCategory.PublishIceConnectionFailed]: [TransportFailureCategory.SignalDisconnect],\n [TransportFailureCategory.SubscribeIceConnectionFailed]: [TransportFailureCategory.SignalDisconnect],\n};\n", "export enum TransportState {\n Disconnected = 'Disconnected',\n Connecting = 'Connecting',\n Joined = 'Joined',\n Preview = 'Preview',\n Failed = 'Failed',\n Reconnecting = 'Reconnecting',\n Leaving = 'Leaving',\n}\n", "export interface PromiseCallbacks<T, K = void> {\n resolve: (value: T) => void;\n reject: (reason?: any) => void;\n metadata?: K;\n}\n\nexport class PromiseWithCallbacks<T> {\n promise: Promise<T>;\n resolve!: (value: T) => void;\n reject!: (reason?: any) => void;\n\n constructor(cb: (resolve: (value: T) => void, reject: (reason?: any) => void) => any) {\n this.promise = new Promise<T>((resolve, reject) => {\n this.resolve = resolve;\n this.reject = reject;\n cb(resolve, reject);\n });\n }\n}\n", "import { Dependencies as TFCDependencies, TransportFailureCategory as TFC } from './models/TransportFailureCategory';\nimport { TransportState } from './models/TransportState';\nimport { HMSException } from '../error/HMSException';\nimport { MAX_TRANSPORT_RETRIES, MAX_TRANSPORT_RETRY_DELAY } from '../utils/constants';\nimport HMSLogger from '../utils/logger';\nimport { PromiseWithCallbacks } from '../utils/promise';\n\n/**\n * Task which is executed by [RetryScheduler.schedule] until max retry count\n * is reached.\n *\n * Any exception raised while executing the task assumes that task is failed.\n * Failed tasks are retried if max retry count is not reached.\n *\n * @returns True if the task if successful, otherwise False\n *\n *\n */\ntype RetryTask = () => Promise<boolean>;\n\ninterface ScheduleTaskParams {\n category: TFC;\n error: HMSException;\n task: RetryTask;\n originalState: TransportState;\n maxFailedRetries?: number;\n changeState?: boolean;\n}\n\nexport class RetryScheduler {\n private readonly TAG = '[RetryScheduler]';\n private inProgress = new Map<TFC, PromiseWithCallbacks<number>>();\n private retryTaskIds: number[] = [];\n\n constructor(\n private onStateChange: (state: TransportState, error?: HMSException) => Promise<void>,\n private sendEvent: (error: HMSException, category: TFC) => void,\n ) {}\n\n async schedule({\n category,\n error,\n task,\n originalState,\n maxFailedRetries = MAX_TRANSPORT_RETRIES,\n changeState = true,\n }: ScheduleTaskParams) {\n await this.scheduleTask({ category, error, changeState, task, originalState, maxFailedRetries });\n }\n\n reset() {\n this.retryTaskIds.forEach(future => clearTimeout(future));\n this.retryTaskIds = [];\n this.inProgress.clear();\n }\n\n isTaskInProgress(category: TFC) {\n return !!this.inProgress.get(category);\n }\n\n // eslint-disable-next-line complexity\n private async scheduleTask({\n category,\n error,\n changeState,\n task,\n originalState,\n maxFailedRetries = MAX_TRANSPORT_RETRIES,\n failedRetryCount = 0,\n }: ScheduleTaskParams & { failedRetryCount?: number }): Promise<void> {\n HMSLogger.d(this.TAG, 'schedule: ', { category: TFC[category], error });\n\n // First schedule call\n if (failedRetryCount === 0) {\n const inProgressTask = this.inProgress.get(category);\n if (inProgressTask) {\n HMSLogger.d(this.TAG, `schedule: Already a task for ${TFC[category]} scheduled, waiting for its completion`);\n await inProgressTask.promise;\n return;\n }\n\n const taskPromise = new PromiseWithCallbacks<number>((_, __) => {});\n this.inProgress.set(category, taskPromise);\n\n this.sendEvent(error, category);\n }\n\n let hasFailedDependency = false;\n const dependencies = TFCDependencies[category];\n\n for (const dependencyIndexString in dependencies) {\n const dependency = dependencies[parseInt(dependencyIndexString)];\n try {\n const dependencyTask = this.inProgress.get(dependency);\n if (dependencyTask) {\n HMSLogger.d(\n this.TAG,\n `schedule: Suspending retry task of ${TFC[category]}, waiting for ${TFC[dependency]} to recover`,\n );\n await dependencyTask.promise;\n HMSLogger.d(\n this.TAG,\n `schedule: Resuming retry task ${TFC[category]} as it's dependency ${TFC[dependency]} is recovered`,\n );\n }\n } catch (ex) {\n HMSLogger.d(\n this.TAG,\n `schedule: Stopping retry task of ${TFC[category]} as it's dependency ${TFC[dependency]} failed to recover`,\n );\n hasFailedDependency = true;\n break;\n }\n }\n\n if (failedRetryCount >= maxFailedRetries || hasFailedDependency) {\n error.description += `. [${TFC[category]}] Could not recover after ${failedRetryCount} tries`;\n\n if (hasFailedDependency) {\n error.description += ` Could not recover all of it's required dependencies - [${(dependencies as Array<TFC>)\n .map(dep => TFC[dep])\n .toString()}]`;\n }\n error.isTerminal = true;\n\n // @NOTE: Don't reject to throw error for dependencies, use onStateChange\n // const taskPromise = this.inProgress.get(category);\n this.inProgress.delete(category);\n // taskPromise?.reject(error);\n this.sendEvent(error, category);\n\n this.reset();\n\n if (changeState) {\n this.onStateChange(TransportState.Failed, error);\n } else {\n throw error;\n }\n\n return;\n }\n\n if (changeState) {\n this.onStateChange(TransportState.Reconnecting, error);\n }\n\n const delay = this.getDelayForRetryCount(category, failedRetryCount);\n\n HMSLogger.d(\n this.TAG,\n `schedule: [${TFC[category]}] [failedRetryCount=${failedRetryCount}] Scheduling retry task in ${delay}ms`,\n );\n\n let taskSucceeded: boolean;\n try {\n taskSucceeded = await this.setTimeoutPromise(task, delay);\n } catch (ex) {\n taskSucceeded = false;\n HMSLogger.w(\n this.TAG,\n `[${TFC[category]}] Un-caught exception ${(ex as HMSException).name} in retry-task, initiating retry`,\n ex,\n );\n }\n\n if (taskSucceeded) {\n const taskPromise = this.inProgress.get(category);\n this.inProgress.delete(category);\n taskPromise?.resolve(failedRetryCount);\n\n if (changeState && this.inProgress.size === 0) {\n this.onStateChange(originalState);\n }\n HMSLogger.d(this.TAG, `schedule: [${TFC[category]}] [failedRetryCount=${failedRetryCount}] Recovered \u267B\uFE0F`);\n } else {\n await this.scheduleTask({\n category,\n error,\n changeState,\n task,\n originalState,\n maxFailedRetries,\n failedRetryCount: failedRetryCount + 1,\n });\n }\n }\n\n private getBaseDelayForTask(category: TFC, n: number) {\n if (category === TFC.JoinWSMessageFailed) {\n // linear backoff(2 + jitter for every retry)\n return 2;\n }\n // exponential backoff\n return Math.pow(2, n);\n }\n\n private getDelayForRetryCount(category: TFC, n: number) {\n const delay = this.getBaseDelayForTask(category, n);\n const jitter = category === TFC.JoinWSMessageFailed ? Math.random() * 2 : Math.random();\n return Math.round(Math.min(delay + jitter, MAX_TRANSPORT_RETRY_DELAY) * 1000);\n }\n\n private async setTimeoutPromise<T>(task: () => Promise<T>, delay: number): Promise<T> {\n return new Promise((resolve, reject) => {\n const timeoutId = window.setTimeout(async () => {\n try {\n const value: T = await task();\n value && this.retryTaskIds.splice(this.retryTaskIds.indexOf(timeoutId), 1);\n resolve(value);\n } catch (error) {\n reject(error);\n }\n }, delay);\n\n this.retryTaskIds.push(timeoutId);\n });\n }\n}\n", "import { BaseSample, PublishAnalyticPayload, TrackAnalytics, VideoSample } from './interfaces';\nimport { EventBus } from '../../events/EventBus';\nimport { HMSTrackStats } from '../../interfaces';\nimport { HMSLocalTrack } from '../../media/tracks';\nimport { HMSWebrtcStats } from '../../rtc-stats';\nimport { IStore } from '../../sdk/store';\nimport { PUBLISH_STATS_PUSH_INTERVAL, PUBLISH_STATS_SAMPLE_WINDOW } from '../../utils/constants';\nimport HMSLogger from '../../utils/logger';\nimport { sleep } from '../../utils/timer-utils';\nimport AnalyticsEventFactory from '../AnalyticsEventFactory';\n\nexport class PublishStatsAnalytics {\n private shouldSendEvent = false;\n private sequenceNum = 1;\n private trackAnalytics: Map<string, RunningTrackAnalytics> = new Map();\n\n constructor(\n private store: IStore,\n private eventBus: EventBus,\n private readonly sampleWindowSize = PUBLISH_STATS_SAMPLE_WINDOW,\n private readonly pushInterval = PUBLISH_STATS_PUSH_INTERVAL,\n ) {\n this.start();\n }\n\n start() {\n if (this.shouldSendEvent) {\n return;\n }\n this.stop();\n this.shouldSendEvent = true;\n this.eventBus.statsUpdate.subscribe(this.handleStatsUpdate);\n this.startLoop().catch(e => HMSLogger.e('[PublishStatsAnalytics]', e.message));\n }\n\n stop = () => {\n if (this.shouldSendEvent) {\n this.sendEvent();\n }\n this.eventBus.statsUpdate.unsubscribe(this.handleStatsUpdate);\n this.shouldSendEvent = false;\n };\n\n private async startLoop() {\n while (this.shouldSendEvent) {\n await sleep(this.pushInterval * 1000);\n this.sendEvent();\n }\n }\n\n private toAnalytics(): PublishAnalyticPayload {\n const audio: TrackAnalytics[] = [];\n const video: TrackAnalytics[] = [];\n this.trackAnalytics.forEach(trackAnalytic => {\n if (trackAnalytic.track.type === 'audio') {\n audio.push(trackAnalytic.toAnalytics());\n } else if (trackAnalytic.track.type === 'video') {\n video.push(trackAnalytic.toAnalytics());\n }\n });\n return {\n audio,\n video,\n joined_at: this.store.getRoom()?.joinedAt?.getTime()!,\n sequence_num: this.sequenceNum++,\n max_window_sec: PUBLISH_STATS_SAMPLE_WINDOW,\n };\n }\n\n private sendEvent = () => {\n this.eventBus.analytics.publish(AnalyticsEventFactory.publishStats(this.toAnalytics()));\n };\n\n private handleStatsUpdate = (hmsStats: HMSWebrtcStats) => {\n const localTracksStats = hmsStats.getLocalTrackStats();\n Object.keys(localTracksStats).forEach(trackIDBeingSent => {\n const trackStats = localTracksStats[trackIDBeingSent];\n const track = this.store.getLocalPeerTracks().find(track => track.getTrackIDBeingSent() === trackIDBeingSent);\n Object.keys(trackStats).forEach(statId => {\n const layerStats = trackStats[statId];\n const identifier = track && this.getTrackIdentifier(track?.trackId, layerStats);\n if (identifier && this.trackAnalytics.has(identifier)) {\n this.trackAnalytics.get(identifier)?.push({\n ...layerStats,\n availableOutgoingBitrate: hmsStats.getLocalPeerStats()?.publish?.availableOutgoingBitrate,\n });\n } else {\n if (track) {\n const trackAnalytics = new RunningTrackAnalytics({\n track,\n sampleWindowSize: this.sampleWindowSize,\n rid: layerStats.rid,\n ssrc: layerStats.ssrc.toString(),\n kind: layerStats.kind,\n });\n trackAnalytics.push({\n ...layerStats,\n availableOutgoingBitrate: hmsStats.getLocalPeerStats()?.publish?.availableOutgoingBitrate,\n });\n this.trackAnalytics.set(this.getTrackIdentifier(track?.trackId, layerStats), trackAnalytics);\n }\n }\n });\n });\n };\n\n private getTrackIdentifier(trackId: string, stats: HMSTrackStats) {\n return stats.rid ? `${trackId}:${stats.rid}` : trackId;\n }\n}\n\ntype TempPublishStats = HMSTrackStats & { availableOutgoingBitrate?: number };\n\nclass RunningTrackAnalytics {\n readonly sampleWindowSize: number;\n track: HMSLocalTrack;\n track_id: string;\n source: string;\n ssrc: string;\n kind: string;\n rid?: string;\n samples: BaseSample[] = [];\n\n private tempStats: TempPublishStats[] = [];\n\n constructor({\n track,\n ssrc,\n rid,\n kind,\n sampleWindowSize,\n }: {\n track: HMSLocalTrack;\n ssrc: string;\n kind: string;\n rid?: string;\n sampleWindowSize: number;\n }) {\n this.track = track;\n this.ssrc = ssrc;\n this.rid = rid;\n this.kind = kind;\n this.track_id = this.track.trackId;\n this.source = this.track.source!;\n this.sampleWindowSize = sampleWindowSize;\n }\n\n push(stat: TempPublishStats) {\n this.tempStats.push(stat);\n\n if (this.shouldCreateSample()) {\n this.samples.push(this.createSample());\n this.tempStats.length = 0;\n }\n }\n\n toAnalytics(): TrackAnalytics {\n return {\n track_id: this.track_id,\n ssrc: this.ssrc,\n source: this.source,\n rid: this.rid,\n samples: this.samples,\n };\n }\n\n private createSample(): BaseSample | VideoSample {\n const latestStat = this.getLatestStat();\n\n const qualityLimitationDurations = latestStat.qualityLimitationDurations;\n const total_quality_limitation = qualityLimitationDurations && {\n bandwidth_sec: qualityLimitationDurations.bandwidth,\n cpu_sec: qualityLimitationDurations.cpu,\n other_sec: qualityLimitationDurations.other,\n };\n\n const resolution = latestStat.frameHeight\n ? {\n height_px: this.getLatestStat().frameHeight,\n width_px: this.getLatestStat().frameWidth,\n }\n : undefined;\n const avg_jitter = this.calculateAverage('jitter', false);\n const avg_jitter_ms = avg_jitter ? Math.round(avg_jitter * 1000) : undefined;\n\n const avg_round_trip_time = this.calculateAverage('roundTripTime', false);\n const avg_round_trip_time_ms = avg_round_trip_time ? Math.round(avg_round_trip_time * 1000) : undefined;\n\n return removeUndefinedFromObject({\n timestamp: Date.now(),\n avg_available_outgoing_bitrate_bps: this.calculateAverage('availableOutgoingBitrate'),\n avg_bitrate_bps: this.calculateAverage('bitrate'),\n avg_fps: this.calculateAverage('framesPerSecond'),\n total_packets_lost: this.calculateDifferenceForSample('packetsLost'),\n total_packets_sent: this.calculateDifferenceForSample('packetsSent'),\n total_packet_sent_delay_sec: parseFloat(this.calculateDifferenceForSample('totalPacketSendDelay').toFixed(4)),\n total_fir_count: this.calculateDifferenceForSample('firCount'),\n total_pli_count: this.calculateDifferenceForSample('pliCount'),\n total_nack_count: this.calculateDifferenceForSample('nackCount'),\n avg_jitter_ms,\n avg_round_trip_time_ms,\n total_quality_limitation,\n resolution,\n });\n }\n\n private getLatestStat() {\n return this.tempStats[this.tempStats.length - 1];\n }\n\n private shouldCreateSample() {\n const length = this.tempStats.length;\n const newStat = this.tempStats[length - 1];\n const prevStat = this.tempStats[length - 2];\n\n return (\n length === PUBLISH_STATS_SAMPLE_WINDOW ||\n hasEnabledStateChanged(newStat, prevStat) ||\n (newStat.kind === 'video' && hasResolutionChanged(newStat, prevStat))\n );\n }\n\n private calculateSum(key: keyof TempPublishStats) {\n const checkStat = this.getLatestStat()[key];\n if (typeof checkStat !== 'number') {\n return;\n }\n return this.tempStats.reduce((partialSum, stat) => {\n return partialSum + ((stat[key] || 0) as number);\n }, 0);\n }\n\n private calculateAverage(key: keyof TempPublishStats, round = true) {\n const sum = this.calculateSum(key);\n const avg = sum !== undefined ? sum / this.tempStats.length : undefined;\n return avg ? (round ? Math.round(avg) : avg) : undefined;\n }\n\n private calculateDifferenceForSample(key: keyof TempPublishStats) {\n const firstValue = Number(this.tempStats[0][key]) || 0;\n const latestValue = Number(this.getLatestStat()[key]) || 0;\n\n return latestValue - firstValue;\n }\n}\n\nconst hasResolutionChanged = (newStat: TempPublishStats, prevStat: TempPublishStats) =>\n newStat && prevStat && (newStat.frameWidth !== prevStat.frameWidth || newStat.frameHeight !== prevStat.frameHeight);\n\nconst hasEnabledStateChanged = (newStat: TempPublishStats, prevStat: TempPublishStats) =>\n newStat && prevStat && newStat.enabled !== prevStat.enabled;\n\nconst removeUndefinedFromObject = <T extends Record<string, any>>(data: T) => {\n return Object.entries(data)\n .filter(([, value]) => value !== undefined)\n .reduce((obj, [key, value]) => {\n obj[key as keyof T] = value;\n return obj;\n }, {} as T);\n};\n", "import { ANALYTICS_BUFFER_SIZE } from '../../utils/constants';\nimport { LocalStorage } from '../../utils/local-storage';\nimport { Queue } from '../../utils/queue';\nimport AnalyticsEvent from '../AnalyticsEvent';\n\nexport class LocalStorageEvents extends Queue<AnalyticsEvent> {\n private localStorage = new LocalStorage<AnalyticsEvent[]>('hms-analytics');\n\n constructor() {\n super(ANALYTICS_BUFFER_SIZE);\n // @TODO: Currently we don't send failed events of old sessions. So reset localstorage for every session.\n // Once support for failed events from old sessions is added, remove clear and init queue from localstorage.\n this.localStorage.clear();\n this.initLocalStorageQueue();\n }\n\n enqueue(event: AnalyticsEvent) {\n super.enqueue(event);\n this.localStorage.set(this.storage);\n }\n\n dequeue() {\n const removedEvent = super.dequeue();\n this.localStorage.set(this.storage);\n return removedEvent;\n }\n\n private initLocalStorageQueue() {\n this.localStorage.get()?.forEach(event => {\n const eventInstance = new AnalyticsEvent(event);\n super.enqueue(eventInstance);\n });\n }\n}\n", "import AnalyticsEvent from './AnalyticsEvent';\nimport { HTTPAnalyticsTransport } from './HTTPAnalyticsTransport';\nimport { IAnalyticsTransportProvider } from './IAnalyticsTransportProvider';\nimport HMSLogger from '../utils/logger';\nimport { Queue } from '../utils/queue';\n\nexport abstract class AnalyticsTransport {\n abstract transportProvider: IAnalyticsTransportProvider;\n abstract failedEvents: Queue<AnalyticsEvent>;\n private readonly TAG = '[AnalyticsTransport]';\n\n sendEvent(event: AnalyticsEvent) {\n try {\n this.sendSingleEvent(event);\n this.flushFailedEvents();\n } catch (error) {\n HMSLogger.w(this.TAG, 'sendEvent failed', error);\n }\n }\n\n flushFailedEvents(currentPeerId?: string) {\n try {\n HMSLogger.d(this.TAG, 'Flushing failed events', this.failedEvents);\n while (this.failedEvents.size() > 0) {\n const event = this.failedEvents.dequeue();\n if (event) {\n const isEventFromCurrentPeer = event.metadata?.peer.peer_id === currentPeerId;\n if (isEventFromCurrentPeer || !event.metadata.peer.peer_id) {\n this.sendSingleEvent(event);\n } else {\n HTTPAnalyticsTransport.sendEvent(event);\n }\n }\n }\n } catch (error) {\n HMSLogger.w(this.TAG, 'flushFailedEvents failed', error);\n }\n }\n\n private sendSingleEvent(event: AnalyticsEvent) {\n try {\n this.transportProvider.sendEvent(event);\n HMSLogger.d(this.TAG, 'Sent event', event.name, event);\n } catch (error) {\n HMSLogger.w(this.TAG, `${this.transportProvider.TAG}.sendEvent failed, adding to local storage events`, {\n event,\n error,\n });\n this.failedEvents.enqueue(event);\n throw error;\n }\n }\n}\n", "import { LocalStorageEvents } from './LocalStoageEvents';\nimport { ISignal } from '../../signal/ISignal';\nimport { AnalyticsTransport } from '../AnalyticsTransport';\n\nexport class SignalAnalyticsTransport extends AnalyticsTransport {\n failedEvents = new LocalStorageEvents();\n\n constructor(public transportProvider: ISignal) {\n super();\n }\n}\n", "// Sent in trickle messages as target - biz understands only 0 and 1\nexport enum HMSConnectionRole {\n Publish = 0,\n Subscribe = 1,\n}\n\nexport interface HMSTrickle {\n candidate: RTCIceCandidateInit;\n target: HMSConnectionRole;\n}\n", "import * as sdpTransform from 'sdp-transform';\nimport { isPresent } from './validations';\nimport { TrackState } from '../notification-manager';\n\n/**\n * @DISCUSS: Should we have a wrapper over RTCSessionDescriptionInit(SDP) and have these methods in it?\n */\n\nexport function fixMsid(desc: RTCSessionDescriptionInit, tracks?: Map<string, TrackState>): RTCSessionDescriptionInit {\n const parsedSdp = sdpTransform.parse(desc.sdp!);\n\n if (!parsedSdp.origin?.username.startsWith('mozilla')) {\n // This isn't firefox, so we return the original offer without doing anything\n return desc;\n }\n\n const mediaTracks = tracks ? Array.from(tracks.values()) : [];\n\n parsedSdp.media.forEach(m => {\n const streamId = m.msid?.split(' ')[0];\n // check for both type and streamid as both video and screenshare have same type but different stream_id\n const trackId = mediaTracks.find(val => val.type === m.type && val.stream_id === streamId)?.track_id;\n if (trackId) {\n m.msid = m.msid?.replace(/\\s(.+)/, ` ${trackId}`);\n }\n });\n\n return { type: desc.type, sdp: sdpTransform.write(parsedSdp) };\n}\n\n/**\n * Get the track ID from the SDP using the transceiver's mid from RTCTrackEvent\n * @TODO: This could take more processing time in a large room and when the SDP is big.\n * Consider using this for Firefox only?\n */\nexport function getSdpTrackIdForMid(\n desc?: RTCSessionDescriptionInit | null,\n mid?: RTCRtpTransceiver['mid'],\n): string | undefined {\n if (!desc?.sdp || !mid) {\n return undefined;\n }\n const parsedSdp = sdpTransform.parse(desc.sdp);\n const trackSection = parsedSdp.media.find(media => isPresent(media.mid) && parseInt(media.mid!) === parseInt(mid));\n const trackId = trackSection?.msid?.split(' ')[1];\n return trackId;\n}\n\nexport function enableOpusDtx(desc: RTCSessionDescriptionInit): RTCSessionDescriptionInit {\n if (desc.sdp!.includes('usedtx=1')) {\n return desc;\n }\n\n return { type: desc.type, sdp: desc.sdp!.replace('useinbandfec=1', 'useinbandfec=1;usedtx=1') };\n}\n", "import { HMSConnectionRole } from './model';\nimport { ErrorFactory } from '../error/ErrorFactory';\nimport { HMSAction } from '../error/HMSAction';\nimport { HMSLocalTrack, HMSLocalVideoTrack } from '../media/tracks';\nimport { TrackState } from '../notification-manager';\nimport { ISignal } from '../signal/ISignal';\nimport HMSLogger from '../utils/logger';\nimport { enableOpusDtx, fixMsid } from '../utils/session-description';\n\nconst TAG = '[HMSConnection]';\ninterface RTCIceCandidatePair {\n local: RTCIceCandidate;\n remote: RTCIceCandidate;\n}\n\nexport default abstract class HMSConnection {\n readonly role: HMSConnectionRole;\n protected readonly signal: ISignal;\n\n abstract readonly nativeConnection: RTCPeerConnection;\n /**\n * We keep a list of pending IceCandidates received\n * from the signalling server. When the peer-connection\n * is initialized we call [addIceCandidate] for each.\n *\n * WARN:\n * - [HMSPublishConnection] keeps the complete list of candidates (for\n * ice-connection failed/disconnect) forever.\n * - [HMSSubscribeConnection] clears this list as soon as we call [addIceCandidate]\n */\n readonly candidates = new Array<RTCIceCandidateInit>();\n\n selectedCandidatePair?: RTCIceCandidatePair;\n\n protected constructor(role: HMSConnectionRole, signal: ISignal) {\n this.role = role;\n this.signal = signal;\n }\n\n public get iceConnectionState(): RTCIceConnectionState {\n return this.nativeConnection.iceConnectionState;\n }\n\n public get connectionState(): RTCPeerConnectionState {\n return this.nativeConnection.connectionState;\n }\n\n private get action() {\n return this.role === HMSConnectionRole.Publish ? HMSAction.PUBLISH : HMSAction.SUBSCRIBE;\n }\n\n addTransceiver(track: MediaStreamTrack, init: RTCRtpTransceiverInit): RTCRtpTransceiver {\n return this.nativeConnection.addTransceiver(track, init);\n }\n\n async createOffer(tracks?: Map<string, TrackState>, options?: RTCOfferOptions): Promise<RTCSessionDescriptionInit> {\n try {\n const offer = await this.nativeConnection.createOffer(options);\n HMSLogger.d(TAG, `[role=${this.role}] createOffer offer=${JSON.stringify(offer, null, 1)}`);\n return enableOpusDtx(fixMsid(offer, tracks));\n } catch (error) {\n throw ErrorFactory.WebrtcErrors.CreateOfferFailed(this.action, (error as Error).message);\n }\n }\n\n async createAnswer(options: RTCOfferOptions | undefined = undefined): Promise<RTCSessionDescriptionInit> {\n try {\n const answer = await this.nativeConnection.createAnswer(options);\n HMSLogger.d(TAG, `[role=${this.role}] createAnswer answer=${JSON.stringify(answer, null, 1)}`);\n return answer;\n } catch (error) {\n throw ErrorFactory.WebrtcErrors.CreateAnswerFailed(this.action, (error as Error).message);\n }\n }\n\n async setLocalDescription(description: RTCSessionDescriptionInit): Promise<void> {\n try {\n HMSLogger.d(TAG, `[role=${this.role}] setLocalDescription description=${JSON.stringify(description, null, 1)}`);\n await this.nativeConnection.setLocalDescription(description);\n } catch (error) {\n throw ErrorFactory.WebrtcErrors.SetLocalDescriptionFailed(this.action, (error as Error).message);\n }\n }\n\n async setRemoteDescription(description: RTCSessionDescriptionInit): Promise<void> {\n try {\n HMSLogger.d(TAG, `[role=${this.role}] setRemoteDescription description=${JSON.stringify(description, null, 1)}`);\n await this.nativeConnection.setRemoteDescription(description);\n } catch (error) {\n throw ErrorFactory.WebrtcErrors.SetRemoteDescriptionFailed(this.action, (error as Error).message);\n }\n }\n\n async addIceCandidate(candidate: RTCIceCandidateInit): Promise<void> {\n if (this.nativeConnection.signalingState === 'closed') {\n HMSLogger.d(TAG, `[role=${this.role}] addIceCandidate signalling state closed`);\n return;\n }\n HMSLogger.d(TAG, `[role=${this.role}] addIceCandidate candidate=${JSON.stringify(candidate, null, 1)}`);\n await this.nativeConnection.addIceCandidate(candidate);\n }\n\n public get remoteDescription(): RTCSessionDescription | null {\n return this.nativeConnection.remoteDescription;\n }\n\n getSenders(): Array<RTCRtpSender> {\n return this.nativeConnection.getSenders();\n }\n\n logSelectedIceCandidatePairs() {\n /**\n * for the very first peer in the room we don't have any subscribe ice candidates\n * because the peer hasn't subscribed to anything.\n *\n * For all peers joining after this peer, we have published and subscribed at the time of join itself\n * so we're able to log both publish and subscribe ice candidates.\n * Added try catch for the whole section as the getSenders and getReceivers is throwing errors in load test\n */\n try {\n const transmitters = this.role === HMSConnectionRole.Publish ? this.getSenders() : this.getReceivers();\n\n transmitters.forEach(transmitter => {\n const kindOfTrack = transmitter.track?.kind;\n if (transmitter.transport) {\n const iceTransport = transmitter.transport.iceTransport;\n\n const logSelectedCandidate = () => {\n // @ts-expect-error\n if (typeof iceTransport.getSelectedCandidatePair === 'function') {\n // @ts-expect-error\n this.selectedCandidatePair = iceTransport.getSelectedCandidatePair();\n HMSLogger.d(\n TAG,\n `${HMSConnectionRole[this.role]} connection`,\n `selected ${kindOfTrack || 'unknown'} candidate pair`,\n JSON.stringify(this.selectedCandidatePair, null, 2),\n );\n }\n };\n\n // @ts-expect-error\n if (typeof iceTransport.onselectedcandidatepairchange === 'function') {\n // @ts-expect-error\n iceTransport.onselectedcandidatepairchange = logSelectedCandidate;\n }\n logSelectedCandidate();\n }\n });\n } catch (error) {\n HMSLogger.w(\n TAG,\n `Error in logging selected ice candidate pair for ${HMSConnectionRole[this.role]} connection`,\n error,\n );\n }\n }\n\n removeTrack(sender: RTCRtpSender) {\n if (this.nativeConnection.signalingState !== 'closed') {\n this.nativeConnection.removeTrack(sender);\n }\n }\n\n async setMaxBitrateAndFramerate(track: HMSLocalTrack) {\n const maxBitrate = track.settings.maxBitrate;\n const maxFramerate = track instanceof HMSLocalVideoTrack && track.settings.maxFramerate;\n const sender = this.getSenders().find(s => s?.track?.id === track.getTrackIDBeingSent());\n\n if (sender) {\n const params = sender.getParameters();\n if (params.encodings.length > 0) {\n if (maxBitrate) {\n params.encodings[0].maxBitrate = maxBitrate * 1000;\n }\n if (maxFramerate) {\n // @ts-ignore\n params.encodings[0].maxFramerate = maxFramerate;\n }\n }\n await sender.setParameters(params);\n } else {\n HMSLogger.w(\n TAG,\n `no sender found to setMaxBitrate for track - ${track.trackId}, sentTrackId - ${track.getTrackIDBeingSent()}`,\n );\n }\n }\n\n async getStats() {\n return await this.nativeConnection.getStats();\n }\n\n async close() {\n this.nativeConnection.close();\n }\n\n private getReceivers() {\n return this.nativeConnection.getReceivers();\n }\n}\n", "import { IPublishConnectionObserver } from './IPublishConnectionObserver';\nimport { ISignal } from '../../signal/ISignal';\nimport { API_DATA_CHANNEL } from '../../utils/constants';\nimport HMSLogger from '../../utils/logger';\nimport HMSConnection from '../HMSConnection';\nimport { HMSConnectionRole } from '../model';\n\nexport default class HMSPublishConnection extends HMSConnection {\n private readonly TAG = '[HMSPublishConnection]';\n private readonly observer: IPublishConnectionObserver;\n readonly nativeConnection: RTCPeerConnection;\n\n constructor(signal: ISignal, config: RTCConfiguration, observer: IPublishConnectionObserver) {\n super(HMSConnectionRole.Publish, signal);\n this.observer = observer;\n\n this.nativeConnection = new RTCPeerConnection(config);\n this.nativeConnection.createDataChannel(API_DATA_CHANNEL, {\n protocol: 'SCTP',\n });\n\n this.nativeConnection.onicecandidate = ({ candidate }) => {\n if (candidate) {\n signal.trickle(this.role, candidate);\n }\n };\n\n this.nativeConnection.oniceconnectionstatechange = () => {\n this.observer.onIceConnectionChange(this.nativeConnection.iceConnectionState);\n };\n\n // @TODO(eswar): Remove this. Use iceconnectionstate change with interval and threshold.\n this.nativeConnection.onconnectionstatechange = () => {\n this.observer.onConnectionStateChange(this.nativeConnection.connectionState);\n };\n }\n\n initAfterJoin() {\n this.nativeConnection.onnegotiationneeded = async () => {\n HMSLogger.d(this.TAG, `onnegotiationneeded`);\n await this.observer.onRenegotiationNeeded();\n };\n }\n}\n", "import EventEmitter from 'eventemitter2';\nimport { v4 as uuid } from 'uuid';\nimport ISubscribeConnectionObserver from './ISubscribeConnectionObserver';\nimport { HMSRemoteStream, HMSSimulcastLayer } from '../../internal';\nimport { HMSRemoteAudioTrack } from '../../media/tracks/HMSRemoteAudioTrack';\nimport { HMSRemoteVideoTrack } from '../../media/tracks/HMSRemoteVideoTrack';\nimport { InitFlags } from '../../signal/init/models';\nimport { ISignal } from '../../signal/ISignal';\nimport { API_DATA_CHANNEL } from '../../utils/constants';\nimport HMSLogger from '../../utils/logger';\nimport { getSdpTrackIdForMid } from '../../utils/session-description';\nimport { sleep } from '../../utils/timer-utils';\nimport { PreferAudioLayerParams, PreferLayerResponse, PreferVideoLayerParams } from '../channel-messages';\nimport HMSConnection from '../HMSConnection';\nimport HMSDataChannel from '../HMSDataChannel';\nimport { HMSConnectionRole } from '../model';\n\nexport default class HMSSubscribeConnection extends HMSConnection {\n private readonly TAG = '[HMSSubscribeConnection]';\n private readonly remoteStreams = new Map<string, HMSRemoteStream>();\n private readonly observer: ISubscribeConnectionObserver;\n private readonly MAX_RETRIES = 3;\n\n readonly nativeConnection: RTCPeerConnection;\n\n private pendingMessageQueue: string[] = [];\n\n private apiChannel?: HMSDataChannel;\n private eventEmitter = new EventEmitter({ maxListeners: 60 });\n\n private initNativeConnectionCallbacks() {\n this.nativeConnection.oniceconnectionstatechange = () => {\n this.observer.onIceConnectionChange(this.nativeConnection.iceConnectionState);\n };\n\n // @TODO(eswar): Remove this. Use iceconnectionstate change with interval and threshold.\n this.nativeConnection.onconnectionstatechange = () => {\n this.observer.onConnectionStateChange(this.nativeConnection.connectionState);\n };\n\n this.nativeConnection.ondatachannel = e => {\n if (e.channel.label !== API_DATA_CHANNEL) {\n // TODO: this.observer.onDataChannel(e.channel);\n return;\n }\n\n this.apiChannel = new HMSDataChannel(\n e.channel,\n {\n onMessage: (value: string) => {\n this.eventEmitter.emit('message', value);\n this.observer.onApiChannelMessage(value);\n },\n },\n `role=${this.role}`,\n );\n\n e.channel.onopen = this.handlePendingApiMessages;\n };\n\n this.nativeConnection.onicecandidate = e => {\n if (e.candidate !== null) {\n this.signal.trickle(this.role, e.candidate);\n }\n };\n\n this.nativeConnection.ontrack = e => {\n const stream = e.streams[0];\n const streamId = stream.id;\n\n if (!this.remoteStreams.has(streamId)) {\n const remote = new HMSRemoteStream(stream, this);\n this.remoteStreams.set(streamId, remote);\n }\n\n stream.addEventListener('removetrack', (ev: MediaStreamTrackEvent) => {\n if (ev.track.id !== e.track.id) {\n return;\n }\n /*\n * this match has to be with nativetrack.id instead of track.trackId as the latter refers to sdp track id for\n * ease of correlating update messages coming from the backend. The two track ids are usually the same, but\n * can be different for some browsers. checkout sdptrackid field in HMSTrack for more details.\n */\n const toRemoveTrackIdx = remote.tracks.findIndex(\n track => track.nativeTrack.id === ev.track.id && e.transceiver.mid === track.transceiver?.mid,\n );\n if (toRemoveTrackIdx >= 0) {\n const toRemoveTrack = remote.tracks[toRemoveTrackIdx];\n this.observer.onTrackRemove(toRemoveTrack);\n remote.tracks.splice(toRemoveTrackIdx, 1);\n // If the length becomes 0 we assume that stream is removed entirely\n if (remote.tracks.length === 0) {\n this.remoteStreams.delete(streamId);\n }\n }\n });\n\n const remote = this.remoteStreams.get(streamId)!;\n const TrackCls = e.track.kind === 'audio' ? HMSRemoteAudioTrack : HMSRemoteVideoTrack;\n const track = new TrackCls(remote, e.track);\n track.transceiver = e.transceiver;\n const trackId = getSdpTrackIdForMid(this.remoteDescription, e.transceiver?.mid);\n trackId && track.setSdpTrackId(trackId);\n remote.tracks.push(track);\n this.observer.onTrackAdd(track);\n };\n }\n\n constructor(\n signal: ISignal,\n config: RTCConfiguration,\n private isFlagEnabled: (flag: InitFlags) => boolean,\n observer: ISubscribeConnectionObserver,\n ) {\n super(HMSConnectionRole.Subscribe, signal);\n this.observer = observer;\n\n this.nativeConnection = new RTCPeerConnection(config);\n this.initNativeConnectionCallbacks();\n }\n\n sendOverApiDataChannel(message: string) {\n if (this.apiChannel && this.apiChannel.readyState === 'open') {\n this.apiChannel.send(message);\n } else {\n HMSLogger.w(this.TAG, `API Data channel not ${this.apiChannel ? 'open' : 'present'}, queueing`, message);\n this.pendingMessageQueue.push(message);\n }\n }\n\n async sendOverApiDataChannelWithResponse<T extends PreferAudioLayerParams | PreferVideoLayerParams>(\n message: T,\n requestId?: string,\n ): Promise<PreferLayerResponse> {\n const id = uuid();\n if (message.method === 'prefer-video-track-state') {\n const disableAutoUnsubscribe = this.isFlagEnabled(InitFlags.FLAG_DISABLE_VIDEO_TRACK_AUTO_UNSUBSCRIBE);\n if (disableAutoUnsubscribe && message.params.max_spatial_layer === HMSSimulcastLayer.NONE) {\n HMSLogger.d(this.TAG, 'video auto unsubscribe is disabled, request is ignored');\n return { id } as PreferLayerResponse;\n }\n }\n const request = JSON.stringify({\n id: requestId || id,\n jsonrpc: '2.0',\n ...message,\n });\n return this.sendMessage(request, id);\n }\n\n async close() {\n await super.close();\n this.apiChannel?.close();\n }\n\n private handlePendingApiMessages = () => {\n this.eventEmitter.emit('open', true);\n if (this.pendingMessageQueue.length > 0) {\n HMSLogger.d(this.TAG, 'Found pending message queue, sending messages');\n this.pendingMessageQueue.forEach(msg => this.sendOverApiDataChannel(msg));\n this.pendingMessageQueue.length = 0;\n }\n };\n\n // eslint-disable-next-line complexity\n private sendMessage = async (request: string, requestId: string): Promise<PreferLayerResponse> => {\n if (this.apiChannel?.readyState !== 'open') {\n await this.eventEmitter.waitFor('open');\n }\n let response: PreferLayerResponse;\n for (let i = 0; i < this.MAX_RETRIES; i++) {\n this.apiChannel!.send(request);\n response = await this.waitForResponse(requestId);\n const error = response.error;\n if (error) {\n // Don't retry or do anything, track is already removed\n if (error.code === 404) {\n HMSLogger.d(this.TAG, `Track not found ${requestId}`, { request, try: i + 1, error });\n break;\n }\n HMSLogger.d(this.TAG, `Failed sending ${requestId}`, { request, try: i + 1, error });\n const shouldRetry = error.code / 100 === 5 || error.code === 429;\n if (!shouldRetry) {\n throw Error(`code=${error.code}, message=${error.message}`);\n }\n const delay = (2 + Math.random() * 2) * 1000;\n await sleep(delay);\n } else {\n break;\n }\n }\n return response!;\n };\n\n private waitForResponse = async (requestId: string): Promise<PreferLayerResponse> => {\n const res = await this.eventEmitter.waitFor('message', function (value) {\n return value.includes(requestId);\n });\n const response = JSON.parse(res[0] as string);\n HMSLogger.d(this.TAG, `response for ${requestId} -`, JSON.stringify(response, null, 2));\n return response;\n };\n}\n", "import HMSLogger from '../utils/logger';\n\nexport interface DataChannelObserver {\n onMessage(value: string): void;\n}\n\nexport default class HMSDataChannel {\n private readonly TAG = '[HMSDataChannel]';\n private readonly nativeChannel: RTCDataChannel;\n private readonly observer: DataChannelObserver;\n private readonly metadata: string;\n\n public get id() {\n return this.nativeChannel.id;\n }\n\n public get label() {\n return this.nativeChannel.label;\n }\n\n public get readyState() {\n return this.nativeChannel.readyState;\n }\n\n constructor(nativeChannel: RTCDataChannel, observer: DataChannelObserver, metadata = '') {\n this.nativeChannel = nativeChannel;\n this.observer = observer;\n this.metadata = metadata;\n\n nativeChannel.onmessage = e => {\n // HMSLogger.d(this.TAG, `[${this.metadata}] onMessage: label=${this.label}, message=${e.data}`);\n this.observer.onMessage(e.data);\n };\n }\n\n send(message: string) {\n HMSLogger.d(this.TAG, `[${this.metadata}] Sending [size=${message.length}] message=${message}`);\n this.nativeChannel.send(message);\n }\n\n close() {\n this.nativeChannel.close();\n }\n}\n", "import { InitConfig } from './models';\nimport { ErrorFactory } from '../../error/ErrorFactory';\nimport { HMSAction } from '../../error/HMSAction';\nimport HMSLogger from '../../utils/logger';\n\nconst TAG = '[InitService]';\nexport default class InitService {\n private static handleError(response: Response, body: { code: number; message: string }) {\n switch (response.status) {\n case 404:\n throw ErrorFactory.APIErrors.EndpointUnreachable(HMSAction.INIT, body.message || response.statusText);\n case 200:\n break;\n default:\n throw ErrorFactory.APIErrors.ServerErrors(\n body.code || response.status,\n HMSAction.INIT,\n body.message || response?.statusText,\n );\n }\n }\n\n static async fetchInitConfig({\n token,\n peerId,\n userAgent,\n initEndpoint = 'https://prod-init.100ms.live',\n region = '',\n }: {\n token: string;\n peerId: string;\n userAgent: string;\n initEndpoint?: string;\n region?: string;\n }): Promise<InitConfig> {\n HMSLogger.d(TAG, `fetchInitConfig: initEndpoint=${initEndpoint} token=${token} peerId=${peerId} region=${region} `);\n const url = getUrl(initEndpoint, peerId, userAgent, region);\n try {\n const response = await fetch(url, {\n headers: {\n Authorization: `Bearer ${token}`,\n },\n });\n try {\n const config = await response.clone().json();\n this.handleError(response, config);\n HMSLogger.d(TAG, `config is ${JSON.stringify(config, null, 2)}`);\n return transformInitConfig(config);\n } catch (err) {\n const text = await response.text();\n HMSLogger.e(TAG, 'json error', (err as Error).message, text);\n throw ErrorFactory.APIErrors.ServerErrors(response.status, HMSAction.INIT, text);\n }\n } catch (err) {\n const error = err as Error;\n if (['Failed to fetch', 'NetworkError', 'ECONNRESET'].some(message => error.message.includes(message))) {\n throw ErrorFactory.APIErrors.EndpointUnreachable(HMSAction.INIT, error.message);\n }\n throw error;\n }\n }\n}\n\nexport function getUrl(endpoint: string, peerId: string, userAgent: string, region?: string) {\n try {\n const url = new URL('/init', endpoint);\n\n if (region && region.trim().length > 0) {\n url.searchParams.set('region', region.trim());\n }\n url.searchParams.set('peer_id', peerId);\n url.searchParams.set('user_agent_v2', userAgent);\n return url.toString();\n } catch (err) {\n const error = err as Error;\n HMSLogger.e(TAG, error.name, error.message);\n throw error;\n }\n}\n\nexport function transformInitConfig(config: any): InitConfig {\n return {\n ...config,\n rtcConfiguration: { ...config.rtcConfiguration, iceServers: config.rtcConfiguration?.ice_servers },\n };\n}\n", "import { v4 as uuid } from 'uuid';\nimport { convertSignalMethodtoErrorAction, HMSSignalMethod, JsonRpcRequest, JsonRpcResponse } from './models';\nimport AnalyticsEvent from '../../analytics/AnalyticsEvent';\nimport { HMSConnectionRole, HMSTrickle } from '../../connection/model';\nimport { ErrorFactory } from '../../error/ErrorFactory';\nimport { HMSAction } from '../../error/HMSAction';\nimport { HMSException } from '../../error/HMSException';\nimport Message from '../../sdk/models/HMSMessage';\nimport {\n DEFAULT_SIGNAL_PING_INTERVAL,\n DEFAULT_SIGNAL_PING_TIMEOUT,\n PONG_RESPONSE_TIMES_SIZE,\n} from '../../utils/constants';\nimport HMSLogger from '../../utils/logger';\nimport { PromiseCallbacks } from '../../utils/promise';\nimport { Queue } from '../../utils/queue';\nimport { isPageHidden } from '../../utils/support';\nimport { sleep } from '../../utils/timer-utils';\nimport {\n AcceptRoleChangeParams,\n BroadcastResponse,\n GetSessionMetadataResponse,\n HLSRequestParams,\n HLSTimedMetadataParams,\n MultiTrackUpdateRequestParams,\n PollInfoGetParams,\n PollInfoGetResponse,\n PollInfoSetParams,\n PollInfoSetResponse,\n PollListParams,\n PollListResponse,\n PollQuestionsGetParams,\n PollQuestionsGetResponse,\n PollQuestionsSetParams,\n PollQuestionsSetResponse,\n PollResponseSetParams,\n PollResponseSetResponse,\n PollResponsesGetParams,\n PollResponsesGetResponse,\n PollResultParams,\n PollResultResponse,\n PollStartParams,\n PollStartResponse,\n PollStopParams,\n PollStopResponse,\n RemovePeerRequest,\n RequestForBulkRoleChangeParams,\n RequestForRoleChangeParams,\n SetSessionMetadataParams,\n SetSessionMetadataResponse,\n StartRTMPOrRecordingRequestParams,\n Track,\n TrackUpdateRequestParams,\n UpdatePeerRequestParams,\n} from '../interfaces';\nimport { ISignal } from '../ISignal';\nimport { ISignalEventsObserver } from '../ISignalEventsObserver';\n\nexport default class JsonRpcSignal implements ISignal {\n readonly TAG = '[SIGNAL]: ';\n readonly observer: ISignalEventsObserver;\n readonly pongResponseTimes = new Queue<number>(PONG_RESPONSE_TIMES_SIZE);\n\n /**\n * Sometimes before [join] is completed, there could be a lot of trickles\n * Sending [HMSTrickle]` before [join] web socket message leads to\n * error: [500] no rtc transport exists for this Peer\n *\n * We keep a list of pending trickles and send them immediately after [join]\n * is done.\n */\n private isJoinCompleted = false;\n private pendingTrickle: Array<HMSTrickle> = [];\n\n private socket: WebSocket | null = null;\n\n private callbacks = new Map<string, PromiseCallbacks<string, { method: HMSSignalMethod }>>();\n\n private _isConnected = false;\n private id = 0;\n\n private onCloseHandler: (event: CloseEvent) => void = () => {};\n\n public get isConnected(): boolean {\n return this._isConnected;\n }\n\n public setIsConnected(newValue: boolean, reason = '') {\n HMSLogger.d(this.TAG, `isConnected set id: ${this.id}, oldValue: ${this._isConnected}, newValue: ${newValue}`);\n if (this._isConnected === newValue) {\n return;\n }\n\n if (this._isConnected && !newValue) {\n // went offline\n this._isConnected = newValue;\n this.rejectPendingCalls(reason);\n this.observer.onOffline(reason);\n } else if (!this._isConnected && newValue) {\n // went online\n this._isConnected = newValue;\n this.observer.onOnline();\n }\n }\n\n constructor(observer: ISignalEventsObserver) {\n this.observer = observer;\n window.addEventListener('offline', this.offlineListener);\n window.addEventListener('online', this.onlineListener);\n\n this.onMessageHandler = this.onMessageHandler.bind(this);\n }\n\n getPongResponseTimes() {\n return this.pongResponseTimes.toList();\n }\n\n private async internalCall<T>(method: string, params: any): Promise<T> {\n const id = uuid();\n const message = { method, params, id, jsonrpc: '2.0' } as JsonRpcRequest;\n\n this.socket?.send(JSON.stringify(message));\n\n try {\n const response = await new Promise<any>((resolve, reject) => {\n this.callbacks.set(id, { resolve, reject, metadata: { method: method as HMSSignalMethod } });\n });\n\n return response;\n } catch (ex) {\n if (ex instanceof HMSException) {\n throw ex;\n }\n\n const error = ex as JsonRpcResponse['error'];\n throw ErrorFactory.WebsocketMethodErrors.ServerErrors(\n Number(error.code),\n convertSignalMethodtoErrorAction(method as HMSSignalMethod),\n error.message,\n );\n }\n }\n\n private notify(method: string, params: any) {\n const message = { method, params };\n\n if (this.socket?.readyState === WebSocket.OPEN) {\n this.socket?.send(JSON.stringify(message));\n }\n }\n\n open(uri: string): Promise<void> {\n return new Promise((resolve, reject) => {\n let promiseSettled = false;\n // cleanup\n if (this.socket) {\n this.socket.close();\n this.socket.removeEventListener('close', this.onCloseHandler);\n this.socket.removeEventListener('message', this.onMessageHandler);\n }\n\n this.socket = new WebSocket(uri); // @DISCUSS: Inject WebSocket as a dependency so that it can be easier to mock and test\n\n const errorListener = () => {\n /**\n * there was an error received from websocket leading to disconnection, this can happen either if server\n * disconnects the websocket for some reason, there is a network disconnect or a firewall/antivirus on user's\n * device is breaking the websocket connecting(which can happen even after a successful connect).\n */\n HMSLogger.e(this.TAG, 'Error from websocket');\n promiseSettled = true;\n // above error does not contain any description hence not sent here\n reject(\n ErrorFactory.WebSocketConnectionErrors.FailedToConnect(HMSAction.JOIN, `Error opening websocket connection`),\n );\n };\n\n this.onCloseHandler = (event: CloseEvent) => {\n HMSLogger.w(`Websocket closed code=${event.code}`);\n if (promiseSettled) {\n this.setIsConnected(false, `code: ${event.code}${event.code !== 1000 ? ', unexpected websocket close' : ''}`);\n } else {\n promiseSettled = true;\n reject(\n ErrorFactory.WebSocketConnectionErrors.AbnormalClose(\n HMSAction.JOIN,\n `Error opening websocket connection - websocket closed unexpectedly with code=${event.code}`,\n ),\n );\n }\n };\n\n this.socket.addEventListener('error', errorListener);\n\n const openHandler = () => {\n promiseSettled = true;\n resolve();\n this.setIsConnected(true);\n this.id++;\n this.socket?.removeEventListener('open', openHandler);\n this.socket?.removeEventListener('error', errorListener);\n this.pingPongLoop(this.id);\n };\n\n this.socket.addEventListener('open', openHandler);\n this.socket.addEventListener('close', this.onCloseHandler);\n this.socket.addEventListener('message', this.onMessageHandler);\n });\n }\n\n async close(): Promise<void> {\n window.removeEventListener('offline', this.offlineListener);\n window.removeEventListener('online', this.onlineListener);\n\n // For `1000` Refer: https://tools.ietf.org/html/rfc6455#section-7.4.1\n if (this.socket) {\n this.socket.close(1000, 'Normal Close');\n this.setIsConnected(false, 'code: 1000, normal websocket close');\n this.socket.removeEventListener('close', this.onCloseHandler);\n this.socket.removeEventListener('message', this.onMessageHandler);\n } else {\n this.setIsConnected(false, 'websocket not connected yet');\n }\n }\n\n async join(\n name: string,\n data: string,\n disableVidAutoSub: boolean,\n serverSubDegrade: boolean,\n simulcast: boolean,\n onDemandTracks: boolean,\n offer?: RTCSessionDescriptionInit,\n ): Promise<RTCSessionDescriptionInit> {\n if (!this.isConnected) {\n throw ErrorFactory.WebSocketConnectionErrors.WebSocketConnectionLost(\n HMSAction.JOIN,\n 'Failed to send join over WS connection',\n );\n }\n const params = {\n name,\n disableVidAutoSub,\n data,\n offer,\n server_sub_degrade: serverSubDegrade,\n simulcast,\n onDemandTracks,\n };\n const response: RTCSessionDescriptionInit = await this.internalCall(HMSSignalMethod.JOIN, params);\n\n this.isJoinCompleted = true;\n this.pendingTrickle.forEach(({ target, candidate }) => this.trickle(target, candidate));\n this.pendingTrickle.length = 0;\n\n HMSLogger.d(this.TAG, `join: response=${JSON.stringify(response, null, 1)}`);\n return response;\n }\n\n trickle(target: HMSConnectionRole, candidate: RTCIceCandidateInit) {\n if (this.isJoinCompleted) {\n this.notify(HMSSignalMethod.TRICKLE, { target, candidate });\n } else {\n this.pendingTrickle.push({ target, candidate });\n }\n }\n\n async offer(desc: RTCSessionDescriptionInit, tracks: Map<string, any>): Promise<RTCSessionDescriptionInit> {\n const response = await this.call(HMSSignalMethod.OFFER, {\n desc,\n tracks: Object.fromEntries(tracks),\n });\n return response as RTCSessionDescriptionInit;\n }\n\n answer(desc: RTCSessionDescriptionInit) {\n this.notify(HMSSignalMethod.ANSWER, { desc });\n }\n\n trackUpdate(tracks: Map<string, Track>) {\n this.notify(HMSSignalMethod.TRACK_UPDATE, { version: '1.0', tracks: Object.fromEntries(tracks) });\n }\n\n async broadcast(message: Message) {\n return await this.call<BroadcastResponse>(HMSSignalMethod.BROADCAST, {\n version: '1.0',\n ...message.toSignalParams(),\n });\n }\n\n leave() {\n this.notify(HMSSignalMethod.LEAVE, { version: '1.0' });\n }\n\n async endRoom(lock: boolean, reason: string) {\n await this.call(HMSSignalMethod.END_ROOM, { lock, reason });\n }\n\n sendEvent(event: AnalyticsEvent) {\n if (!this.isConnected) {\n throw Error(`${this.TAG} not connected. Could not send event ${event}`);\n }\n this.notify(HMSSignalMethod.ANALYTICS, event.toSignalParams());\n }\n\n ping(timeout: number): Promise<number> {\n const pingTime = Date.now();\n const timer: Promise<number> = new Promise(resolve => {\n setTimeout(() => {\n resolve(Date.now() - pingTime);\n }, timeout + 1);\n });\n const pongTimeDiff = this.internalCall(HMSSignalMethod.PING, { timestamp: pingTime })\n .then(() => Date.now() - pingTime)\n .catch(() => Date.now() - pingTime);\n\n return Promise.race([timer, pongTimeDiff]);\n }\n\n async requestRoleChange(params: RequestForRoleChangeParams) {\n await this.call(HMSSignalMethod.ROLE_CHANGE_REQUEST, params);\n }\n\n async requestBulkRoleChange(params: RequestForBulkRoleChangeParams) {\n await this.call(HMSSignalMethod.ROLE_CHANGE_REQUEST, params);\n }\n\n async acceptRoleChangeRequest(params: AcceptRoleChangeParams) {\n await this.call(HMSSignalMethod.ROLE_CHANGE, params);\n }\n\n async requestTrackStateChange(params: TrackUpdateRequestParams) {\n await this.call(HMSSignalMethod.TRACK_UPDATE_REQUEST, params);\n }\n\n async requestMultiTrackStateChange(params: MultiTrackUpdateRequestParams) {\n await this.call(HMSSignalMethod.CHANGE_TRACK_MUTE_STATE_REQUEST, params);\n }\n\n async removePeer(params: RemovePeerRequest) {\n await this.call(HMSSignalMethod.PEER_LEAVE_REQUEST, params);\n }\n\n async startRTMPOrRecording(params: StartRTMPOrRecordingRequestParams) {\n await this.call(HMSSignalMethod.START_RTMP_OR_RECORDING_REQUEST, { version: '1.0', ...params });\n }\n\n async stopRTMPAndRecording() {\n await this.call(HMSSignalMethod.STOP_RTMP_AND_RECORDING_REQUEST, { version: '1.0' });\n }\n\n async startHLSStreaming(params: HLSRequestParams): Promise<void> {\n await this.call(HMSSignalMethod.START_HLS_STREAMING, { version: '1.0', ...params });\n }\n\n async stopHLSStreaming(params?: HLSRequestParams): Promise<void> {\n await this.call(HMSSignalMethod.STOP_HLS_STREAMING, { version: '1.0', ...params });\n }\n\n async sendHLSTimedMetadata(params?: HLSTimedMetadataParams): Promise<void> {\n await this.call(HMSSignalMethod.HLS_TIMED_METADATA, { version: '1.0', ...params });\n }\n\n async updatePeer(params: UpdatePeerRequestParams) {\n await this.call(HMSSignalMethod.UPDATE_PEER_METADATA, { version: '1.0', ...params });\n }\n\n setSessionMetadata(params: SetSessionMetadataParams) {\n if (!this.isConnected) {\n throw ErrorFactory.WebSocketConnectionErrors.WebSocketConnectionLost(\n HMSAction.RECONNECT_SIGNAL,\n 'Failed to set session store value due to network disconnection',\n );\n }\n return this.call<SetSessionMetadataResponse>(HMSSignalMethod.SET_METADATA, { version: '1.1', ...params });\n }\n\n listenMetadataChange(keys: string[]): Promise<void> {\n if (!this.isConnected) {\n throw ErrorFactory.WebSocketConnectionErrors.WebSocketConnectionLost(\n HMSAction.RECONNECT_SIGNAL,\n 'Failed to observe session store key due to network disconnection',\n );\n }\n return this.call(HMSSignalMethod.LISTEN_METADATA_CHANGE, { version: '1.1', keys });\n }\n\n getSessionMetadata(key?: string) {\n if (!this.isConnected) {\n throw ErrorFactory.WebSocketConnectionErrors.WebSocketConnectionLost(\n HMSAction.RECONNECT_SIGNAL,\n 'Failed to set session store value due to network disconnection',\n );\n }\n return this.call<GetSessionMetadataResponse>(HMSSignalMethod.GET_METADATA, { key, version: '1.1' });\n }\n\n setPollInfo(params: PollInfoSetParams) {\n this.valiateConnection();\n return this.call<PollInfoSetResponse>(HMSSignalMethod.POLL_INFO_SET, { version: '1.0', ...params });\n }\n\n getPollInfo(params: PollInfoGetParams) {\n this.valiateConnection();\n return this.call<PollInfoGetResponse>(HMSSignalMethod.POLL_INFO_GET, { version: '1.0', ...params });\n }\n\n setPollQuestions(params: PollQuestionsSetParams) {\n this.valiateConnection();\n return this.call<PollQuestionsSetResponse>(HMSSignalMethod.POLL_QUESTIONS_SET, { version: '1.0', ...params });\n }\n\n startPoll(params: PollStartParams) {\n this.valiateConnection();\n return this.call<PollStartResponse>(HMSSignalMethod.POLL_START, { version: '1.0', ...params });\n }\n\n stopPoll(params: PollStopParams) {\n this.valiateConnection();\n return this.call<PollStopResponse>(HMSSignalMethod.POLL_STOP, { version: '1.0', ...params });\n }\n\n getPollQuestions(params: PollQuestionsGetParams): Promise<PollQuestionsGetResponse> {\n this.valiateConnection();\n return this.call<PollQuestionsGetResponse>(HMSSignalMethod.POLL_QUESTIONS_GET, { version: '1.0', ...params });\n }\n\n setPollResponses(params: PollResponseSetParams): Promise<PollResponseSetResponse> {\n this.valiateConnection();\n return this.call<PollResponseSetResponse>(HMSSignalMethod.POLL_RESPONSE_SET, { version: '1.0', ...params });\n }\n\n getPollResponses(params: PollResponsesGetParams): Promise<PollResponsesGetResponse> {\n this.valiateConnection();\n return this.call<PollResponsesGetResponse>(HMSSignalMethod.POLL_RESPONSES, { version: '1.0', ...params });\n }\n\n getPollsList(params: PollListParams): Promise<PollListResponse> {\n this.valiateConnection();\n return this.call<PollListResponse>(HMSSignalMethod.POLL_LIST, { version: '1.0', ...params });\n }\n\n getPollResult(params: PollResultParams): Promise<PollResultResponse> {\n this.valiateConnection();\n return this.call<PollResultResponse>(HMSSignalMethod.POLL_RESULT, { version: '1.0', ...params });\n }\n\n private valiateConnection() {\n if (!this.isConnected) {\n throw ErrorFactory.WebSocketConnectionErrors.WebSocketConnectionLost(\n HMSAction.RECONNECT_SIGNAL,\n 'Failed to send message due to network disconnection',\n );\n }\n }\n\n private onMessageHandler(event: MessageEvent) {\n const text: string = event.data;\n const response = JSON.parse(text);\n this.resolvePingOnAnyResponse();\n if (response.id) {\n this.handleResponseWithId(response);\n } else if (response.method) {\n this.handleResponseWithMethod(response);\n } else {\n throw Error(`WebSocket message has no 'method' or 'id' field, message=${response}`);\n }\n }\n\n private handleResponseWithId(response: any) {\n /** This is a response to [call] */\n const typedResponse = response as JsonRpcResponse;\n const id: string = typedResponse.id;\n if (this.callbacks.has(id)) {\n const cb = this.callbacks.get(id)!;\n this.callbacks.delete(id);\n if (typedResponse.result) {\n cb.resolve(typedResponse.result);\n } else {\n cb.reject(typedResponse.error);\n }\n } else {\n this.observer.onNotification(typedResponse);\n }\n }\n\n private handleResponseWithMethod(response: any) {\n switch (response.method) {\n case HMSSignalMethod.OFFER:\n this.observer.onOffer(response.params);\n break;\n case HMSSignalMethod.TRICKLE:\n this.observer.onTrickle(response.params);\n break;\n case HMSSignalMethod.SERVER_ERROR:\n this.observer.onServerError(\n ErrorFactory.WebsocketMethodErrors.ServerErrors(\n Number(response.params.code),\n HMSSignalMethod.SERVER_ERROR,\n response.params.message,\n ),\n );\n break;\n case HMSSignalMethod.SERVER_WARNING:\n HMSLogger.w(this.TAG, response.params);\n break;\n default:\n this.observer.onNotification(response);\n break;\n }\n }\n\n private resolvePingOnAnyResponse = () => {\n this.callbacks.forEach((callback, key) => {\n if (callback.metadata?.method === HMSSignalMethod.PING) {\n //@ts-ignore\n callback.resolve({ timestamp: Date.now() });\n this.callbacks.delete(key);\n }\n });\n };\n\n private rejectPendingCalls(reason = '') {\n this.callbacks.forEach((callback, id) => {\n if (callback.metadata?.method !== HMSSignalMethod.PING) {\n HMSLogger.e(this.TAG, `rejecting pending callback ${callback.metadata?.method}, id=${id}`);\n callback.reject(\n ErrorFactory.WebSocketConnectionErrors.WebSocketConnectionLost(\n callback.metadata?.method\n ? convertSignalMethodtoErrorAction(callback.metadata?.method)\n : HMSAction.RECONNECT_SIGNAL,\n reason,\n ),\n );\n this.callbacks.delete(id);\n }\n });\n }\n\n private async pingPongLoop(id: number) {\n const pingTimeout = window.HMS?.PING_TIMEOUT || DEFAULT_SIGNAL_PING_TIMEOUT;\n if (this.isConnected) {\n const pongTimeDiff = await this.ping(pingTimeout);\n this.pongResponseTimes.enqueue(pongTimeDiff);\n if (pongTimeDiff > pingTimeout) {\n HMSLogger.d(this.TAG, `Pong timeout ${id}, pageHidden=${isPageHidden()}`);\n if (this.id === id) {\n this.setIsConnected(false, 'ping pong failure');\n }\n } else {\n setTimeout(() => this.pingPongLoop(id), window.HMS?.PING_INTERVAL || DEFAULT_SIGNAL_PING_INTERVAL);\n }\n }\n }\n\n private async call<T>(method: HMSSignalMethod, params: Record<string, any>): Promise<T> {\n const MAX_RETRIES = 3;\n let error: HMSException = ErrorFactory.WebsocketMethodErrors.ServerErrors(500, method, `Default ${method} error`);\n\n let retry;\n for (retry = 1; retry <= MAX_RETRIES; retry++) {\n try {\n HMSLogger.d(this.TAG, `Try number ${retry} sending ${method}`, params);\n return await this.internalCall(method, params);\n } catch (err) {\n error = err as HMSException;\n HMSLogger.e(this.TAG, `Failed sending ${method} try: ${retry}`, { method, params, error });\n const shouldRetry = parseInt(`${error.code / 100}`) === 5 || error.code === 429;\n if (!shouldRetry) {\n break;\n }\n\n const delay = (2 + Math.random() * 2) * 1000;\n await sleep(delay);\n }\n }\n HMSLogger.e(`Sending ${method} over WS failed after ${Math.min(retry, MAX_RETRIES)} retries`, {\n method,\n params,\n error,\n });\n throw error;\n }\n\n private offlineListener = () => {\n HMSLogger.d(this.TAG, 'Window network offline');\n this.setIsConnected(false, 'Window network offline');\n };\n\n private onlineListener = () => {\n HMSLogger.d(this.TAG, 'Window network online');\n this.observer.onNetworkOnline();\n };\n}\n", "// @ts-nocheck\nimport { isBrowser } from './support';\n\nexport const getNetworkInfo = () => {\n if (!isBrowser || typeof navigator.connection === 'undefined') {\n return;\n }\n\n const connection = navigator.connection;\n const networkInfo = {\n downlink: connection.downlink,\n downlinkMax: connection.downlinkMax,\n effectiveType: connection.effectiveType,\n rtt: connection.rtt,\n saveData: connection.saveData,\n type: connection.type,\n };\n return networkInfo;\n};\n", "import { JoinParameters } from './models/JoinParameters';\nimport { TransportFailureCategory } from './models/TransportFailureCategory';\nimport { TransportState } from './models/TransportState';\nimport ITransport from './ITransport';\nimport ITransportObserver from './ITransportObserver';\nimport { RetryScheduler } from './RetryScheduler';\nimport { AdditionalAnalyticsProperties } from '../analytics/AdditionalAnalyticsProperties';\nimport AnalyticsEvent from '../analytics/AnalyticsEvent';\nimport AnalyticsEventFactory from '../analytics/AnalyticsEventFactory';\nimport { AnalyticsEventsService } from '../analytics/AnalyticsEventsService';\nimport { AnalyticsTimer, TimedEvent } from '../analytics/AnalyticsTimer';\nimport { HTTPAnalyticsTransport } from '../analytics/HTTPAnalyticsTransport';\nimport { PublishStatsAnalytics } from '../analytics/publish-stats';\nimport { SignalAnalyticsTransport } from '../analytics/signal-transport/SignalAnalyticsTransport';\nimport { HMSConnectionRole, HMSTrickle } from '../connection/model';\nimport { IPublishConnectionObserver } from '../connection/publish/IPublishConnectionObserver';\nimport HMSPublishConnection from '../connection/publish/publishConnection';\nimport ISubscribeConnectionObserver from '../connection/subscribe/ISubscribeConnectionObserver';\nimport HMSSubscribeConnection from '../connection/subscribe/subscribeConnection';\nimport { DeviceManager } from '../device-manager';\nimport { ErrorCodes } from '../error/ErrorCodes';\nimport { ErrorFactory } from '../error/ErrorFactory';\nimport { HMSAction } from '../error/HMSAction';\nimport { HMSException } from '../error/HMSException';\nimport { EventBus } from '../events/EventBus';\nimport { HLSConfig, HLSTimedMetadata, HMSPeer, HMSRole, HMSRoleChangeRequest } from '../interfaces';\nimport { RTMPRecordingConfig } from '../interfaces/rtmp-recording-config';\nimport { HMSLocalStream } from '../media/streams/HMSLocalStream';\nimport { HMSLocalTrack, HMSLocalVideoTrack, HMSTrack } from '../media/tracks';\nimport { TrackState } from '../notification-manager';\nimport { HMSWebrtcInternals } from '../rtc-stats/HMSWebrtcInternals';\nimport Message from '../sdk/models/HMSMessage';\nimport { IStore } from '../sdk/store';\nimport InitService from '../signal/init';\nimport { InitConfig, InitFlags } from '../signal/init/models';\nimport {\n HLSRequestParams,\n HLSTimedMetadataParams,\n HLSVariant,\n MultiTrackUpdateRequestParams,\n PollInfoGetParams,\n PollInfoGetResponse,\n PollInfoSetParams,\n PollInfoSetResponse,\n PollListParams,\n PollListResponse,\n PollQuestionsGetParams,\n PollQuestionsGetResponse,\n PollQuestionsSetParams,\n PollQuestionsSetResponse,\n PollResponseSetParams,\n PollResponseSetResponse,\n PollResponsesGetParams,\n PollResponsesGetResponse,\n PollResultParams,\n PollResultResponse,\n PollStartParams,\n PollStartResponse,\n PollStopParams,\n SetSessionMetadataParams,\n StartRTMPOrRecordingRequestParams,\n TrackUpdateRequestParams,\n} from '../signal/interfaces';\nimport { ISignal } from '../signal/ISignal';\nimport { ISignalEventsObserver } from '../signal/ISignalEventsObserver';\nimport JsonRpcSignal from '../signal/jsonrpc';\nimport {\n ICE_DISCONNECTION_TIMEOUT,\n MAX_TRANSPORT_RETRIES,\n RENEGOTIATION_CALLBACK_ID,\n SUBSCRIBE_ICE_CONNECTION_CALLBACK_ID,\n SUBSCRIBE_TIMEOUT,\n} from '../utils/constants';\nimport HMSLogger from '../utils/logger';\nimport { getNetworkInfo } from '../utils/network-info';\nimport { PromiseCallbacks } from '../utils/promise';\n\nconst TAG = '[HMSTransport]:';\n\n// @DISCUSS: action and extra are not used at all.\ninterface CallbackTriple {\n promise: PromiseCallbacks<boolean>;\n action: HMSAction;\n extra: any;\n}\n\ninterface NegotiateJoinParams {\n name: string;\n data: string;\n autoSubscribeVideo: boolean;\n}\n\nexport default class HMSTransport implements ITransport {\n private state: TransportState = TransportState.Disconnected;\n private trackStates: Map<string, TrackState> = new Map();\n private publishConnection: HMSPublishConnection | null = null;\n private subscribeConnection: HMSSubscribeConnection | null = null;\n private initConfig?: InitConfig;\n private endpoint!: string;\n private joinParameters?: JoinParameters;\n private retryScheduler: RetryScheduler;\n private webrtcInternals?: HMSWebrtcInternals;\n private publishStatsAnalytics?: PublishStatsAnalytics;\n private maxSubscribeBitrate = 0;\n joinRetryCount = 0;\n\n constructor(\n private observer: ITransportObserver,\n private deviceManager: DeviceManager,\n private store: IStore,\n private eventBus: EventBus,\n private analyticsEventsService: AnalyticsEventsService,\n private analyticsTimer: AnalyticsTimer,\n ) {\n this.webrtcInternals = new HMSWebrtcInternals(\n this.store,\n this.eventBus,\n this.publishConnection?.nativeConnection,\n this.subscribeConnection?.nativeConnection,\n );\n\n const onStateChange = async (state: TransportState, error?: HMSException) => {\n if (state !== this.state) {\n this.state = state;\n await this.observer.onStateChange(this.state, error);\n }\n };\n this.retryScheduler = new RetryScheduler(onStateChange, this.sendErrorAnalyticsEvent.bind(this));\n\n this.eventBus.statsUpdate.subscribe(stats => {\n const currentSubscribeBitrate = stats.getLocalPeerStats()?.subscribe?.bitrate || 0;\n this.maxSubscribeBitrate = Math.max(this.maxSubscribeBitrate, currentSubscribeBitrate);\n });\n\n this.eventBus.localAudioEnabled.subscribe(({ track }) => this.trackUpdate(track));\n this.eventBus.localVideoEnabled.subscribe(({ track }) => this.trackUpdate(track));\n }\n\n /**\n * Map of callbacks used to wait for an event to fire.\n * Used here for:\n * 1. publish/unpublish waits for [IPublishConnectionObserver.onRenegotiationNeeded] to complete\n */\n private readonly callbacks = new Map<string, CallbackTriple>();\n\n private signalObserver: ISignalEventsObserver = {\n onOffer: async (jsep: RTCSessionDescriptionInit) => {\n try {\n if (!this.subscribeConnection) {\n return;\n }\n await this.subscribeConnection.setRemoteDescription(jsep);\n HMSLogger.d(\n TAG,\n `[SUBSCRIBE] Adding ${this.subscribeConnection.candidates.length} ice-candidates`,\n this.subscribeConnection.candidates,\n );\n for (const candidate of this.subscribeConnection.candidates) {\n await this.subscribeConnection.addIceCandidate(candidate);\n }\n this.subscribeConnection.candidates.length = 0;\n const answer = await this.subscribeConnection.createAnswer();\n await this.subscribeConnection.setLocalDescription(answer);\n this.signal.answer(answer);\n HMSLogger.d(TAG, '[role=SUBSCRIBE] onOffer renegotiation DONE \u2705');\n } catch (err) {\n HMSLogger.d(TAG, '[role=SUBSCRIBE] onOffer renegotiation FAILED \u274C', err);\n this.state = TransportState.Failed;\n let ex: HMSException;\n if (err instanceof HMSException) {\n ex = err;\n } else {\n ex = ErrorFactory.GenericErrors.Unknown(HMSAction.PUBLISH, (err as Error).message);\n }\n this.observer.onFailure(ex);\n this.eventBus.analytics.publish(AnalyticsEventFactory.subscribeFail(ex));\n }\n },\n\n onTrickle: async (trickle: HMSTrickle) => {\n const connection =\n trickle.target === HMSConnectionRole.Publish ? this.publishConnection : this.subscribeConnection;\n if (!connection?.remoteDescription) {\n // ICE candidates can't be added without any remote session description\n connection?.candidates.push(trickle.candidate);\n } else {\n await connection.addIceCandidate(trickle.candidate);\n }\n },\n\n onNotification: (message: any) => this.observer.onNotification(message),\n\n onServerError: async (error: HMSException) => {\n await this.observer.onStateChange(TransportState.Failed, error);\n },\n\n onFailure: (error: HMSException) => {\n // @DISCUSS: Should we remove this? Pong failure would have already scheduled signal retry.\n if (this.joinParameters) {\n this.retryScheduler.schedule({\n category: TransportFailureCategory.SignalDisconnect,\n error,\n task: this.retrySignalDisconnectTask,\n originalState: this.state,\n });\n }\n },\n\n onOffline: async (reason: string) => {\n HMSLogger.d(TAG, 'socket offline', TransportState[this.state]);\n try {\n if (this.state !== TransportState.Leaving && this.joinParameters) {\n this.retryScheduler.schedule({\n category: TransportFailureCategory.SignalDisconnect,\n error: ErrorFactory.WebSocketConnectionErrors.WebSocketConnectionLost(HMSAction.RECONNECT_SIGNAL, reason),\n task: this.retrySignalDisconnectTask,\n originalState: this.state,\n });\n }\n } catch (e) {\n console.error(e);\n }\n },\n\n // this is called when socket connection is successful\n onOnline: () => {\n HMSLogger.d(TAG, 'socket online', TransportState[this.state]);\n this.analyticsSignalTransport.flushFailedEvents(this.store.getLocalPeer()?.peerId);\n },\n // this is called when window.online event is triggered\n onNetworkOnline: () => {\n this.analyticsEventsService.flushFailedClientEvents();\n },\n };\n\n private signal: ISignal = new JsonRpcSignal(this.signalObserver);\n private analyticsSignalTransport = new SignalAnalyticsTransport(this.signal);\n\n private publishConnectionObserver: IPublishConnectionObserver = {\n onRenegotiationNeeded: async () => {\n await this.performPublishRenegotiation();\n },\n\n onIceConnectionChange: async (newState: RTCIceConnectionState) => {\n const log = newState === 'disconnected' ? HMSLogger.w.bind(HMSLogger) : HMSLogger.d.bind(HMSLogger);\n log(TAG, `Publish ice connection state change: ${newState}`);\n\n // @TODO: Uncomment this and remove connectionstatechange\n if (newState === 'failed') {\n // await this.handleIceConnectionFailure(HMSConnectionRole.Publish);\n }\n },\n\n // @TODO(eswar): Remove this. Use iceconnectionstate change with interval and threshold.\n onConnectionStateChange: async (newState: RTCPeerConnectionState) => {\n const log = newState === 'disconnected' ? HMSLogger.w.bind(HMSLogger) : HMSLogger.d.bind(HMSLogger);\n log(TAG, `Publish connection state change: ${newState}`);\n\n if (newState === 'connected') {\n this.publishConnection?.logSelectedIceCandidatePairs();\n }\n\n if (newState === 'disconnected') {\n // if state stays disconnected for 5 seconds, retry\n setTimeout(() => {\n if (this.publishConnection?.connectionState === 'disconnected') {\n this.handleIceConnectionFailure(\n HMSConnectionRole.Publish,\n ErrorFactory.WebrtcErrors.ICEDisconnected(\n HMSAction.PUBLISH,\n `local candidate - ${this.publishConnection?.selectedCandidatePair?.local.candidate}; remote candidate - ${this.publishConnection?.selectedCandidatePair?.remote.candidate}`,\n ),\n );\n }\n }, ICE_DISCONNECTION_TIMEOUT);\n }\n\n if (newState === 'failed') {\n await this.handleIceConnectionFailure(\n HMSConnectionRole.Publish,\n ErrorFactory.WebrtcErrors.ICEFailure(\n HMSAction.PUBLISH,\n `local candidate - ${this.publishConnection?.selectedCandidatePair?.local.candidate}; remote candidate - ${this.publishConnection?.selectedCandidatePair?.remote.candidate}`,\n ),\n );\n }\n },\n };\n\n private subscribeConnectionObserver: ISubscribeConnectionObserver = {\n onApiChannelMessage: (message: string) => {\n this.observer.onNotification(JSON.parse(message));\n },\n\n onTrackAdd: (track: HMSTrack) => {\n HMSLogger.d(TAG, '[Subscribe] onTrackAdd', `${track}`);\n this.observer.onTrackAdd(track);\n },\n\n onTrackRemove: (track: HMSTrack) => {\n HMSLogger.d(TAG, '[Subscribe] onTrackRemove', `${track}`);\n this.observer.onTrackRemove(track);\n },\n\n onIceConnectionChange: async (newState: RTCIceConnectionState) => {\n const log = newState === 'disconnected' ? HMSLogger.w.bind(HMSLogger) : HMSLogger.d.bind(HMSLogger);\n log(TAG, `Subscribe ice connection state change: ${newState}`);\n\n if (newState === 'failed') {\n // await this.handleIceConnectionFailure(HMSConnectionRole.Subscribe);\n }\n\n if (newState === 'connected') {\n const callback = this.callbacks.get(SUBSCRIBE_ICE_CONNECTION_CALLBACK_ID);\n this.callbacks.delete(SUBSCRIBE_ICE_CONNECTION_CALLBACK_ID);\n\n if (callback) {\n callback.promise.resolve(true);\n }\n }\n },\n\n // @TODO(eswar): Remove this. Use iceconnectionstate change with interval and threshold.\n onConnectionStateChange: async (newState: RTCPeerConnectionState) => {\n const log = newState === 'disconnected' ? HMSLogger.w.bind(HMSLogger) : HMSLogger.d.bind(HMSLogger);\n log(TAG, `Subscribe connection state change: ${newState}`);\n\n if (newState === 'failed') {\n await this.handleIceConnectionFailure(\n HMSConnectionRole.Subscribe,\n ErrorFactory.WebrtcErrors.ICEFailure(\n HMSAction.SUBSCRIBE,\n `local candidate - ${this.subscribeConnection?.selectedCandidatePair?.local.candidate}; remote candidate - ${this.subscribeConnection?.selectedCandidatePair?.remote.candidate}`,\n ),\n );\n }\n\n if (newState === 'disconnected') {\n setTimeout(() => {\n if (this.subscribeConnection?.connectionState === 'disconnected') {\n this.handleIceConnectionFailure(\n HMSConnectionRole.Subscribe,\n ErrorFactory.WebrtcErrors.ICEDisconnected(\n HMSAction.SUBSCRIBE,\n `local candidate - ${this.subscribeConnection?.selectedCandidatePair?.local.candidate}; remote candidate - ${this.subscribeConnection?.selectedCandidatePair?.remote.candidate}`,\n ),\n );\n }\n }, ICE_DISCONNECTION_TIMEOUT);\n }\n\n if (newState === 'connected') {\n this.handleSubscribeConnectionConnected();\n }\n },\n };\n\n getWebrtcInternals() {\n return this.webrtcInternals;\n }\n\n isFlagEnabled(flag: InitFlags) {\n const config = this.initConfig?.config;\n const flags = config?.enabledFlags || [];\n return flags.includes(flag);\n }\n\n async preview(\n token: string,\n endpoint: string,\n peerId: string,\n customData: { name: string; metaData: string },\n autoSubscribeVideo = false,\n ): Promise<InitConfig | void> {\n const initConfig = await this.connect(token, endpoint, peerId, customData, autoSubscribeVideo);\n this.state = TransportState.Preview;\n this.observer.onStateChange(this.state);\n return initConfig;\n }\n\n async join(\n authToken: string,\n peerId: string,\n customData: { name: string; metaData: string },\n initEndpoint: string,\n autoSubscribeVideo = false,\n ): Promise<void> {\n HMSLogger.d(TAG, 'join: started \u23F0');\n try {\n if (!this.signal.isConnected || !this.initConfig) {\n await this.connect(authToken, initEndpoint, peerId, customData, autoSubscribeVideo);\n }\n\n this.validateNotDisconnected('connect');\n\n if (this.initConfig) {\n await this.waitForLocalRoleAvailability();\n await this.createConnectionsAndNegotiateJoin(customData, autoSubscribeVideo);\n await this.initRtcStatsMonitor();\n\n HMSLogger.d(TAG, '\u2705 join: Negotiated over PUBLISH connection');\n }\n } catch (error) {\n HMSLogger.e(TAG, `join: failed \u274C [token=${authToken}]`, error);\n this.state = TransportState.Failed;\n const ex = error as HMSException;\n // set isTerminal to true if not already when error code is 500(internal biz server error)\n ex.isTerminal = ex.isTerminal || ex.code === 500;\n await this.observer.onStateChange(this.state, ex);\n throw ex;\n }\n\n HMSLogger.d(TAG, '\u2705 join: successful');\n this.state = TransportState.Joined;\n this.observer.onStateChange(this.state);\n }\n\n async connect(\n token: string,\n endpoint: string,\n peerId: string,\n customData: { name: string; metaData: string },\n autoSubscribeVideo = false,\n ): Promise<InitConfig | void> {\n this.setTransportStateForConnect();\n this.joinParameters = new JoinParameters(\n token,\n peerId,\n customData.name,\n customData.metaData,\n endpoint,\n autoSubscribeVideo,\n );\n try {\n const response = await this.internalConnect(token, endpoint, peerId);\n return response;\n } catch (error) {\n const shouldRetry =\n error instanceof HMSException &&\n ([\n ErrorCodes.WebSocketConnectionErrors.WEBSOCKET_CONNECTION_LOST,\n ErrorCodes.WebSocketConnectionErrors.FAILED_TO_CONNECT,\n ErrorCodes.WebSocketConnectionErrors.ABNORMAL_CLOSE,\n ErrorCodes.APIErrors.ENDPOINT_UNREACHABLE,\n ].includes(error.code) ||\n error.code.toString().startsWith('5') ||\n error.code.toString().startsWith('429'));\n\n if (shouldRetry) {\n const task = async () => {\n await this.internalConnect(token, endpoint, peerId);\n return Boolean(this.initConfig && this.initConfig.endpoint);\n };\n\n await this.retryScheduler.schedule({\n category: TransportFailureCategory.ConnectFailed,\n error,\n task,\n originalState: this.state,\n maxFailedRetries: MAX_TRANSPORT_RETRIES,\n changeState: false,\n });\n } else {\n throw error;\n }\n }\n }\n\n async leave(notifyServer: boolean): Promise<void> {\n this.retryScheduler.reset();\n this.joinParameters = undefined;\n HMSLogger.d(TAG, 'leaving in transport');\n try {\n this.state = TransportState.Leaving;\n this.publishStatsAnalytics?.stop();\n this.webrtcInternals?.cleanup();\n await this.publishConnection?.close();\n await this.subscribeConnection?.close();\n if (notifyServer) {\n try {\n this.signal.leave();\n HMSLogger.d(TAG, 'signal leave done');\n } catch (err) {\n HMSLogger.w(TAG, 'failed to send leave on websocket to server', err);\n }\n }\n this.analyticsEventsService.flushFailedClientEvents();\n this.analyticsEventsService.reset();\n await this.signal.close();\n } catch (err) {\n this.eventBus.analytics.publish(AnalyticsEventFactory.disconnect(err as Error));\n HMSLogger.e(TAG, 'leave: FAILED \u274C', err);\n } finally {\n this.state = TransportState.Disconnected;\n this.observer.onStateChange(this.state);\n }\n }\n\n handleLocalRoleUpdate = async ({ oldRole, newRole }: { oldRole: HMSRole; newRole: HMSRole }) => {\n const changedFromNonWebRTCToWebRTC = !this.doesRoleNeedWebRTC(oldRole) && this.doesRoleNeedWebRTC(newRole);\n if (!changedFromNonWebRTCToWebRTC) {\n return;\n }\n\n HMSLogger.d(\n TAG,\n 'Local peer role updated to webrtc role, creating PeerConnections and performing inital publish negotiation \u23F3',\n );\n this.createPeerConnections();\n await this.negotiateOnFirstPublish();\n };\n\n async publish(tracks: Array<HMSLocalTrack>): Promise<void> {\n for (const track of tracks) {\n try {\n await this.publishTrack(track);\n } catch (error) {\n this.eventBus.analytics.publish(\n AnalyticsEventFactory.publish({\n devices: this.deviceManager.getDevices(),\n error: error as Error,\n }),\n );\n }\n }\n }\n\n async unpublish(tracks: Array<HMSLocalTrack>): Promise<void> {\n for (const track of tracks) {\n await this.unpublishTrack(track);\n }\n }\n\n async sendMessage(message: Message) {\n return await this.signal.broadcast(message);\n }\n\n /**\n * TODO: check if track.publishedTrackId be used instead of the hack to match with track with same type and\n * source. The hack won't work if there are multiple tracks with same source and type.\n */\n trackUpdate(track: HMSLocalTrack) {\n const currentTrackStates = Array.from(this.trackStates.values());\n const originalTrackState = currentTrackStates.find(\n trackState => track.type === trackState.type && track.source === trackState.source,\n );\n if (originalTrackState) {\n const newTrackState = new TrackState({\n ...originalTrackState,\n mute: !track.enabled,\n });\n this.trackStates.set(originalTrackState.track_id, newTrackState);\n HMSLogger.d(TAG, 'Track Update', this.trackStates, track);\n this.signal.trackUpdate(new Map([[originalTrackState.track_id, newTrackState]]));\n }\n }\n\n async changeRole(forPeer: HMSPeer, toRole: string, force = false) {\n await this.signal.requestRoleChange({\n requested_for: forPeer.peerId,\n role: toRole,\n force,\n });\n }\n\n async changeRoleOfPeer(forPeer: HMSPeer, toRole: string, force: boolean) {\n await this.signal.requestRoleChange({\n requested_for: forPeer.peerId,\n role: toRole,\n force,\n });\n }\n\n async changeRoleOfPeersWithRoles(roles: HMSRole[], toRole: string) {\n await this.signal.requestBulkRoleChange({\n roles: roles.map((role: HMSRole) => role.name),\n role: toRole,\n force: true,\n });\n }\n\n async acceptRoleChange(request: HMSRoleChangeRequest) {\n await this.signal.acceptRoleChangeRequest({\n requested_by: request.requestedBy?.peerId,\n role: request.role.name,\n token: request.token,\n });\n }\n\n async endRoom(lock: boolean, reason: string) {\n await this.signal.endRoom(lock, reason);\n }\n\n async removePeer(peerId: string, reason: string) {\n await this.signal.removePeer({ requested_for: peerId, reason });\n }\n\n async startRTMPOrRecording(params: RTMPRecordingConfig) {\n const signalParams: StartRTMPOrRecordingRequestParams = {\n meeting_url: params.meetingURL,\n record: params.record,\n };\n\n if (params.rtmpURLs?.length) {\n signalParams.rtmp_urls = params.rtmpURLs;\n }\n\n if (params.resolution) {\n signalParams.resolution = params.resolution;\n }\n\n await this.signal.startRTMPOrRecording(signalParams);\n }\n\n async stopRTMPOrRecording() {\n await this.signal.stopRTMPAndRecording();\n }\n\n async startHLSStreaming(params?: HLSConfig) {\n const hlsParams: HLSRequestParams = {};\n if (params && params.variants && params.variants.length > 0) {\n hlsParams.variants = params.variants.map(variant => {\n const hlsVariant: HLSVariant = { meeting_url: variant.meetingURL };\n if (variant.metadata) {\n hlsVariant.metadata = variant.metadata;\n }\n return hlsVariant;\n });\n }\n if (params?.recording) {\n hlsParams.hls_recording = {\n single_file_per_layer: params.recording.singleFilePerLayer,\n hls_vod: params.recording.hlsVod,\n };\n }\n await this.signal.startHLSStreaming(hlsParams);\n }\n\n async stopHLSStreaming(params?: HLSConfig) {\n if (params) {\n const hlsParams: HLSRequestParams = {\n variants: params?.variants?.map(variant => {\n const hlsVariant: HLSVariant = { meeting_url: variant.meetingURL };\n if (variant.metadata) {\n hlsVariant.metadata = variant.metadata;\n }\n return hlsVariant;\n }),\n };\n await this.signal.stopHLSStreaming(hlsParams);\n }\n await this.signal.stopHLSStreaming();\n }\n\n async sendHLSTimedMetadata(metadataList: HLSTimedMetadata[]) {\n if (metadataList.length > 0) {\n const hlsMtParams: HLSTimedMetadataParams = {\n metadata_objs: metadataList,\n };\n\n await this.signal.sendHLSTimedMetadata(hlsMtParams);\n }\n }\n async changeName(name: string) {\n const peer = this.store.getLocalPeer();\n if (peer && peer.name !== name) {\n await this.signal.updatePeer({\n name: name,\n });\n }\n }\n\n async changeMetadata(metadata: string) {\n await this.signal.updatePeer({\n data: metadata,\n });\n }\n\n getSessionMetadata(key?: string) {\n return this.signal.getSessionMetadata(key);\n }\n\n setSessionMetadata(params: SetSessionMetadataParams) {\n return this.signal.setSessionMetadata(params);\n }\n\n listenMetadataChange(keys: string[]): Promise<void> {\n return this.signal.listenMetadataChange(keys);\n }\n\n setPollInfo(params: PollInfoSetParams): Promise<PollInfoSetResponse> {\n return this.signal.setPollInfo(params);\n }\n\n getPollInfo(params: PollInfoGetParams): Promise<PollInfoGetResponse> {\n return this.signal.getPollInfo(params);\n }\n\n setPollQuestions(params: PollQuestionsSetParams): Promise<PollQuestionsSetResponse> {\n return this.signal.setPollQuestions(params);\n }\n\n getPollQuestions(params: PollQuestionsGetParams): Promise<PollQuestionsGetResponse> {\n return this.signal.getPollQuestions(params);\n }\n\n startPoll(params: PollStartParams): Promise<PollStartResponse> {\n return this.signal.startPoll(params);\n }\n\n stopPoll(params: PollStopParams): Promise<PollStartResponse> {\n return this.signal.stopPoll(params);\n }\n\n setPollResponses(params: PollResponseSetParams): Promise<PollResponseSetResponse> {\n return this.signal.setPollResponses(params);\n }\n\n getPollResponses(params: PollResponsesGetParams): Promise<PollResponsesGetResponse> {\n return this.signal.getPollResponses(params);\n }\n\n getPollsList(params: PollListParams): Promise<PollListResponse> {\n return this.signal.getPollsList(params);\n }\n\n getPollResult(params: PollResultParams): Promise<PollResultResponse> {\n return this.signal.getPollResult(params);\n }\n\n async changeTrackState(trackUpdateRequest: TrackUpdateRequestParams) {\n await this.signal.requestTrackStateChange(trackUpdateRequest);\n }\n\n async changeMultiTrackState(trackUpdateRequest: MultiTrackUpdateRequestParams) {\n await this.signal.requestMultiTrackStateChange(trackUpdateRequest);\n }\n\n private async publishTrack(track: HMSLocalTrack): Promise<void> {\n track.publishedTrackId = track.getTrackIDBeingSent();\n HMSLogger.d(\n TAG,\n `\u23F3 publishTrack: trackId=${track.trackId}, toPublishTrackId=${track.publishedTrackId}`,\n `${track}`,\n );\n this.trackStates.set(track.publishedTrackId, new TrackState(track));\n const p = new Promise<boolean>((resolve, reject) => {\n this.callbacks.set(RENEGOTIATION_CALLBACK_ID, {\n promise: { resolve, reject },\n action: HMSAction.PUBLISH,\n extra: {},\n });\n });\n const stream = track.stream as HMSLocalStream;\n stream.setConnection(this.publishConnection!);\n const simulcastLayers = this.store.getSimulcastLayers(track.source!);\n stream.addTransceiver(track, simulcastLayers);\n HMSLogger.time(`publish-${track.trackId}-${track.type}`);\n await p;\n HMSLogger.timeEnd(`publish-${track.trackId}-${track.type}`);\n // add track to store after publish\n this.store.addTrack(track);\n\n await stream\n .setMaxBitrateAndFramerate(track)\n .then(() => {\n HMSLogger.d(\n TAG,\n `Setting maxBitrate=${track.settings.maxBitrate} kpbs${\n track instanceof HMSLocalVideoTrack ? ` and maxFramerate=${track.settings.maxFramerate}` : ''\n } for ${track.source} ${track.type} ${track.trackId}`,\n );\n })\n .catch(error => HMSLogger.w(TAG, 'Failed setting maxBitrate and maxFramerate', error));\n\n track.isPublished = true;\n\n HMSLogger.d(TAG, `\u2705 publishTrack: trackId=${track.trackId}`, `${track}`, this.callbacks);\n }\n\n private async unpublishTrack(track: HMSLocalTrack): Promise<void> {\n HMSLogger.d(TAG, `\u23F3 unpublishTrack: trackId=${track.trackId}`, `${track}`);\n if (track.publishedTrackId && this.trackStates.has(track.publishedTrackId)) {\n this.trackStates.delete(track.publishedTrackId);\n } else {\n // TODO: hotfix to unpublish replaced video track id, solve it properly\n // it won't work when there are multiple regular video tracks, hmslocalvideotrack can store\n // the original initial track id for a proper fix\n const currentTrackStates = Array.from(this.trackStates.values());\n const originalTrackState = currentTrackStates.find(\n trackState => track.type === trackState.type && track.source === trackState.source,\n );\n if (originalTrackState) {\n this.trackStates.delete(originalTrackState.track_id);\n }\n }\n const p = new Promise<boolean>((resolve, reject) => {\n this.callbacks.set(RENEGOTIATION_CALLBACK_ID, {\n promise: { resolve, reject },\n action: HMSAction.UNPUBLISH,\n extra: {},\n });\n });\n const stream = track.stream as HMSLocalStream;\n stream.removeSender(track);\n await p;\n await track.cleanup();\n // remove track from store on unpublish\n this.store.removeTrack(track);\n HMSLogger.d(TAG, `\u2705 unpublishTrack: trackId=${track.trackId}`, this.callbacks);\n }\n\n private waitForLocalRoleAvailability() {\n if (this.store.hasRoleDetailsArrived()) {\n return;\n } else {\n return new Promise<void>(resolve => {\n this.eventBus.policyChange.subscribeOnce(() => resolve());\n });\n }\n }\n\n private async createConnectionsAndNegotiateJoin(\n customData: { name: string; metaData: string },\n autoSubscribeVideo = false,\n ) {\n const isWebRTC = this.doesLocalPeerNeedWebRTC();\n if (isWebRTC) {\n this.createPeerConnections();\n }\n\n this.analyticsTimer.start(TimedEvent.JOIN_RESPONSE);\n await this.negotiateJoinWithRetry({\n name: customData.name,\n data: customData.metaData,\n autoSubscribeVideo,\n isWebRTC,\n });\n this.analyticsTimer.end(TimedEvent.JOIN_RESPONSE);\n }\n\n private createPeerConnections() {\n if (this.initConfig) {\n if (!this.publishConnection) {\n this.publishConnection = new HMSPublishConnection(\n this.signal,\n this.initConfig.rtcConfiguration,\n this.publishConnectionObserver,\n );\n }\n\n if (!this.subscribeConnection) {\n this.subscribeConnection = new HMSSubscribeConnection(\n this.signal,\n this.initConfig.rtcConfiguration,\n this.isFlagEnabled.bind(this),\n this.subscribeConnectionObserver,\n );\n }\n }\n }\n\n private async negotiateJoinWithRetry({\n name,\n data,\n autoSubscribeVideo,\n isWebRTC = true,\n }: NegotiateJoinParams & { isWebRTC: boolean }) {\n try {\n await this.negotiateJoin({ name, data, autoSubscribeVideo, isWebRTC });\n } catch (error) {\n HMSLogger.e(TAG, 'Join negotiation failed \u274C', error);\n const hmsError =\n error instanceof HMSException\n ? error\n : ErrorFactory.WebsocketMethodErrors.ServerErrors(\n 500,\n HMSAction.JOIN,\n `Websocket join error - ${(error as Error).message}`,\n );\n const shouldRetry =\n parseInt(`${hmsError.code / 100}`) === 5 ||\n [ErrorCodes.WebSocketConnectionErrors.WEBSOCKET_CONNECTION_LOST, 429].includes(hmsError.code);\n\n if (hmsError.code === 410) {\n hmsError.isTerminal = true;\n }\n\n if (shouldRetry) {\n this.joinRetryCount = 0;\n hmsError.isTerminal = false;\n const task = async () => {\n this.joinRetryCount++;\n return await this.negotiateJoin({ name, data, autoSubscribeVideo, isWebRTC });\n };\n\n await this.retryScheduler.schedule({\n category: TransportFailureCategory.JoinWSMessageFailed,\n error: hmsError,\n task,\n originalState: TransportState.Joined,\n maxFailedRetries: 3,\n changeState: false,\n });\n } else {\n throw error;\n }\n }\n }\n\n private async negotiateJoin({\n name,\n data,\n autoSubscribeVideo,\n isWebRTC = true,\n }: NegotiateJoinParams & { isWebRTC: boolean }): Promise<boolean> {\n if (isWebRTC) {\n return await this.negotiateJoinWebRTC({ name, data, autoSubscribeVideo });\n } else {\n return await this.negotiateJoinNonWebRTC({ name, data, autoSubscribeVideo });\n }\n }\n\n private async negotiateJoinWebRTC({ name, data, autoSubscribeVideo }: NegotiateJoinParams): Promise<boolean> {\n HMSLogger.d(TAG, '\u23F3 join: Negotiating over PUBLISH connection');\n if (!this.publishConnection) {\n HMSLogger.e(TAG, 'Publish peer connection not found, cannot negotiate');\n return false;\n }\n const offer = await this.publishConnection.createOffer();\n await this.publishConnection.setLocalDescription(offer);\n const serverSubDegrade = this.isFlagEnabled(InitFlags.FLAG_SERVER_SUB_DEGRADATION);\n const simulcast = this.isFlagEnabled(InitFlags.FLAG_SERVER_SIMULCAST);\n const onDemandTracks = this.isFlagEnabled(InitFlags.FLAG_ON_DEMAND_TRACKS);\n const answer = await this.signal.join(\n name,\n data,\n !autoSubscribeVideo,\n serverSubDegrade,\n simulcast,\n onDemandTracks,\n offer,\n );\n await this.publishConnection.setRemoteDescription(answer);\n for (const candidate of this.publishConnection.candidates) {\n await this.publishConnection.addIceCandidate(candidate);\n }\n\n this.publishConnection.initAfterJoin();\n return !!answer;\n }\n\n private async negotiateJoinNonWebRTC({ name, data, autoSubscribeVideo }: NegotiateJoinParams): Promise<boolean> {\n HMSLogger.d(TAG, '\u23F3 join: Negotiating Non-WebRTC');\n const serverSubDegrade = this.isFlagEnabled(InitFlags.FLAG_SERVER_SUB_DEGRADATION);\n const simulcast = this.isFlagEnabled(InitFlags.FLAG_SERVER_SIMULCAST);\n const onDemandTracks = this.isFlagEnabled(InitFlags.FLAG_ON_DEMAND_TRACKS);\n const response = await this.signal.join(\n name,\n data,\n !autoSubscribeVideo,\n serverSubDegrade,\n simulcast,\n onDemandTracks,\n );\n return !!response;\n }\n\n /**\n * Negotiate on first publish after changing role from non-webrtc peer to webrtc peer by sending offer\n */\n private async negotiateOnFirstPublish() {\n HMSLogger.d(TAG, '\u23F3 Negotiating offer over PUBLISH connection');\n if (!this.publishConnection) {\n HMSLogger.e(TAG, 'Publish peer connection not found, cannot negotiate');\n return false;\n }\n const offer = await this.publishConnection.createOffer(this.trackStates);\n await this.publishConnection.setLocalDescription(offer);\n const answer = await this.signal.offer(offer, this.trackStates);\n await this.publishConnection.setRemoteDescription(answer);\n for (const candidate of this.publishConnection.candidates) {\n await this.publishConnection.addIceCandidate(candidate);\n }\n\n this.publishConnection.initAfterJoin();\n return !!answer;\n }\n\n private async performPublishRenegotiation(constraints?: RTCOfferOptions) {\n HMSLogger.d(TAG, `\u23F3 [role=PUBLISH] onRenegotiationNeeded START`, this.trackStates);\n const callback = this.callbacks.get(RENEGOTIATION_CALLBACK_ID);\n if (!callback) {\n return;\n }\n\n if (!this.publishConnection) {\n HMSLogger.e(TAG, 'Publish peer connection not found, cannot renegotiate');\n return;\n }\n\n try {\n const offer = await this.publishConnection.createOffer(this.trackStates, constraints);\n await this.publishConnection.setLocalDescription(offer);\n HMSLogger.time(`renegotiation-offer-exchange`);\n const answer = await this.signal.offer(offer, this.trackStates);\n this.callbacks.delete(RENEGOTIATION_CALLBACK_ID);\n HMSLogger.timeEnd(`renegotiation-offer-exchange`);\n await this.publishConnection.setRemoteDescription(answer);\n callback.promise.resolve(true);\n HMSLogger.d(TAG, `[role=PUBLISH] onRenegotiationNeeded DONE \u2705`);\n } catch (err) {\n let ex: HMSException;\n if (err instanceof HMSException) {\n ex = err;\n } else {\n ex = ErrorFactory.GenericErrors.Unknown(HMSAction.PUBLISH, (err as Error).message);\n }\n\n callback!.promise.reject(ex);\n HMSLogger.d(TAG, `[role=PUBLISH] onRenegotiationNeeded FAILED \u274C`);\n }\n }\n\n private async handleIceConnectionFailure(role: HMSConnectionRole, error: HMSException) {\n // ice retry is already in progress(from disconnect state)\n if (\n this.retryScheduler.isTaskInProgress(\n HMSConnectionRole.Publish\n ? TransportFailureCategory.PublishIceConnectionFailed\n : TransportFailureCategory.SubscribeIceConnectionFailed,\n )\n ) {\n return;\n }\n\n if (role === HMSConnectionRole.Publish) {\n this.retryScheduler.schedule({\n category: TransportFailureCategory.PublishIceConnectionFailed,\n error,\n task: this.retryPublishIceFailedTask,\n originalState: TransportState.Joined,\n });\n } else {\n this.retryScheduler.schedule({\n category: TransportFailureCategory.SubscribeIceConnectionFailed,\n error,\n task: this.retrySubscribeIceFailedTask,\n originalState: TransportState.Joined,\n maxFailedRetries: 1,\n });\n }\n }\n\n private async internalConnect(token: string, initEndpoint: string, peerId: string) {\n HMSLogger.d(TAG, 'connect: started \u23F0');\n const connectRequestedAt = new Date();\n try {\n this.analyticsTimer.start(TimedEvent.INIT);\n this.initConfig = await InitService.fetchInitConfig({\n token,\n peerId,\n userAgent: this.store.getUserAgent(),\n initEndpoint,\n });\n this.analyticsTimer.end(TimedEvent.INIT);\n HTTPAnalyticsTransport.setWebsocketEndpoint(this.initConfig.endpoint);\n // if leave was called while init was going on, don't open websocket\n this.validateNotDisconnected('post init');\n await this.openSignal(token, peerId);\n this.observer.onConnected();\n this.store.setSimulcastEnabled(this.isFlagEnabled(InitFlags.FLAG_SERVER_SIMULCAST));\n HMSLogger.d(TAG, 'Adding Analytics Transport: JsonRpcSignal');\n this.analyticsEventsService.setTransport(this.analyticsSignalTransport);\n this.analyticsEventsService.flush();\n return this.initConfig;\n } catch (error) {\n if (this.state !== TransportState.Reconnecting) {\n this.eventBus.analytics.publish(\n AnalyticsEventFactory.connect(\n error as Error,\n this.getAdditionalAnalyticsProperties(),\n connectRequestedAt,\n new Date(),\n initEndpoint,\n ),\n );\n }\n HMSLogger.e(TAG, '\u274C internal connect: failed', error);\n throw error;\n }\n }\n\n // leave could be called between any two async tasks, which would make\n // the state disconnected instead of connecting, throw error for those cases.\n private validateNotDisconnected(stage: string) {\n if (this.state === TransportState.Disconnected) {\n HMSLogger.w(TAG, 'aborting join as transport state is disconnected');\n throw ErrorFactory.GenericErrors.ValidationFailed(`leave called before join could complete - stage=${stage}`);\n }\n }\n\n private async openSignal(token: string, peerId: string) {\n if (!this.initConfig) {\n throw ErrorFactory.APIErrors.InitConfigNotAvailable(HMSAction.INIT, 'Init Config not found');\n }\n\n HMSLogger.d(TAG, '\u23F3 internal connect: connecting to ws endpoint', this.initConfig.endpoint);\n const url = new URL(this.initConfig.endpoint);\n url.searchParams.set('peer', peerId);\n url.searchParams.set('token', token);\n url.searchParams.set('user_agent_v2', this.store.getUserAgent());\n this.endpoint = url.toString();\n this.analyticsTimer.start(TimedEvent.WEBSOCKET_CONNECT);\n await this.signal.open(this.endpoint);\n this.analyticsTimer.end(TimedEvent.WEBSOCKET_CONNECT);\n this.analyticsTimer.start(TimedEvent.ON_POLICY_CHANGE);\n this.analyticsTimer.start(TimedEvent.ROOM_STATE);\n HMSLogger.d(TAG, '\u2705 internal connect: connected to ws endpoint');\n }\n\n private async initRtcStatsMonitor() {\n this.webrtcInternals?.setPeerConnections({\n publish: this.publishConnection?.nativeConnection,\n subscribe: this.subscribeConnection?.nativeConnection,\n });\n\n if (this.isFlagEnabled(InitFlags.FLAG_PUBLISH_STATS)) {\n this.publishStatsAnalytics = new PublishStatsAnalytics(\n this.store,\n this.eventBus,\n this.initConfig?.config.publishStats?.maxSampleWindowSize,\n this.initConfig?.config.publishStats?.maxSamplePushInterval,\n );\n\n this.getWebrtcInternals()?.start();\n }\n }\n\n /**\n * Role does not need WebRTC(peer connections to communicate to SFU) if it cannot publish or subscribe to anything\n * @returns boolean denoting if a peer cannot publish(video, audio or screen) and cannot subscribe to any role\n */\n private doesRoleNeedWebRTC(role: HMSRole) {\n if (!this.isFlagEnabled(InitFlags.FLAG_NON_WEBRTC_DISABLE_OFFER)) {\n return true;\n }\n\n const isPublishing = Boolean(role.publishParams.allowed && role.publishParams.allowed?.length > 0);\n const isSubscribing = Boolean(\n role.subscribeParams.subscribeToRoles && role.subscribeParams.subscribeToRoles?.length > 0,\n );\n\n return isPublishing || isSubscribing;\n }\n\n private doesLocalPeerNeedWebRTC() {\n const localRole = this.store.getLocalPeer()?.role;\n if (!localRole) {\n return true;\n }\n\n return this.doesRoleNeedWebRTC(localRole);\n }\n\n private retryPublishIceFailedTask = async () => {\n /**\n * Proceed with the retry even if the connection state is connected as the offer could have failed\n * which will cause missing tiles if it is not sent again.\n * Do iceRestart only if not connected\n */\n if (this.publishConnection) {\n const p = new Promise<boolean>((resolve, reject) => {\n this.callbacks.set(RENEGOTIATION_CALLBACK_ID, {\n promise: { resolve, reject },\n action: HMSAction.RESTART_ICE,\n extra: {},\n });\n });\n await this.performPublishRenegotiation({ iceRestart: this.publishConnection.connectionState !== 'connected' });\n await p;\n }\n\n return true;\n };\n\n private retrySubscribeIceFailedTask = async () => {\n if (this.subscribeConnection && this.subscribeConnection.connectionState !== 'connected') {\n const p = new Promise<boolean>((resolve, reject) => {\n // Use subscribe constant string\n this.callbacks.set(SUBSCRIBE_ICE_CONNECTION_CALLBACK_ID, {\n promise: { resolve, reject },\n action: HMSAction.RESTART_ICE,\n extra: {},\n });\n });\n\n const timeout = new Promise(resolve => {\n setTimeout(resolve, SUBSCRIBE_TIMEOUT, false);\n });\n\n return Promise.race([p, timeout]) as Promise<boolean>;\n }\n\n return true;\n };\n\n private retrySignalDisconnectTask = async () => {\n HMSLogger.d(TAG, 'retrySignalDisconnectTask', { signalConnected: this.signal.isConnected });\n // Check if ws is disconnected - otherwise if only publishIce fails\n // and ws connect is success then we don't need to reconnect to WebSocket\n if (!this.signal.isConnected) {\n await this.internalConnect(\n this.joinParameters!.authToken,\n this.joinParameters!.endpoint,\n this.joinParameters!.peerId,\n );\n }\n\n // Only retry publish failed task after joining the call - not needed in preview signal reconnect\n const ok = this.store.getRoom()?.joinedAt\n ? this.signal.isConnected && (await this.retryPublishIceFailedTask())\n : this.signal.isConnected;\n // Send track update to sync local track state changes during reconnection\n this.signal.trackUpdate(this.trackStates);\n\n return ok;\n };\n\n private handleSubscribeConnectionConnected() {\n this.subscribeConnection?.logSelectedIceCandidatePairs();\n const callback = this.callbacks.get(SUBSCRIBE_ICE_CONNECTION_CALLBACK_ID);\n this.callbacks.delete(SUBSCRIBE_ICE_CONNECTION_CALLBACK_ID);\n\n if (callback) {\n callback.promise.resolve(true);\n }\n }\n\n private setTransportStateForConnect() {\n if (this.state === TransportState.Failed) {\n this.state = TransportState.Disconnected;\n }\n\n if (this.state !== TransportState.Disconnected && this.state !== TransportState.Reconnecting) {\n throw ErrorFactory.WebsocketMethodErrors.AlreadyJoined(\n HMSAction.JOIN,\n `Cannot join a meeting in ${this.state} state`,\n );\n }\n\n if (this.state === TransportState.Disconnected) {\n this.state = TransportState.Connecting;\n this.observer.onStateChange(this.state);\n }\n }\n\n private sendErrorAnalyticsEvent(error: HMSException, category: TransportFailureCategory) {\n const additionalProps = this.getAdditionalAnalyticsProperties();\n let event: AnalyticsEvent;\n switch (category) {\n case TransportFailureCategory.ConnectFailed:\n event = AnalyticsEventFactory.connect(error, additionalProps);\n break;\n case TransportFailureCategory.SignalDisconnect:\n event = AnalyticsEventFactory.disconnect(error, additionalProps);\n break;\n case TransportFailureCategory.JoinWSMessageFailed:\n event = AnalyticsEventFactory.join({\n error,\n time: this.analyticsTimer.getTimeTaken(TimedEvent.JOIN),\n init_response_time: this.analyticsTimer.getTimeTaken(TimedEvent.INIT),\n ws_connect_time: this.analyticsTimer.getTimeTaken(TimedEvent.WEBSOCKET_CONNECT),\n on_policy_change_time: this.analyticsTimer.getTimeTaken(TimedEvent.ON_POLICY_CHANGE),\n local_audio_track_time: this.analyticsTimer.getTimeTaken(TimedEvent.LOCAL_AUDIO_TRACK),\n local_video_track_time: this.analyticsTimer.getTimeTaken(TimedEvent.LOCAL_VIDEO_TRACK),\n retries_join: this.joinRetryCount,\n });\n break;\n case TransportFailureCategory.PublishIceConnectionFailed:\n event = AnalyticsEventFactory.publish({ error });\n break;\n case TransportFailureCategory.SubscribeIceConnectionFailed:\n event = AnalyticsEventFactory.subscribeFail(error);\n break;\n }\n this.eventBus.analytics.publish(event!);\n }\n\n getSubscribeConnection() {\n return this.subscribeConnection;\n }\n\n getAdditionalAnalyticsProperties(): AdditionalAnalyticsProperties {\n const network_info = getNetworkInfo();\n const document_hidden = typeof document !== 'undefined' && document.hidden;\n const num_degraded_tracks = this.store.getRemoteVideoTracks().filter(track => track.degraded).length;\n const publishBitrate = this.getWebrtcInternals()?.getCurrentStats()?.getLocalPeerStats()?.publish?.bitrate;\n const subscribeBitrate = this.getWebrtcInternals()?.getCurrentStats()?.getLocalPeerStats()?.subscribe?.bitrate;\n\n return {\n network_info,\n document_hidden,\n num_degraded_tracks,\n bitrate: {\n publish: publishBitrate,\n subscribe: subscribeBitrate,\n },\n max_sub_bitrate: this.maxSubscribeBitrate,\n recent_pong_response_times: this.signal.getPongResponseTimes(),\n transport_state: this.state,\n };\n }\n}\n", "import { ErrorFactory } from '../error/ErrorFactory';\nimport { HMSAction } from '../error/HMSAction';\n\n/**\n * @param retryCodes codes from the server reponse that needs to be retried\n */\n// eslint-disable-next-line complexity\nexport const fetchWithRetry = async (\n url: RequestInfo,\n options: RequestInit,\n retryCodes?: number[],\n): Promise<Response> => {\n const MAX_RETRIES = 4;\n let error = Error('something went wrong during fetch');\n for (let i = 0; i < MAX_RETRIES; i++) {\n try {\n // fetch will throw error if there's a browser-level issue\n const response = await fetch(url, options);\n const data = await response.clone().json();\n // throw error for additional codes to retry based on server's response\n if (retryCodes && retryCodes.length && !response.ok && retryCodes.includes(data.code)) {\n throw ErrorFactory.APIErrors.ServerErrors(data.code, HMSAction.GET_TOKEN, data.message, false);\n }\n\n return response;\n } catch (err) {\n error = err as unknown as Error;\n }\n }\n if (['Failed to fetch', 'NetworkError'].some(message => error.message.includes(message))) {\n throw ErrorFactory.APIErrors.EndpointUnreachable(HMSAction.GET_TOKEN, error.message);\n }\n throw error;\n};\n", "import { ErrorFactory } from '../error/ErrorFactory';\nimport { HMSAction } from '../error/HMSAction';\n\nexport interface AuthToken {\n roomId: string;\n userId: string;\n role: string;\n}\n\nexport default function decodeJWT(token?: string): AuthToken {\n if (!token || token.length === 0) {\n throw ErrorFactory.APIErrors.InvalidTokenFormat(\n HMSAction.INIT,\n 'Token cannot be an empty string or undefined or null',\n );\n }\n\n const parts = token.split('.');\n if (parts.length !== 3) {\n throw ErrorFactory.APIErrors.InvalidTokenFormat(\n HMSAction.INIT,\n `Expected 3 '.' separate fields - header, payload and signature respectively`,\n );\n }\n\n const payloadStr = atob(parts[1]);\n try {\n const payload = JSON.parse(payloadStr);\n return {\n roomId: payload.room_id,\n userId: payload.user_id,\n role: payload.role,\n } as AuthToken;\n } catch (err) {\n throw ErrorFactory.APIErrors.InvalidTokenFormat(\n HMSAction.INIT,\n `couldn't parse to json - ${(err as Error).message}`,\n );\n }\n}\n", "import Message from './models/HMSMessage';\nimport HMSRoom from './models/HMSRoom';\nimport { HMSLocalPeer, HMSPeer, HMSRemotePeer } from './models/peer';\nimport { LocalTrackManager } from './LocalTrackManager';\nimport { NetworkTestManager } from './NetworkTestManager';\nimport RoleChangeManager from './RoleChangeManager';\nimport { IStore, Store } from './store';\nimport { WakeLockManager } from './WakeLockManager';\nimport AnalyticsEvent from '../analytics/AnalyticsEvent';\nimport AnalyticsEventFactory from '../analytics/AnalyticsEventFactory';\nimport { HMSAnalyticsLevel } from '../analytics/AnalyticsEventLevel';\nimport { AnalyticsEventsService } from '../analytics/AnalyticsEventsService';\nimport { AnalyticsTimer, TimedEvent } from '../analytics/AnalyticsTimer';\nimport { AudioSinkManager } from '../audio-sink-manager';\nimport { DeviceManager } from '../device-manager';\nimport { AudioOutputManager } from '../device-manager/AudioOutputManager';\nimport { DeviceStorageManager } from '../device-manager/DeviceStorage';\nimport { ErrorCodes } from '../error/ErrorCodes';\nimport { ErrorFactory } from '../error/ErrorFactory';\nimport { HMSAction } from '../error/HMSAction';\nimport { HMSException } from '../error/HMSException';\nimport { EventBus } from '../events/EventBus';\nimport {\n HMSChangeMultiTrackStateParams,\n HMSConfig,\n HMSConnectionQualityListener,\n HMSDeviceChangeEvent,\n HMSFrameworkInfo,\n HMSMessageInput,\n HMSPlaylistType,\n HMSPreviewConfig,\n HMSRole,\n HMSRoleChangeRequest,\n HMSScreenShareConfig,\n TokenRequest,\n TokenRequestOptions,\n} from '../interfaces';\nimport { DeviceChangeListener } from '../interfaces/devices';\nimport { IErrorListener } from '../interfaces/error-listener';\nimport { HLSConfig, HLSTimedMetadata } from '../interfaces/hls-config';\nimport { HMSInterface } from '../interfaces/hms';\nimport { HMSLeaveRoomRequest } from '../interfaces/leave-room-request';\nimport { HMSPreviewListener } from '../interfaces/preview-listener';\nimport { RTMPRecordingConfig } from '../interfaces/rtmp-recording-config';\nimport InitialSettings from '../interfaces/settings';\nimport { HMSAudioListener, HMSTrackUpdate, HMSUpdateListener } from '../interfaces/update-listener';\nimport { HMSLocalStream } from '../media/streams/HMSLocalStream';\nimport {\n HMSLocalAudioTrack,\n HMSLocalTrack,\n HMSLocalVideoTrack,\n HMSRemoteTrack,\n HMSTrackSource,\n HMSTrackType,\n HMSVideoTrack,\n} from '../media/tracks';\nimport { HMSNotificationMethod, PeerLeaveRequestNotification } from '../notification-manager';\nimport { NotificationManager } from '../notification-manager/NotificationManager';\nimport { PlaylistManager } from '../playlist-manager';\nimport { SessionStore } from '../session-store';\nimport { InteractivityCenter } from '../session-store/interactivity-center';\nimport { InitConfig } from '../signal/init/models';\nimport HMSTransport from '../transport';\nimport ITransportObserver from '../transport/ITransportObserver';\nimport { TransportState } from '../transport/models/TransportState';\nimport { fetchWithRetry } from '../utils/fetch';\nimport decodeJWT from '../utils/jwt';\nimport HMSLogger, { HMSLogLevel } from '../utils/logger';\nimport { HMSAudioContextHandler } from '../utils/media';\nimport { isNode } from '../utils/support';\nimport { validateMediaDevicesExistence, validateRTCPeerConnection } from '../utils/validations';\n\nconst INITIAL_STATE = {\n published: false,\n isInitialised: false,\n isReconnecting: false,\n isPreviewInProgress: false,\n isPreviewCalled: false,\n isJoinInProgress: false,\n deviceManagersInitialised: false,\n};\n\nexport class HMSSdk implements HMSInterface {\n private transport!: HMSTransport;\n private readonly TAG = '[HMSSdk]:';\n private listener?: HMSUpdateListener;\n private errorListener?: IErrorListener;\n private deviceChangeListener?: DeviceChangeListener;\n private audioListener?: HMSAudioListener;\n private store!: IStore;\n private notificationManager?: NotificationManager;\n private deviceManager!: DeviceManager;\n private audioSinkManager!: AudioSinkManager;\n private playlistManager!: PlaylistManager;\n private audioOutput!: AudioOutputManager;\n private transportState: TransportState = TransportState.Disconnected;\n private roleChangeManager?: RoleChangeManager;\n private localTrackManager!: LocalTrackManager;\n private analyticsEventsService!: AnalyticsEventsService;\n private analyticsTimer = new AnalyticsTimer();\n private eventBus!: EventBus;\n private networkTestManager!: NetworkTestManager;\n private wakeLockManager!: WakeLockManager;\n private sessionStore!: SessionStore;\n private interactivityCenter!: InteractivityCenter;\n private sdkState = { ...INITIAL_STATE };\n private frameworkInfo?: HMSFrameworkInfo;\n\n private initNotificationManager() {\n if (!this.notificationManager) {\n this.notificationManager = new NotificationManager(\n this.store,\n this.eventBus,\n this.transport!,\n this.listener,\n this.audioListener,\n );\n }\n }\n\n private initStoreAndManagers() {\n if (this.sdkState.isInitialised) {\n /**\n * Set listener after both join and preview, since they can have different listeners\n */\n this.notificationManager?.setListener(this.listener);\n this.audioSinkManager.setListener(this.listener);\n this.interactivityCenter.setListener(this.listener);\n return;\n }\n\n this.sdkState.isInitialised = true;\n this.store = new Store();\n this.eventBus = new EventBus();\n this.wakeLockManager = new WakeLockManager();\n this.networkTestManager = new NetworkTestManager(this.eventBus, this.listener);\n this.playlistManager = new PlaylistManager(this, this.eventBus);\n this.deviceManager = new DeviceManager(this.store, this.eventBus);\n this.audioSinkManager = new AudioSinkManager(this.store, this.deviceManager, this.eventBus);\n this.audioOutput = new AudioOutputManager(this.deviceManager, this.audioSinkManager);\n this.audioSinkManager.setListener(this.listener);\n this.eventBus.autoplayError.subscribe(this.handleAutoplayError);\n this.localTrackManager = new LocalTrackManager(\n this.store,\n this.observer,\n this.deviceManager,\n this.eventBus,\n this.analyticsTimer,\n );\n this.analyticsEventsService = new AnalyticsEventsService(this.store);\n this.transport = new HMSTransport(\n this.observer,\n this.deviceManager,\n this.store,\n this.eventBus,\n this.analyticsEventsService,\n this.analyticsTimer,\n );\n this.sessionStore = new SessionStore(this.transport);\n this.interactivityCenter = new InteractivityCenter(this.transport, this.store, this.listener);\n\n /**\n * Note: Subscribe to events here right after creating stores and managers\n * to not miss events that are published before the handlers are subscribed.\n */\n this.eventBus.analytics.subscribe(this.sendAnalyticsEvent);\n this.eventBus.deviceChange.subscribe(this.handleDeviceChange);\n this.eventBus.audioPluginFailed.subscribe(this.handleAudioPluginError);\n }\n\n private validateJoined(name: string) {\n if (!this.localPeer) {\n throw ErrorFactory.GenericErrors.NotConnected(HMSAction.VALIDATION, `Not connected - ${name}`);\n }\n }\n\n // @ts-ignore\n private sendHLSAnalytics(error: HMSException) {\n this.sendAnalyticsEvent(AnalyticsEventFactory.hlsPlayerError(error));\n }\n\n async refreshDevices() {\n this.validateJoined('refreshDevices');\n await this.deviceManager.init(true);\n }\n\n getWebrtcInternals() {\n return this.transport?.getWebrtcInternals();\n }\n\n getSessionStore() {\n return this.sessionStore;\n }\n\n getPlaylistManager(): PlaylistManager {\n return this.playlistManager;\n }\n\n getRecordingState() {\n return this.store.getRoom()?.recording;\n }\n\n getRTMPState() {\n return this.store.getRoom()?.rtmp;\n }\n\n getHLSState() {\n return this.store.getRoom()?.hls;\n }\n\n getTemplateAppData() {\n return this.store.getTemplateAppData();\n }\n\n getInteractivityCenter() {\n return this.interactivityCenter;\n }\n\n private handleAutoplayError = (error: HMSException) => {\n this.errorListener?.onError?.(error);\n };\n\n private get localPeer(): HMSLocalPeer | undefined {\n return this.store?.getLocalPeer();\n }\n\n private observer: ITransportObserver = {\n onNotification: (message: any) => {\n if (message.method === HMSNotificationMethod.PEER_LEAVE_REQUEST) {\n this.handlePeerLeaveRequest(message.params as PeerLeaveRequestNotification);\n return;\n }\n\n switch (message.method) {\n case HMSNotificationMethod.POLICY_CHANGE:\n this.analyticsTimer.end(TimedEvent.ON_POLICY_CHANGE);\n break;\n case HMSNotificationMethod.PEER_LIST:\n this.analyticsTimer.end(TimedEvent.PEER_LIST);\n this.sendJoinAnalyticsEvent(this.sdkState.isPreviewCalled);\n break;\n case HMSNotificationMethod.ROOM_STATE:\n this.analyticsTimer.end(TimedEvent.ROOM_STATE);\n break;\n default:\n }\n\n this.notificationManager?.handleNotification(message, this.sdkState.isReconnecting);\n },\n\n onConnected: () => {\n this.initNotificationManager();\n },\n\n onTrackAdd: (track: HMSRemoteTrack) => {\n this.notificationManager?.handleTrackAdd(track);\n },\n\n onTrackRemove: (track: HMSRemoteTrack) => {\n this.notificationManager?.handleTrackRemove(track);\n },\n\n onFailure: (exception: HMSException) => {\n this.errorListener?.onError(exception);\n },\n\n onStateChange: async (state: TransportState, error?: HMSException) => {\n const handleFailedState = async (error?: HMSException) => {\n await this.internalLeave(true, error);\n /**\n * no need to call onError here when preview/join is in progress\n * since preview/join will call onError when they receive leave event from the above call\n */\n if (!this.sdkState.isPreviewInProgress && !this.sdkState.isJoinInProgress) {\n this.errorListener?.onError?.(error!);\n }\n this.sdkState.isReconnecting = false;\n };\n\n switch (state) {\n case TransportState.Preview:\n case TransportState.Joined:\n this.initNotificationManager();\n if (this.transportState === TransportState.Reconnecting) {\n this.listener?.onReconnected();\n }\n break;\n case TransportState.Failed:\n await handleFailedState(error);\n break;\n case TransportState.Reconnecting:\n this.sdkState.isReconnecting = true;\n this.listener?.onReconnecting(error!);\n break;\n }\n\n this.transportState = state;\n HMSLogger.d(this.TAG, 'Transport State Change', this.transportState);\n },\n };\n\n private handlePeerLeaveRequest = (message: PeerLeaveRequestNotification) => {\n const peer = message.requested_by ? this.store.getPeerById(message.requested_by) : undefined;\n const request: HMSLeaveRoomRequest = {\n roomEnded: message.room_end,\n reason: message.reason,\n requestedBy: peer,\n };\n this.listener?.onRemovedFromRoom(request);\n this.internalLeave(false);\n };\n\n async preview(config: HMSPreviewConfig, listener: HMSPreviewListener) {\n validateMediaDevicesExistence();\n validateRTCPeerConnection();\n\n if (this.sdkState.isPreviewInProgress) {\n return Promise.reject(\n ErrorFactory.GenericErrors.PreviewAlreadyInProgress(HMSAction.PREVIEW, 'Preview already called'),\n );\n }\n\n this.analyticsTimer.start(TimedEvent.PREVIEW);\n this.setUpPreview(config, listener);\n\n // Request permissions and populate devices before waiting for policy\n if (config.alwaysRequestPermissions) {\n this.localTrackManager.requestPermissions().then(async () => {\n await this.initDeviceManagers();\n });\n }\n\n let initSuccessful = false;\n let networkTestFinished = false;\n const timerId = setTimeout(() => {\n // If init or network is not done by 3s send -1\n if (!initSuccessful || !networkTestFinished) {\n this.listener?.onNetworkQuality?.(-1);\n }\n }, 3000);\n return new Promise<void>((resolve, reject) => {\n const policyHandler = async () => {\n if (this.localPeer) {\n const newRole = config.asRole && this.store.getPolicyForRole(config.asRole);\n this.localPeer.asRole = newRole || this.localPeer.role;\n }\n const tracks = await this.localTrackManager.getTracksToPublish(config.settings);\n tracks.forEach(track => this.setLocalPeerTrack(track));\n this.localPeer?.audioTrack && this.initPreviewTrackAudioLevelMonitor();\n await this.initDeviceManagers();\n this.sdkState.isPreviewInProgress = false;\n this.analyticsTimer.end(TimedEvent.PREVIEW);\n const room = this.store.getRoom();\n if (room) {\n listener.onPreview(room, tracks);\n }\n this.sendPreviewAnalyticsEvent();\n resolve();\n };\n\n const errorHandler = (ex?: HMSException) => {\n this.analyticsTimer.end(TimedEvent.PREVIEW);\n ex && this.errorListener?.onError(ex);\n this.sendPreviewAnalyticsEvent(ex);\n this.sdkState.isPreviewInProgress = false;\n reject(ex as HMSException);\n };\n\n this.eventBus.policyChange.subscribeOnce(policyHandler);\n this.eventBus.leave.subscribeOnce(errorHandler);\n\n this.transport\n .preview(\n config.authToken,\n config.initEndpoint!,\n this.localPeer!.peerId,\n { name: config.userName, metaData: config.metaData || '' },\n config.autoVideoSubscribe,\n )\n .then((initConfig: InitConfig | void) => {\n initSuccessful = true;\n clearTimeout(timerId);\n if (initConfig && config.captureNetworkQualityInPreview) {\n this.networkTestManager.start(initConfig.config?.networkHealth).then(() => {\n networkTestFinished = true;\n });\n }\n })\n .catch(errorHandler);\n });\n }\n\n private handleDeviceChange = (event: HMSDeviceChangeEvent) => {\n HMSLogger.d(this.TAG, 'Device Change event', event);\n this.deviceChangeListener?.onDeviceChange?.(event);\n if (event.error && event.type) {\n const track = event.type.includes('audio') ? this.localPeer?.audioTrack : this.localPeer?.videoTrack;\n this.errorListener?.onError(event.error);\n if (\n [\n ErrorCodes.TracksErrors.CANT_ACCESS_CAPTURE_DEVICE,\n ErrorCodes.TracksErrors.DEVICE_IN_USE,\n ErrorCodes.TracksErrors.DEVICE_NOT_AVAILABLE,\n ].includes(event.error.code) &&\n track\n ) {\n track.setEnabled(false);\n this.listener?.onTrackUpdate(HMSTrackUpdate.TRACK_MUTED, track, this.localPeer!);\n }\n }\n };\n\n private handleAudioPluginError = (error: HMSException) => {\n HMSLogger.e(this.TAG, 'Audio Plugin Error event', error);\n this.errorListener?.onError(error);\n };\n\n async join(config: HMSConfig, listener: HMSUpdateListener) {\n validateMediaDevicesExistence();\n validateRTCPeerConnection();\n\n if (this.sdkState.isPreviewInProgress) {\n throw ErrorFactory.GenericErrors.NotReady(HMSAction.JOIN, \"Preview is in progress, can't join\");\n }\n\n this.analyticsTimer.start(TimedEvent.JOIN);\n this.sdkState.isJoinInProgress = true;\n\n const { roomId, userId, role } = decodeJWT(config.authToken);\n const previewRole = this.localPeer?.asRole?.name || this.localPeer?.role?.name;\n this.networkTestManager?.stop();\n this.listener = listener;\n this.commonSetup(config, roomId, listener);\n this.removeDevicesFromConfig(config);\n this.store.setConfig(config);\n /** set after config since we need config to get env for user agent */\n this.store.createAndSetUserAgent(this.frameworkInfo);\n HMSAudioContextHandler.resumeContext();\n // acquire screen lock to stay awake while in call\n const storeConfig = this.store.getConfig();\n if (storeConfig?.autoManageWakeLock) {\n this.wakeLockManager.acquireLock();\n }\n\n if (!this.localPeer) {\n this.createAndAddLocalPeerToStore(config, role, userId);\n } else {\n this.localPeer.name = config.userName;\n this.localPeer.role = this.store.getPolicyForRole(role);\n this.localPeer.customerUserId = userId;\n this.localPeer.metadata = config.metaData;\n delete this.localPeer.asRole;\n }\n\n this.roleChangeManager = new RoleChangeManager(\n this.store,\n this.transport,\n this.deviceManager,\n this.getAndPublishTracks.bind(this),\n this.removeTrack.bind(this),\n this.listener,\n );\n this.eventBus.localRoleUpdate.subscribe(this.handleLocalRoleUpdate);\n\n HMSLogger.d(this.TAG, `\u23F3 Joining room ${roomId}`);\n\n HMSLogger.time(`join-room-${roomId}`);\n\n try {\n await this.transport.join(\n config.authToken,\n this.localPeer!.peerId,\n { name: config.userName, metaData: config.metaData! },\n config.initEndpoint!,\n config.autoVideoSubscribe,\n );\n HMSLogger.d(this.TAG, `\u2705 Joined room ${roomId}`);\n this.analyticsTimer.start(TimedEvent.PEER_LIST);\n await this.notifyJoin();\n this.sdkState.isJoinInProgress = false;\n await this.publish(config.settings, previewRole);\n } catch (error) {\n this.analyticsTimer.end(TimedEvent.JOIN);\n this.sdkState.isJoinInProgress = false;\n this.listener?.onError(error as HMSException);\n this.sendJoinAnalyticsEvent(this.sdkState.isPreviewCalled, error as HMSException);\n HMSLogger.e(this.TAG, 'Unable to join room', error);\n throw error;\n }\n HMSLogger.timeEnd(`join-room-${roomId}`);\n }\n\n private stringifyMetadata(config: HMSConfig) {\n if (config.metaData && typeof config.metaData !== 'string') {\n config.metaData = JSON.stringify(config.metaData);\n } else if (!config.metaData) {\n config.metaData = '';\n }\n }\n\n private cleanup() {\n this.cleanDeviceManagers();\n this.eventBus.analytics.unsubscribe(this.sendAnalyticsEvent);\n this.analyticsTimer.cleanup();\n DeviceStorageManager.cleanup();\n this.playlistManager.cleanup();\n this.wakeLockManager?.cleanup();\n LocalTrackManager.cleanup();\n this.notificationManager = undefined;\n HMSLogger.cleanup();\n this.sdkState = { ...INITIAL_STATE };\n /**\n * when leave is called after preview itself without join.\n * Store won't have the tracks in this case\n */\n if (this.localPeer) {\n this.localPeer.audioTrack?.cleanup();\n this.localPeer.audioTrack = undefined;\n this.localPeer.videoTrack?.cleanup();\n this.localPeer.videoTrack = undefined;\n }\n this.store.cleanup();\n this.listener = undefined;\n if (this.roleChangeManager) {\n this.eventBus.localRoleUpdate.unsubscribe(this.handleLocalRoleUpdate);\n }\n }\n\n leave(notifyServer?: boolean) {\n return this.internalLeave(notifyServer);\n }\n\n private async internalLeave(notifyServer = true, error?: HMSException) {\n const room = this.store?.getRoom();\n if (room) {\n const roomId = room.id;\n this.networkTestManager?.stop();\n this.eventBus.leave.publish(error);\n HMSLogger.d(this.TAG, `\u23F3 Leaving room ${roomId}`);\n // browsers often put limitation on amount of time a function set on window onBeforeUnload can take in case of\n // tab refresh or close. Therefore prioritise the leave action over anything else, if tab is closed/refreshed\n // we would want leave to succeed to stop stucked peer for others. The followup cleanup however is important\n // for cases where uses stays on the page post leave.\n await this.transport?.leave(notifyServer);\n this.cleanup();\n HMSLogger.d(this.TAG, `\u2705 Left room ${roomId}`);\n }\n }\n\n async getAuthTokenByRoomCode(tokenRequest: TokenRequest, tokenRequestOptions?: TokenRequestOptions): Promise<string> {\n const tokenAPIURL = (tokenRequestOptions || {}).endpoint || 'https://auth.100ms.live/v2/token';\n this.analyticsTimer.start(TimedEvent.GET_TOKEN);\n const response = await fetchWithRetry(\n tokenAPIURL,\n {\n method: 'POST',\n body: JSON.stringify({ code: tokenRequest.roomCode, user_id: tokenRequest.userId }),\n },\n [429, 500, 501, 502, 503, 504, 505, 506, 507, 508, 509, 510, 511],\n );\n\n const data = await response.json();\n this.analyticsTimer.end(TimedEvent.GET_TOKEN);\n\n if (!response.ok) {\n throw ErrorFactory.APIErrors.ServerErrors(data.code, HMSAction.GET_TOKEN, data.message, false);\n }\n\n const { token } = data;\n if (!token) {\n throw Error(data.message);\n }\n return token;\n }\n\n getLocalPeer() {\n return this.store.getLocalPeer();\n }\n\n getPeers() {\n return this.store.getPeers();\n }\n\n getPeerMap() {\n return this.store.getPeerMap();\n }\n\n getAudioOutput() {\n return this.audioOutput;\n }\n\n sendMessage(type: string, message: string) {\n this.sendMessageInternal({ message, type });\n }\n\n async sendBroadcastMessage(message: string, type?: string) {\n return await this.sendMessageInternal({ message, type });\n }\n\n async sendGroupMessage(message: string, roles: HMSRole[], type?: string) {\n const knownRoles = this.store.getKnownRoles();\n const recipientRoles =\n roles.filter(role => {\n return knownRoles[role.name];\n }) || [];\n if (recipientRoles.length === 0) {\n throw ErrorFactory.GenericErrors.ValidationFailed('No valid role is present', roles);\n }\n return await this.sendMessageInternal({ message, recipientRoles: roles, type });\n }\n\n async sendDirectMessage(message: string, peer: HMSPeer, type?: string) {\n const recipientPeer = this.store.getPeerById(peer.peerId);\n if (!recipientPeer) {\n throw ErrorFactory.GenericErrors.ValidationFailed('Invalid peer - peer not present in the room', peer);\n }\n if (this.localPeer?.peerId === peer.peerId) {\n throw ErrorFactory.GenericErrors.ValidationFailed('Cannot send message to self');\n }\n return await this.sendMessageInternal({ message, recipientPeer: peer, type });\n }\n\n private async sendMessageInternal({ recipientRoles, recipientPeer, type = 'chat', message }: HMSMessageInput) {\n if (message.replace(/\\u200b/g, ' ').trim() === '') {\n HMSLogger.w(this.TAG, 'sendMessage', 'Ignoring empty message send');\n throw ErrorFactory.GenericErrors.ValidationFailed('Empty message not allowed');\n }\n const hmsMessage = new Message({\n sender: this.localPeer!,\n type,\n message,\n recipientPeer,\n recipientRoles,\n time: new Date(),\n });\n HMSLogger.d(this.TAG, 'Sending Message: ', hmsMessage);\n const response = await this.transport.sendMessage(hmsMessage);\n hmsMessage.time = new Date(response.timestamp);\n hmsMessage.id = response.message_id;\n return hmsMessage;\n }\n\n async startScreenShare(onStop: () => void, config?: HMSScreenShareConfig) {\n const publishParams = this.store.getPublishParams();\n if (!publishParams) {\n return;\n }\n\n const { allowed } = publishParams;\n const canPublishScreen = allowed && allowed.includes('screen');\n\n if (!canPublishScreen) {\n HMSLogger.e(this.TAG, `Role ${this.localPeer?.role} cannot share screen`);\n return;\n }\n\n if (this.localPeer?.auxiliaryTracks?.find(track => track.source === 'screen')) {\n throw Error('Cannot share multiple screens');\n }\n\n const tracks = await this.getScreenshareTracks(onStop, config);\n if (!this.localPeer) {\n HMSLogger.d(this.TAG, 'Screenshared when not connected');\n tracks.forEach(track => {\n track.cleanup();\n });\n return;\n }\n await this.transport.publish(tracks);\n tracks.forEach(track => {\n track.peerId = this.localPeer?.peerId;\n this.localPeer?.auxiliaryTracks.push(track);\n this.listener?.onTrackUpdate(HMSTrackUpdate.TRACK_ADDED, track, this.localPeer!);\n });\n }\n\n private async stopEndedScreenshare(onStop: () => void) {\n HMSLogger.d(this.TAG, `\u2705 Screenshare ended natively`);\n await this.stopScreenShare();\n onStop();\n }\n\n async stopScreenShare() {\n HMSLogger.d(this.TAG, `\u2705 Screenshare ended from app`);\n const screenTracks = this.localPeer?.auxiliaryTracks.filter(t => t.source === 'screen');\n if (screenTracks) {\n for (const track of screenTracks) {\n await this.removeTrack(track.trackId);\n }\n }\n }\n\n async addTrack(track: MediaStreamTrack, source: HMSTrackSource = 'regular'): Promise<void> {\n if (!track) {\n HMSLogger.w(this.TAG, 'Please pass a valid MediaStreamTrack');\n return;\n }\n if (!this.localPeer) {\n throw ErrorFactory.GenericErrors.NotConnected(HMSAction.VALIDATION, 'No local peer present, cannot addTrack');\n }\n const isTrackPresent = this.localPeer.auxiliaryTracks.find(t => t.trackId === track.id);\n if (isTrackPresent) {\n return;\n }\n\n const type = track.kind;\n const nativeStream = new MediaStream([track]);\n const stream = new HMSLocalStream(nativeStream);\n\n const TrackKlass = type === 'audio' ? HMSLocalAudioTrack : HMSLocalVideoTrack;\n const hmsTrack = new TrackKlass(stream, track, source, this.eventBus);\n this.setPlaylistSettings({\n track,\n hmsTrack,\n source,\n });\n\n await this.transport?.publish([hmsTrack]);\n hmsTrack.peerId = this.localPeer?.peerId;\n this.localPeer?.auxiliaryTracks.push(hmsTrack);\n this.listener?.onTrackUpdate(HMSTrackUpdate.TRACK_ADDED, hmsTrack, this.localPeer!);\n }\n\n async removeTrack(trackId: string, internal = false) {\n if (!this.localPeer) {\n throw ErrorFactory.GenericErrors.NotConnected(HMSAction.VALIDATION, 'No local peer present, cannot removeTrack');\n }\n const trackIndex = this.localPeer.auxiliaryTracks.findIndex(t => t.trackId === trackId);\n if (trackIndex > -1) {\n const track = this.localPeer.auxiliaryTracks[trackIndex];\n if (track.isPublished) {\n await this.transport!.unpublish([track]);\n } else {\n await track.cleanup();\n }\n // Stop local playback when playlist track is removed\n if (!internal) {\n this.stopPlaylist(track);\n }\n this.localPeer.auxiliaryTracks.splice(trackIndex, 1);\n this.listener?.onTrackUpdate(HMSTrackUpdate.TRACK_REMOVED, track, this.localPeer);\n } else {\n HMSLogger.w(this.TAG, `No track found for ${trackId}`);\n }\n }\n\n setAnalyticsLevel(level: HMSAnalyticsLevel) {\n this.analyticsEventsService.level = level;\n }\n\n setLogLevel(level: HMSLogLevel) {\n HMSLogger.level = level;\n }\n\n addAudioListener(audioListener: HMSAudioListener) {\n this.audioListener = audioListener;\n this.notificationManager?.setAudioListener(audioListener);\n }\n\n addConnectionQualityListener(qualityListener: HMSConnectionQualityListener) {\n this.notificationManager?.setConnectionQualityListener(qualityListener);\n }\n\n async changeRole(forPeer: HMSPeer, toRole: string, force = false) {\n if (!forPeer.role || forPeer.role.name === toRole) {\n return;\n }\n\n await this.transport?.changeRoleOfPeer(forPeer, toRole, force);\n }\n\n async changeRoleOfPeer(forPeer: HMSPeer, toRole: string, force = false) {\n if (!forPeer.role || forPeer.role.name === toRole) {\n return;\n }\n\n await this.transport?.changeRoleOfPeer(forPeer, toRole, force);\n }\n\n async changeRoleOfPeersWithRoles(roles: HMSRole[], toRole: string) {\n if (roles.length <= 0 || !toRole) {\n return;\n }\n\n await this.transport?.changeRoleOfPeersWithRoles(roles, toRole);\n }\n\n async acceptChangeRole(request: HMSRoleChangeRequest) {\n await this.transport?.acceptRoleChange(request);\n }\n\n async endRoom(lock: boolean, reason: string) {\n if (!this.localPeer) {\n throw ErrorFactory.GenericErrors.NotConnected(HMSAction.VALIDATION, 'No local peer present, cannot end room');\n }\n await this.transport?.endRoom(lock, reason);\n await this.leave();\n }\n\n async removePeer(peer: HMSRemotePeer, reason: string) {\n if (!this.localPeer) {\n throw ErrorFactory.GenericErrors.NotConnected(HMSAction.VALIDATION, 'No local peer present, cannot remove peer');\n }\n\n if (!this.store.getPeerById(peer.peerId)) {\n throw ErrorFactory.GenericErrors.ValidationFailed('Invalid peer, given peer not present in room', peer);\n }\n await this.transport?.removePeer(peer.peerId, reason);\n }\n\n async startRTMPOrRecording(params: RTMPRecordingConfig) {\n if (!this.localPeer) {\n throw ErrorFactory.GenericErrors.NotConnected(\n HMSAction.VALIDATION,\n 'No local peer present, cannot start streaming or recording',\n );\n }\n await this.transport?.startRTMPOrRecording(params);\n }\n\n async stopRTMPAndRecording() {\n if (!this.localPeer) {\n throw ErrorFactory.GenericErrors.NotConnected(\n HMSAction.VALIDATION,\n 'No local peer present, cannot stop streaming or recording',\n );\n }\n await this.transport?.stopRTMPOrRecording();\n }\n\n async startHLSStreaming(params?: HLSConfig) {\n if (!this.localPeer) {\n throw ErrorFactory.GenericErrors.NotConnected(\n HMSAction.VALIDATION,\n 'No local peer present, cannot start HLS streaming',\n );\n }\n await this.transport?.startHLSStreaming(params);\n }\n\n async stopHLSStreaming(params?: HLSConfig) {\n if (!this.localPeer) {\n throw ErrorFactory.GenericErrors.NotConnected(\n HMSAction.VALIDATION,\n 'No local peer present, cannot stop HLS streaming',\n );\n }\n await this.transport?.stopHLSStreaming(params);\n }\n\n async sendHLSTimedMetadata(metadataList: HLSTimedMetadata[]) {\n this.validateJoined('sendHLSTimedMetadata');\n await this.transport?.sendHLSTimedMetadata(metadataList);\n }\n\n async changeName(name: string) {\n this.validateJoined('changeName');\n await this.transport?.changeName(name);\n this.notificationManager?.updateLocalPeer({ name });\n }\n\n async changeMetadata(metadata: string) {\n this.validateJoined('changeMetadata');\n await this.transport?.changeMetadata(metadata);\n this.notificationManager?.updateLocalPeer({ metadata });\n }\n\n async setSessionMetadata(metadata: any) {\n await this.transport.setSessionMetadata({ key: 'default', data: metadata });\n }\n\n async getSessionMetadata() {\n const response = await this.transport.getSessionMetadata('default');\n return response.data;\n }\n\n getRoles(): HMSRole[] {\n return Object.values(this.store.getKnownRoles());\n }\n\n async changeTrackState(forRemoteTrack: HMSRemoteTrack, enabled: boolean) {\n if (forRemoteTrack.type === HMSTrackType.VIDEO && forRemoteTrack.source !== 'regular') {\n HMSLogger.w(this.TAG, `Muting non-regular video tracks is currently not supported`);\n return;\n }\n\n if (forRemoteTrack.enabled === enabled) {\n HMSLogger.w(this.TAG, `Aborting change track state, track already has enabled - ${enabled}`, forRemoteTrack);\n return;\n }\n\n if (!this.store.getTrackById(forRemoteTrack.trackId)) {\n throw ErrorFactory.GenericErrors.ValidationFailed('No track found for change track state', forRemoteTrack);\n }\n\n const peer = this.store.getPeerByTrackId(forRemoteTrack.trackId);\n\n if (!peer) {\n throw ErrorFactory.GenericErrors.ValidationFailed('No peer found for change track state', forRemoteTrack);\n }\n\n await this.transport?.changeTrackState({\n requested_for: peer.peerId,\n track_id: forRemoteTrack.trackId,\n stream_id: forRemoteTrack.stream.id,\n mute: !enabled,\n });\n }\n\n async changeMultiTrackState(params: HMSChangeMultiTrackStateParams) {\n if (typeof params.enabled !== 'boolean') {\n throw ErrorFactory.GenericErrors.ValidationFailed('Pass a boolean for enabled');\n }\n const { enabled, roles, type, source } = params;\n await this.transport?.changeMultiTrackState({\n value: !enabled,\n type,\n source,\n roles: roles?.map(role => role?.name),\n });\n }\n\n setFrameworkInfo(frameworkInfo: HMSFrameworkInfo) {\n this.frameworkInfo = { ...this.frameworkInfo, ...frameworkInfo };\n }\n\n async attachVideo(track: HMSVideoTrack, videoElement: HTMLVideoElement) {\n const config = this.store.getConfig();\n if (config?.autoManageVideo) {\n track.attach(videoElement);\n } else {\n await track.addSink(videoElement);\n }\n }\n\n async detachVideo(track: HMSVideoTrack, videoElement: HTMLVideoElement) {\n const config = this.store.getConfig();\n if (config?.autoManageVideo) {\n track.detach(videoElement);\n } else {\n await track.removeSink(videoElement);\n }\n }\n\n private async publish(initialSettings?: InitialSettings, oldRole?: string) {\n if ([this.store.getPublishParams(), !this.sdkState.published, !isNode].every(value => !!value)) {\n // if preview asRole(oldRole) is used, use roleChangeManager to diff policy and publish, else do normal publish\n const publishAction =\n oldRole && oldRole !== this.localPeer?.role?.name\n ? () =>\n this.roleChangeManager?.diffRolesAndPublishTracks({\n oldRole: this.store.getPolicyForRole(oldRole),\n newRole: this.localPeer!.role!,\n })\n : () => this.getAndPublishTracks(initialSettings);\n\n await publishAction?.()?.catch(error => {\n HMSLogger.e(this.TAG, 'Error in publish', error);\n this.listener?.onError(error);\n });\n }\n }\n\n private async getAndPublishTracks(initialSettings?: InitialSettings) {\n const tracks = await this.localTrackManager.getTracksToPublish(initialSettings);\n await this.setAndPublishTracks(tracks);\n this.localPeer?.audioTrack?.initAudioLevelMonitor();\n this.sdkState.published = true;\n }\n\n private handleLocalRoleUpdate = async ({ oldRole, newRole }: { oldRole: HMSRole; newRole: HMSRole }) => {\n await this.transport.handleLocalRoleUpdate({ oldRole, newRole });\n await this.roleChangeManager?.handleLocalPeerRoleUpdate({ oldRole, newRole });\n };\n\n private async setAndPublishTracks(tracks: HMSLocalTrack[]) {\n for (const track of tracks) {\n await this.transport.publish([track]);\n this.setLocalPeerTrack(track);\n this.listener?.onTrackUpdate(HMSTrackUpdate.TRACK_ADDED, track, this.localPeer!);\n }\n await this.initDeviceManagers();\n }\n\n private setLocalPeerTrack(track: HMSLocalTrack) {\n track.peerId = this.localPeer?.peerId;\n switch (track.type) {\n case HMSTrackType.AUDIO:\n this.localPeer!.audioTrack = track as HMSLocalAudioTrack;\n break;\n\n case HMSTrackType.VIDEO:\n this.localPeer!.videoTrack = track as HMSLocalVideoTrack;\n break;\n }\n }\n\n private async initDeviceManagers() {\n // No need to initialise and add listeners if already initialised in preview\n if (this.sdkState.deviceManagersInitialised) {\n return;\n }\n this.sdkState.deviceManagersInitialised = true;\n await this.deviceManager.init();\n if (!(await this.deviceManager.updateOutputDevice(this.store.getConfig()?.settings?.audioOutputDeviceId))) {\n await this.deviceManager.updateOutputDevice(DeviceStorageManager.getSelection()?.audioOutput?.deviceId);\n }\n this.audioSinkManager.init(this.store.getConfig()?.audioSinkElementId);\n }\n\n private cleanDeviceManagers() {\n this.eventBus.deviceChange.unsubscribe(this.handleDeviceChange);\n this.eventBus.audioPluginFailed.unsubscribe(this.handleAudioPluginError);\n this.eventBus.autoplayError.unsubscribe(this.handleAutoplayError);\n this.deviceManager.cleanup();\n this.audioSinkManager.cleanup();\n }\n\n private initPreviewTrackAudioLevelMonitor() {\n const localAudioTrack = this.localPeer?.audioTrack;\n localAudioTrack?.initAudioLevelMonitor();\n this.eventBus.trackAudioLevelUpdate.subscribe(audioLevelUpdate => {\n const hmsSpeakers =\n audioLevelUpdate && audioLevelUpdate.track.trackId === localAudioTrack?.trackId\n ? [{ audioLevel: audioLevelUpdate.audioLevel, peer: this.localPeer!, track: localAudioTrack! }]\n : [];\n this.store.updateSpeakers(hmsSpeakers);\n this.audioListener?.onAudioLevelUpdate(hmsSpeakers);\n });\n this.eventBus.localAudioSilence.subscribe(this.sendAudioPresenceFailed);\n }\n\n private notifyJoin() {\n const localPeer = this.store.getLocalPeer();\n const room = this.store.getRoom();\n if (!room) {\n HMSLogger.w(this.TAG, 'notify join - room not present');\n return;\n }\n\n room.joinedAt = new Date();\n if (localPeer) {\n localPeer.joinedAt = room.joinedAt;\n }\n\n if (localPeer?.role) {\n this.analyticsTimer.end(TimedEvent.JOIN);\n this.listener?.onJoin(room);\n return;\n }\n\n return new Promise<void>((resolve, reject) => {\n this.eventBus.policyChange.subscribeOnce(() => {\n this.analyticsTimer.end(TimedEvent.JOIN);\n this.listener?.onJoin(room);\n resolve();\n });\n\n this.eventBus.leave.subscribeOnce(ex => {\n reject(ex);\n });\n });\n }\n\n /**\n * Init store and other managers, setup listeners, create local peer, room\n * @param {HMSConfig} config\n * @param {HMSPreviewListener} listener\n */\n private setUpPreview(config: HMSPreviewConfig, listener: HMSPreviewListener) {\n this.listener = listener as unknown as HMSUpdateListener;\n this.sdkState.isPreviewCalled = true;\n this.sdkState.isPreviewInProgress = true;\n const { roomId, userId, role } = decodeJWT(config.authToken);\n this.commonSetup(config, roomId, listener);\n this.store.setConfig(config);\n /** set after config since we need config to get env for user agent */\n this.store.createAndSetUserAgent(this.frameworkInfo);\n this.createAndAddLocalPeerToStore(config, role, userId, config.asRole);\n }\n\n /**\n * Set bitrate and dimensions for playlist track\n */\n private async setPlaylistSettings({\n track,\n hmsTrack,\n source,\n }: {\n track: MediaStreamTrack;\n hmsTrack: HMSLocalAudioTrack | HMSLocalVideoTrack;\n source: string;\n }) {\n if (source === 'videoplaylist') {\n const settings: { maxBitrate?: number; width?: number; height?: number } = {};\n if (track.kind === 'audio') {\n settings.maxBitrate = 64;\n } else {\n settings.maxBitrate = 1000;\n const { width, height } = track.getSettings();\n settings.width = width;\n settings.height = height;\n }\n // TODO: rt update from policy once policy is updated\n await hmsTrack.setSettings(settings);\n } else if (source === 'audioplaylist') {\n // TODO: rt update from policy once policy is updated\n await hmsTrack.setSettings({ maxBitrate: 64 });\n }\n }\n\n /**\n * @param {HMSConfig} config\n * @param {string} role\n * @param {string} userId\n */\n private createAndAddLocalPeerToStore(config: HMSConfig, role: string, userId: string, asRole?: string) {\n const policy = this.store.getPolicyForRole(role);\n const asRolePolicy = asRole ? this.store.getPolicyForRole(asRole) : undefined;\n const localPeer = new HMSLocalPeer({\n name: config.userName || '',\n customerUserId: userId,\n metadata: config.metaData || '',\n role: policy,\n // default value is the original role if user didn't pass asRole in config\n asRole: asRolePolicy || policy,\n });\n\n this.store.addPeer(localPeer);\n }\n\n /**\n * init managers and set listeners - common for join and preview\n * @param {HMSConfig} config\n * @param {string} roomId\n * @param {HMSPreviewListener | HMSUpdateListener} listener\n */\n private commonSetup(config: HMSConfig, roomId: string, listener: HMSPreviewListener | HMSUpdateListener) {\n this.stringifyMetadata(config);\n if (!config.initEndpoint) {\n config.initEndpoint = 'https://prod-init.100ms.live';\n }\n this.errorListener = listener;\n this.deviceChangeListener = listener;\n this.initStoreAndManagers();\n\n this.store.setErrorListener(this.errorListener);\n if (!this.store.getRoom()) {\n this.store.setRoom(new HMSRoom(roomId));\n }\n }\n\n /**\n * Remove deviceId's passed in config for join if preview was already called\n * @param {HMSConfig} config\n */\n private removeDevicesFromConfig(config: HMSConfig) {\n const storedConfig = this.store.getConfig();\n if (storedConfig && config.settings) {\n // preview was called\n delete config.settings.audioOutputDeviceId;\n delete config.settings.videoDeviceId;\n delete config.settings.audioInputDeviceId;\n }\n }\n\n /**\n * Get screenshare based on policy and audioOnly flag\n * @param {function} onStop\n * @param config\n * @returns\n */\n private async getScreenshareTracks(onStop: () => void, config?: HMSScreenShareConfig) {\n const [videoTrack, audioTrack] = await this.localTrackManager.getLocalScreen(config);\n\n const handleEnded = () => {\n this.stopEndedScreenshare(onStop);\n };\n\n const tracks = [];\n if (config?.audioOnly) {\n videoTrack.nativeTrack.stop();\n if (!audioTrack) {\n throw ErrorFactory.TracksErrors.NothingToReturn(\n HMSAction.TRACK,\n 'Select share audio when sharing screen',\n 'No audio found',\n );\n }\n tracks.push(audioTrack);\n audioTrack.nativeTrack.addEventListener('ended', handleEnded);\n } else {\n tracks.push(videoTrack);\n videoTrack.nativeTrack.addEventListener('ended', handleEnded);\n // audio track is not always available\n if (audioTrack) {\n tracks.push(audioTrack);\n }\n }\n return tracks;\n }\n\n private sendAudioPresenceFailed = () => {\n const error = ErrorFactory.TracksErrors.NoAudioDetected(HMSAction.PREVIEW);\n HMSLogger.w(this.TAG, 'Audio Presence Failure', this.transportState, error);\n // this.sendAnalyticsEvent(\n // AnalyticsEventFactory.audioDetectionFail(error, this.deviceManager.getCurrentSelection().audioInput),\n // );\n // this.listener?.onError(error);\n };\n\n private sendJoinAnalyticsEvent = (is_preview_called = false, error?: HMSException) => {\n this.eventBus.analytics.publish(\n AnalyticsEventFactory.join({\n error,\n ...this.analyticsTimer.getTimes(),\n time: this.analyticsTimer.getTimeTaken(TimedEvent.JOIN),\n is_preview_called,\n retries_join: this.transport.joinRetryCount,\n }),\n );\n };\n\n private sendPreviewAnalyticsEvent = (error?: HMSException) => {\n this.eventBus.analytics.publish(\n AnalyticsEventFactory.preview({\n error,\n ...this.analyticsTimer.getTimes(),\n time: this.analyticsTimer.getTimeTaken(TimedEvent.PREVIEW),\n }),\n );\n };\n\n private sendAnalyticsEvent = (event: AnalyticsEvent) => {\n this.analyticsEventsService.queue(event).flush();\n };\n\n private stopPlaylist(track: HMSLocalTrack) {\n if (track.source === 'audioplaylist') {\n this.playlistManager.stop(HMSPlaylistType.audio);\n } else if (track.source === 'videoplaylist') {\n this.playlistManager.stop(HMSPlaylistType.video);\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\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 commonShim.shimParameterlessSetLocalDescription(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.shimRTCIceCandidateRelayProtocol(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 commonShim.shimParameterlessSetLocalDescription(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 commonShim.shimParameterlessSetLocalDescription(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.shimRTCIceCandidateRelayProtocol(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", "/*\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 !window.navigator.userAgent) {\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 // 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';\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';\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\n ? {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';\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 // WebIDL input coercion and validation\n let sendEncodings = arguments[1] && arguments[1].sendEncodings;\n if (sendEncodings === undefined) {\n sendEncodings = [];\n }\n sendEncodings = [...sendEncodings];\n const shouldPerformCheck = sendEncodings.length > 0;\n if (shouldPerformCheck) {\n // If sendEncodings params are provided, validate grammar\n 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 = sendEncodings;\n sender.sendEncodings = 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/* 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'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.urls === undefined && server.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', {direction: 'recvonly'});\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', {direction: 'recvonly'});\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", "/*\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.substring(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 for (const key in parsedCandidate) {\n if (!(key in nativeCandidate)) {\n Object.defineProperty(nativeCandidate, key,\n {value: parsedCandidate[key]});\n }\n }\n\n // Override serializer to not serialize the extra attributes.\n nativeCandidate.toJSON = function toJSON() {\n return {\n candidate: nativeCandidate.candidate,\n sdpMid: nativeCandidate.sdpMid,\n sdpMLineIndex: nativeCandidate.sdpMLineIndex,\n usernameFragment: nativeCandidate.usernameFragment,\n };\n };\n return nativeCandidate;\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 shimRTCIceCandidateRelayProtocol(window) {\n if (!window.RTCIceCandidate || (window.RTCIceCandidate && 'relayProtocol' in\n window.RTCIceCandidate.prototype)) {\n return;\n }\n\n // Hook up the augmented candidate in onicecandidate and\n // addEventListener('icecandidate', ...)\n utils.wrapPeerConnectionEvent(window, 'icecandidate', e => {\n if (e.candidate) {\n const parsedCandidate = SDPUtils.parseCandidate(e.candidate.candidate);\n if (parsedCandidate.type === 'relay') {\n // This is a libwebrtc-specific mapping of local type preference\n // to relayProtocol.\n e.candidate.relayProtocol = {\n 0: 'tls',\n 1: 'tcp',\n 2: 'udp',\n }[parsedCandidate.priority >> 24];\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].substring(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// Note: Make sure to call this ahead of APIs that modify\n// setLocalDescription.length\nexport function shimParameterlessSetLocalDescription(window, browserDetails) {\n if (!(window.RTCPeerConnection && window.RTCPeerConnection.prototype)) {\n return;\n }\n const nativeSetLocalDescription =\n window.RTCPeerConnection.prototype.setLocalDescription;\n if (!nativeSetLocalDescription || nativeSetLocalDescription.length === 0) {\n return;\n }\n window.RTCPeerConnection.prototype.setLocalDescription =\n function setLocalDescription() {\n let desc = arguments[0] || {};\n if (typeof desc !== 'object' || (desc.type && desc.sdp)) {\n return nativeSetLocalDescription.apply(this, arguments);\n }\n // The remaining steps should technically happen when SLD comes off the\n // RTCPeerConnection's operations chain (not ahead of going on it), but\n // this is too difficult to shim. Instead, this shim only covers the\n // common case where the operations chain is empty. This is imperfect, but\n // should cover many cases. Rationale: Even if we can't reduce the glare\n // window to zero on imperfect implementations, there's value in tapping\n // into the perfect negotiation pattern that several browsers support.\n desc = {type: desc.type, sdp: desc.sdp};\n if (!desc.type) {\n switch (this.signalingState) {\n case 'stable':\n case 'have-local-offer':\n case 'have-remote-pranswer':\n desc.type = 'offer';\n break;\n default:\n desc.type = 'answer';\n break;\n }\n }\n if (desc.sdp || (desc.type !== 'offer' && desc.type !== 'answer')) {\n return nativeSetLocalDescription.apply(this, [desc]);\n }\n const func = desc.type === 'offer' ? this.createOffer : this.createAnswer;\n return func.apply(this)\n .then(d => nativeSetLocalDescription.apply(this, [d]));\n };\n}\n", "{\n \"version\": \"0.10.12\",\n \"license\": \"MIT\",\n \"main\": \"dist/index.cjs.js\",\n \"module\": \"dist/index.js\",\n \"typings\": \"dist/index.d.ts\",\n \"files\": [\n \"dist\",\n \"src\"\n ],\n \"engines\": {\n \"node\": \">=12\"\n },\n \"exports\": {\n \".\": {\n \"require\": \"./dist/index.cjs.js\",\n \"import\": \"./dist/index.js\",\n \"default\": \"./dist/index.js\"\n }\n },\n \"scripts\": {\n \"prestart\": \"rm -rf dist && yarn types:build\",\n \"start\": \"concurrently \\\"yarn dev\\\" \\\"yarn types\\\"\",\n \"dev\": \"node ../../scripts/dev\",\n \"build:only\": \"node ../../scripts/build\",\n \"build\": \"yarn build:only && yarn types:build\",\n \"types\": \"tsc -w\",\n \"types:build\": \"tsc -p tsconfig.json\",\n \"format\": \"prettier --write src/**/*.ts\",\n \"test\": \"jest --maxWorkers=1\",\n \"test:watch\": \"jest --watch\",\n \"test:coverage\": \"jest --coverage\",\n \"lint\": \"eslint -c ../../.eslintrc .\",\n \"lint:fix\": \"yarn lint --fix\",\n \"prepare\": \"yarn build\",\n \"size\": \"size-limit\",\n \"analyze\": \"size-limit --why\",\n \"docs\": \"rm -rf ./docs && typedoc && rm -f ./docs/README.md && mkdir ./docs/home &&mv ./docs/modules.md ./docs/home/content.md && node ../../scripts/docs-store && npx prettier --write './docs/**/*'\"\n },\n \"name\": \"@100mslive/hms-video-store\",\n \"author\": \"100ms\",\n \"sideEffects\": false,\n \"dependencies\": {\n \"@100mslive/hms-video\": \"0.9.12\",\n \"eventemitter2\": \"^6.4.7\",\n \"immer\": \"^9.0.6\",\n \"reselect\": \"4.0.0\",\n \"zustand\": \"3.5.7\"\n },\n \"devDependencies\": {\n \"ts-node\": \"^10.4.0\",\n \"tslib\": \"^2.2.0\"\n },\n \"description\": \"This is an addon to the core sdk provided by 100ms. It abstracts away the intricacies of data management and provides a flux based reactive data store where data flows in only one direction.\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/100mslive/hms-video-store.git\"\n },\n \"keywords\": [\n \"video\",\n \"webrtc\",\n \"conferencing\",\n \"100ms\"\n ],\n \"bugs\": {\n \"url\": \"https://github.com/100mslive/hms-video-store/issues\"\n },\n \"homepage\": \"https://github.com/100mslive/hms-video-store#readme\"\n}\n", "import { HLSVariant, HMSHLS, HMSRecording, HMSRTMP } from '@100mslive/hms-video';\nimport { HMSPeerID } from './peer';\n\nexport type { HMSRecording, HMSRTMP, HMSHLS, HLSVariant };\nexport type HMSRoomID = string;\n\n/**\n * Check out internal-docs/RoomStateFlow.tldr for flow of room state\n * View it by\n * - Installing tldraw for VSCode(https://marketplace.visualstudio.com/items?itemName=tldraw-org.tldraw-vscode), or\n * - Open the file in https://www.tldraw.com/\n */\nexport enum HMSRoomState {\n Disconnected = 'Disconnected',\n Preview = 'Preview',\n Connecting = 'Connecting',\n Connected = 'Connected',\n Reconnecting = 'Reconnecting',\n Disconnecting = 'Disconnecting',\n Failed = 'Failed',\n}\n\nexport interface HMSRoom {\n id: HMSRoomID;\n name: string;\n isConnected?: boolean;\n peers: HMSPeerID[];\n localPeer: HMSPeerID;\n roomState: HMSRoomState;\n recording: HMSRecording;\n rtmp: HMSRTMP;\n hls: HMSHLS;\n sessionId: string;\n startedAt?: Date;\n joinedAt?: Date;\n /**\n * if this number is available room.peers is not guaranteed to have all the peers.\n */\n peerCount?: number;\n}\n", "import { HMSException } from './error';\nimport { HMSMessage, HMSMessageID } from './message';\nimport { HMSPeer, HMSPeerID, HMSSpeaker, HMSTrack, HMSTrackID } from './peer';\nimport { HMSPlaylist } from './playlist';\nimport { HMSRoleChangeStoreRequest } from './requests';\nimport { HMSRole } from './role';\nimport { HMSRoom, HMSRoomState } from './room';\nimport { HMSMediaSettings } from './settings';\nimport { DeviceMap, HMSConnectionQuality, HMSPeerStats, HMSPoll, HMSTrackStats } from '../hmsSDKStore/sdkTypes';\n\nexport interface HMSGenericTypes {\n sessionStore: Record<string, any>;\n}\n\n/*\n * Defines the schema of the central store. UI Components are aware of the presence\n * of this central store. This is the global state - the single source of immutable truth.\n */\nexport interface HMSStore<T extends HMSGenericTypes = { sessionStore: Record<string, any> }> {\n room: HMSRoom;\n peers: Record<HMSPeerID, HMSPeer>;\n speakers: Record<HMSTrackID, HMSSpeaker>;\n connectionQualities: Record<HMSPeerID, HMSConnectionQuality>;\n tracks: Record<HMSTrackID, HMSTrack>;\n playlist: HMSPlaylist<any>;\n messages: {\n byID: Record<HMSMessageID, HMSMessage>;\n allIDs: HMSMessageID[];\n };\n settings: HMSMediaSettings;\n devices: DeviceMap;\n roles: Record<string, HMSRole>;\n templateAppData: Record<string, string>;\n appData?: Record<string, any>;\n roleChangeRequests: HMSRoleChangeStoreRequest[];\n /** @deprecated use `sessionStore` instead */\n sessionMetadata?: any;\n preview?: {\n localPeer?: HMSPeerID;\n asRole?: string;\n videoTrack?: HMSTrackID;\n audioTrack?: HMSTrackID;\n };\n errors: HMSException[]; // for the convenience of debugging and seeing any error in devtools\n sessionStore: T['sessionStore'];\n polls: Record<string, HMSPoll>;\n hideLocalPeer: boolean;\n}\n\nexport interface HMSStatsStore {\n remoteTrackStats: Record<HMSTrackID, HMSTrackStats | undefined>;\n localTrackStats: Record<HMSTrackID, HMSTrackStats[] | undefined>;\n peerStats: Record<HMSPeerID, HMSPeerStats | undefined>;\n localPeer: {\n id: HMSPeerID;\n videoTrack?: HMSTrackID;\n audioTrack?: HMSTrackID;\n };\n}\n\n/**\n * @internal\n */\nexport const createDefaultStoreState = <T extends HMSGenericTypes>(): HMSStore<T> => {\n return {\n room: {\n id: '',\n isConnected: false,\n name: '',\n peers: [],\n localPeer: '',\n roomState: HMSRoomState.Disconnected,\n recording: {\n browser: {\n running: false,\n },\n server: {\n running: false,\n },\n hls: { running: false },\n },\n rtmp: {\n running: false,\n },\n hls: {\n running: false,\n variants: [],\n },\n sessionId: '',\n },\n peers: {},\n tracks: {},\n playlist: {\n audio: {\n list: {},\n selection: { id: '', hasPrevious: false, hasNext: false },\n progress: 0,\n volume: 0,\n currentTime: 0,\n playbackRate: 1.0,\n },\n video: {\n list: {},\n selection: { id: '', hasPrevious: false, hasNext: false },\n progress: 0,\n volume: 0,\n currentTime: 0,\n playbackRate: 1.0,\n },\n },\n messages: { byID: {}, allIDs: [] },\n speakers: {},\n connectionQualities: {},\n settings: {\n audioInputDeviceId: '',\n audioOutputDeviceId: '',\n videoInputDeviceId: '',\n },\n devices: {\n audioInput: [],\n audioOutput: [],\n videoInput: [],\n },\n roles: {},\n roleChangeRequests: [],\n errors: [],\n sessionStore: {},\n templateAppData: {},\n polls: {},\n hideLocalPeer: false,\n };\n};\n\nexport const createDefaultStatsStore = (): HMSStatsStore => {\n return {\n peerStats: {},\n remoteTrackStats: {},\n localTrackStats: {},\n localPeer: { id: '' },\n };\n};\n", "import { HMSPeerID } from './peer';\nimport { HMSRoleName } from './role';\n\nexport type HMSMessageID = string;\n\n/**\n * @internal\n */\nexport enum HMSMessageType {\n CHAT = 'chat',\n}\n\nexport interface HMSMessage {\n id: HMSMessageID;\n sender?: HMSPeerID;\n senderName?: string;\n senderUserId?: string;\n senderRole?: string;\n recipientPeer?: HMSPeerID;\n recipientRoles?: HMSRoleName[];\n time: Date;\n read: boolean;\n type: string;\n message: any;\n /**\n * true if message will not be put it in store because it has been ignored\n */\n ignored: boolean;\n}\n\n/**\n * @internal\n */\nexport interface HMSMessageInput {\n recipientPeer?: HMSPeerID;\n recipientRoles?: HMSRoleName[];\n type?: string;\n message: any;\n}\n", "import { HMSPoll } from '@100mslive/hms-video';\nimport { HMSDeviceChangeEvent } from './device-change';\nimport { HMSException } from './error';\nimport { HMSMessage } from './message';\nimport { HMSPeer, HMSTrack } from './peer';\nimport { HMSPlaylistItem } from './playlist';\nimport { HMSChangeMultiTrackStateRequest, HMSChangeTrackStateRequest, HMSLeaveRoomRequest } from './requests';\n\ninterface BaseNotification {\n id: number;\n type: string;\n message: string;\n severity?: HMSNotificationSeverity;\n}\nexport interface HMSPeerNotification extends BaseNotification {\n type:\n | HMSNotificationTypes.PEER_JOINED\n | HMSNotificationTypes.PEER_LEFT\n | HMSNotificationTypes.NAME_UPDATED\n | HMSNotificationTypes.METADATA_UPDATED\n | HMSNotificationTypes.ROLE_UPDATED;\n data: HMSPeer;\n}\n\nexport interface HMSPeerListNotification extends BaseNotification {\n type: HMSNotificationTypes.PEER_LIST;\n data: HMSPeer[];\n}\nexport interface HMSTrackNotification extends BaseNotification {\n type:\n | HMSNotificationTypes.TRACK_ADDED\n | HMSNotificationTypes.TRACK_DEGRADED\n | HMSNotificationTypes.TRACK_UNMUTED\n | HMSNotificationTypes.TRACK_DESCRIPTION_CHANGED\n | HMSNotificationTypes.TRACK_MUTED\n | HMSNotificationTypes.TRACK_REMOVED\n | HMSNotificationTypes.TRACK_RESTORED;\n data: HMSTrack;\n}\nexport interface HMSMessageNotification extends BaseNotification {\n type: HMSNotificationTypes.NEW_MESSAGE;\n data: HMSMessage;\n}\nexport interface HMSExceptionNotification extends BaseNotification {\n type: HMSNotificationTypes.ERROR;\n data: HMSException;\n}\nexport interface HMSChangeTrackStateRequestNotification extends BaseNotification {\n type: HMSNotificationTypes.CHANGE_TRACK_STATE_REQUEST;\n data: HMSChangeTrackStateRequest;\n}\nexport interface HMSChangeMultiTrackStateRequestNotification extends BaseNotification {\n type: HMSNotificationTypes.CHANGE_MULTI_TRACK_STATE_REQUEST;\n data: HMSChangeMultiTrackStateRequest;\n}\n\nexport interface HMSLeaveRoomRequestNotification extends BaseNotification {\n type: HMSNotificationTypes.ROOM_ENDED | HMSNotificationTypes.REMOVED_FROM_ROOM;\n data: HMSLeaveRoomRequest;\n}\nexport interface HMSDeviceChangeEventNotification extends BaseNotification {\n type: HMSNotificationTypes.DEVICE_CHANGE_UPDATE;\n data?: HMSDeviceChangeEvent;\n}\nexport interface HMSPlaylistItemNotification<T> extends BaseNotification {\n type: HMSNotificationTypes.PLAYLIST_TRACK_ENDED;\n data: HMSPlaylistItem<T>;\n}\n\nexport interface HMSReconnectionNotification extends BaseNotification {\n type: HMSNotificationTypes.RECONNECTED | HMSNotificationTypes.RECONNECTING;\n data: null;\n}\n\nexport interface HMSPollNotification extends BaseNotification {\n type: HMSNotificationTypes.POLL_STARTED | HMSNotificationTypes.POLL_STOPPED | HMSNotificationTypes.POLL_VOTES_UPDATED;\n data: HMSPoll;\n}\n\nexport type HMSNotification =\n | HMSPeerNotification\n | HMSPeerListNotification\n | HMSTrackNotification\n | HMSMessageNotification\n | HMSExceptionNotification\n | HMSChangeTrackStateRequestNotification\n | HMSChangeMultiTrackStateRequestNotification\n | HMSLeaveRoomRequestNotification\n | HMSDeviceChangeEventNotification\n | HMSReconnectionNotification\n | HMSPlaylistItemNotification<any>;\n\nexport enum HMSNotificationSeverity {\n INFO = 'info',\n ERROR = 'error',\n}\n\nexport enum HMSNotificationTypes {\n PEER_JOINED = 'PEER_JOINED',\n PEER_LEFT = 'PEER_LEFT',\n PEER_LIST = 'PEER_LIST',\n NEW_MESSAGE = 'NEW_MESSAGE',\n ERROR = 'ERROR',\n RECONNECTING = 'RECONNECTING',\n RECONNECTED = 'RECONNECTED',\n TRACK_ADDED = 'TRACK_ADDED',\n TRACK_REMOVED = 'TRACK_REMOVED',\n TRACK_MUTED = 'TRACK_MUTED',\n TRACK_UNMUTED = 'TRACK_UNMUTED',\n TRACK_DEGRADED = 'TRACK_DEGRADED',\n TRACK_RESTORED = 'TRACK_RESTORED',\n TRACK_DESCRIPTION_CHANGED = 'TRACK_DESCRIPTION_CHANGED',\n ROLE_UPDATED = 'ROLE_UPDATED',\n CHANGE_TRACK_STATE_REQUEST = 'CHANGE_TRACK_STATE_REQUEST',\n CHANGE_MULTI_TRACK_STATE_REQUEST = 'CHANGE_MULTI_TRACK_STATE_REQUEST',\n ROOM_ENDED = 'ROOM_ENDED',\n REMOVED_FROM_ROOM = 'REMOVED_FROM_ROOM',\n DEVICE_CHANGE_UPDATE = 'DEVICE_CHANGE_UPDATE',\n PLAYLIST_TRACK_ENDED = 'PLAYLIST_TRACK_ENDED',\n NAME_UPDATED = 'NAME_UPDATED',\n METADATA_UPDATED = 'METADATA_UPDATED',\n POLL_CREATED = 'POLL_CREATED',\n POLL_STARTED = 'POLL_STARTED',\n POLL_STOPPED = 'POLL_STOPPED',\n POLL_VOTES_UPDATED = 'POLL_VOTES_UPDATED',\n}\n\nexport type HMSNotificationMapping<T extends HMSNotificationTypes, C = any> = {\n [HMSNotificationTypes.PEER_JOINED]: HMSPeerNotification;\n [HMSNotificationTypes.PEER_LEFT]: HMSPeerNotification;\n [HMSNotificationTypes.PEER_LIST]: HMSPeerListNotification;\n [HMSNotificationTypes.NAME_UPDATED]: HMSPeerNotification;\n [HMSNotificationTypes.METADATA_UPDATED]: HMSPeerNotification;\n [HMSNotificationTypes.ROLE_UPDATED]: HMSPeerNotification;\n [HMSNotificationTypes.TRACK_ADDED]: HMSTrackNotification;\n [HMSNotificationTypes.TRACK_REMOVED]: HMSTrackNotification;\n [HMSNotificationTypes.TRACK_MUTED]: HMSTrackNotification;\n [HMSNotificationTypes.TRACK_UNMUTED]: HMSTrackNotification;\n [HMSNotificationTypes.TRACK_DEGRADED]: HMSTrackNotification;\n [HMSNotificationTypes.TRACK_RESTORED]: HMSTrackNotification;\n [HMSNotificationTypes.TRACK_DESCRIPTION_CHANGED]: HMSTrackNotification;\n [HMSNotificationTypes.TRACK_UNMUTED]: HMSTrackNotification;\n [HMSNotificationTypes.NEW_MESSAGE]: HMSMessageNotification;\n [HMSNotificationTypes.ROOM_ENDED]: HMSLeaveRoomRequestNotification;\n [HMSNotificationTypes.REMOVED_FROM_ROOM]: HMSLeaveRoomRequestNotification;\n [HMSNotificationTypes.DEVICE_CHANGE_UPDATE]: HMSDeviceChangeEventNotification;\n [HMSNotificationTypes.PLAYLIST_TRACK_ENDED]: HMSPlaylistItemNotification<C>;\n [HMSNotificationTypes.ERROR]: HMSExceptionNotification;\n [HMSNotificationTypes.CHANGE_TRACK_STATE_REQUEST]: HMSChangeTrackStateRequestNotification;\n [HMSNotificationTypes.CHANGE_MULTI_TRACK_STATE_REQUEST]: HMSChangeMultiTrackStateRequestNotification;\n [HMSNotificationTypes.RECONNECTED]: HMSReconnectionNotification;\n [HMSNotificationTypes.RECONNECTING]: HMSReconnectionNotification;\n [HMSNotificationTypes.POLL_STARTED]: HMSPollNotification;\n [HMSNotificationTypes.POLL_STOPPED]: HMSPollNotification;\n [HMSNotificationTypes.POLL_VOTES_UPDATED]: HMSPollNotification;\n [HMSNotificationTypes.POLL_CREATED]: HMSPollNotification;\n}[T];\n\ntype MappedNotifications<Type extends HMSNotificationTypes[]> = {\n [index in keyof Type]: HMSNotificationMapping<Type[index]>;\n};\n\nexport type HMSNotificationTypeParam = HMSNotificationTypes | HMSNotificationTypes[] | undefined;\n\nexport type HMSNotificationInCallback<T extends HMSNotificationTypeParam> = T extends HMSNotificationTypes[]\n ? MappedNotifications<T>[number]\n : T extends HMSNotificationTypes\n ? HMSNotificationMapping<T>\n : HMSNotification;\n\nexport type HMSNotificationCallback<T extends HMSNotificationTypeParam> = (\n notification: HMSNotificationInCallback<T>,\n) => void;\n\n/**\n * @category Core\n */\nexport interface IHMSNotifications {\n /**\n * you can subscribe to notifications for new message, peer add etc. using this function.\n * note that this is not meant to maintain any state on your side, as the reactive store already\n * does that. The intent of this function is mainly to display toast notifications or send analytics.\n * We'll provide a display message which can be displayed as it is for common cases.\n */\n onNotification<T extends HMSNotificationTypeParam>(cb: HMSNotificationCallback<T>, types?: T): () => void;\n}\n", "export enum HMSPlaylistType {\n audio = 'audio',\n video = 'video',\n}\nexport interface HMSPlaylistItem<T> {\n name: string;\n id: string;\n metadata?: T;\n url: string;\n type: HMSPlaylistType;\n duration?: number;\n playing: boolean;\n selected: boolean;\n}\n\nexport interface HMSPlaylistSelection {\n id: string;\n hasPrevious: boolean;\n hasNext: boolean;\n}\n\nexport interface HMSPlaylist<T> {\n audio: {\n list: Record<string, HMSPlaylistItem<T>>;\n selection: HMSPlaylistSelection;\n progress: number;\n volume: number;\n currentTime: number;\n playbackRate: number;\n };\n video: {\n list: Record<string, HMSPlaylistItem<T>>;\n selection: HMSPlaylistSelection;\n progress: number;\n volume: number;\n currentTime: number;\n playbackRate: number;\n };\n}\n\nexport interface IHMSPlaylistActions {\n /**\n * Pass the id of the item to be played\n * @param {string} id - id of playlist item\n */\n play(id: string): Promise<void>;\n /**\n * Pauses current playing item\n */\n pause(): Promise<void>;\n /**\n * PlayNext\n */\n playNext(): Promise<void>;\n /**\n * PlayPrevious\n */\n playPrevious(): Promise<void>;\n /**\n * seek passing seekValue - this is relative to current position\n * @param {number} seekValue - number in seconds to move forwards(pass negative values to move backwards)\n */\n seek(seekValue: number): void;\n /**\n * seek passing seekValue - seekValue will be absolute\n * @param {number} seekValue - value in seconds of absolute position in the playlist item duration\n */\n seekTo(seekValue: number): void;\n /**\n * set volume passing volume\n * @param {number} volume - number between 0-100\n */\n setVolume(volume: number): void;\n /**\n * pass list to set playlist\n * @param {HMSPlaylistItem[]} list of playlist items\n */\n setList<T>(list: HMSPlaylistItem<T>[]): void;\n /**\n * Stop the current playback and remove the tracks\n */\n stop(): Promise<void>;\n /**\n * set whether to autoplay next item in playlist after the current one ends\n * @param {boolean} autoplay\n */\n setIsAutoplayOn(autoplay: boolean): void;\n /**\n * Control the playback speed - 1.0 being normal, less than 1.0 will play it slowly\n * and more than 1.0 will play it faster.\n * @param playbackRate - value from 0.25 and 2.0\n */\n setPlaybackRate(playbackRate: number): void;\n removeItem(id: string): Promise<boolean>;\n clearList(): Promise<void>;\n}\n", "import { createSelector } from 'reselect';\nimport {\n getScreenSharesByPeer,\n isAudioPlaylist,\n isDegraded,\n isTrackDisplayEnabled,\n isTrackEnabled,\n isVideo,\n isVideoPlaylist,\n} from './selectorUtils';\n// noinspection ES6PreferShortImport\nimport { HMSRole } from '../hmsSDKStore/sdkTypes';\nimport {\n HMSException,\n HMSMessage,\n HMSPeer,\n HMSPeerID,\n HMSRoom,\n HMSRoomState,\n HMSStore,\n HMSVideoTrack,\n} from '../schema';\n\n/**\n * Select the current {@link HMSRoom} object to which you are connected.\n * @param store\n */\nexport const selectRoom = (store: HMSStore): HMSRoom => store.room;\n/**\n * Select the current {@link HMSException[]} object to monitor the error logs\n * @param store\n */\nexport const selectErrors = (store: HMSStore): HMSException[] => store.errors;\n\n/**\n * It will help to get the all the error\n */\nexport const selectRecentError = createSelector(selectErrors, errors => (errors.length === 0 ? null : errors.at(-1)));\n/**\n * Select the ID of the current room to which you are connected.\n */\nexport const selectRoomID = createSelector(selectRoom, room => room.id);\n\n/**\n * @internal\n */\nexport const selectPeersMap = (store: HMSStore): Record<HMSPeerID, HMSPeer> => store.peers;\n\n/**\n * @internal\n */\nexport const selectMessagesMap = (store: HMSStore) => store.messages.byID;\n\n/**\n * Select IDs of messages you've sent or received sorted chronologically.\n */\nexport const selectMessageIDsInOrder = (store: HMSStore) => store.messages.allIDs;\n\n/**\n * @internal\n */\nexport const selectTracksMap = (store: HMSStore) => store.tracks;\n\n/**\n * Select your media settings\n * i.e., choosen audio input device, audio output device and video input device.\n * @param store\n */\nexport const selectLocalMediaSettings = (store: HMSStore) => store.settings;\n\n/**\n * select appData.\n * @internal\n */\nexport const selectFullAppData = (store: HMSStore) => store.appData;\n\n/**\n * Select the available audio input, audio output and video input devices on your machine.\n * @param store\n * @returns An object of array of available audio input, audio output and video input devices.\n * ```\n * type DeviceMap = {\n * audioInput: InputDeviceInfo[];\n * audioOutput: MediaDeviceInfo[];\n * videoInput: InputDeviceInfo[];\n * }\n * ```\n */\nexport const selectDevices = (store: HMSStore) => {\n return store.devices;\n};\n\nexport const selectSpeakers = (store: HMSStore) => {\n return store.speakers;\n};\n\nexport const selectConnectionQualities = (store: HMSStore) => {\n return store.connectionQualities;\n};\n\n/**\n * Select a boolean flag denoting whether you've joined a room.\n * NOTE: Returns true only after join, returns false during preview.\n */\nexport const selectIsConnectedToRoom = createSelector([selectRoom], room => room && room.isConnected);\n\n/**\n * selectPeerCount gives the number of peers Inside the room. This doesn't count the local peer if\n * they're still in preview and haven't yet joined the room. Note that this will not necessarily equal the\n * number of peers received through selectPeers, it's possible to know total number of people in the room\n * without having details of everyone depending on dashboard settings.\n */\nexport const selectPeerCount = createSelector([selectIsConnectedToRoom, selectRoom], (isConnected, room) => {\n if (isConnected) {\n // if we have peer count from server return that else return number of peers in the store.\n // In case the strongly consistent peer list is disabled and only eventual consistent count and peer\n // details is sent, room.peerCount may be 0 for a few second even though local peer is connected, send 1 in that case.\n // TODO: Fix this at populating room.peerCount level than in selector.\n return room.peerCount !== undefined ? room.peerCount || 1 : room.peers.length;\n } else {\n // if we have peer count from server return that, else return number of peers except the local one because local is\n // not joined yet.\n // Math.max to ensure we're not returning -1, if the selector is called before local peer is put in the store\n return Math.max(room.peerCount !== undefined ? room.peerCount : room.peers.length - 1, 0);\n }\n});\n\n/**\n * @internal\n * Select a boolean flag denoting whether to hide local peer.\n * When this is true, `selectPeers` skips local peer.\n */\nconst selectHideLocalPeer = (store: HMSStore): boolean => store.hideLocalPeer;\n\n/**\n * Select an array of peers(remote peers and your local peer) present in the room.\n */\nexport const selectPeers = createSelector(\n [selectRoom, selectPeersMap, selectHideLocalPeer],\n (room, storePeers, hideLocalPeer) => {\n if (hideLocalPeer) {\n return room.peers.filter(peerID => room.localPeer !== peerID).map(peerID => storePeers[peerID]);\n }\n return room.peers.map(peerID => storePeers[peerID]);\n },\n);\n\n/**\n * Select an array of tracks(remote peer tracks and your local tracks) present in the room.\n */\nconst selectTracks = createSelector(selectTracksMap, storeTracks => {\n return Object.values(storeTracks);\n});\n\n/**\n * Select the local peer object object assigned to you.\n */\nexport const selectLocalPeer = createSelector(selectRoom, selectPeersMap, (room, peers): HMSPeer | undefined => {\n return peers[room.localPeer];\n});\n\n/**\n * Select the peer ID of your local peer.\n */\nexport const selectLocalPeerID = createSelector(selectRoom, room => {\n return room.localPeer;\n});\n\n/**\n * Select the peer name of your local peer.\n */\nexport const selectLocalPeerName = createSelector(selectLocalPeer, peer => peer?.name);\n\n/**\n * Select the role name of your local peer.\n */\nexport const selectLocalPeerRoleName = createSelector(selectLocalPeer, peer => peer?.roleName);\n\n/**\n * Select the track ID of your local peer's primary audio track\n */\nexport const selectLocalAudioTrackID = createSelector(selectLocalPeer, peer => peer?.audioTrack);\n\n/**\n * Select the track ID of your local peer's primary video track\n */\nexport const selectLocalVideoTrackID = createSelector(selectLocalPeer, peer => peer?.videoTrack);\n\n/**\n * Select an array of track IDs of your local peer's auxiliary tracks\n */\nconst selectLocalAuxiliaryTrackIDs = createSelector(selectLocalPeer, peer => peer?.auxiliaryTracks);\n\n/**\n * Select an array of track IDs of all your local peer's tracks\n */\nexport const selectLocalTrackIDs = createSelector(\n [selectLocalAudioTrackID, selectLocalVideoTrackID, selectLocalAuxiliaryTrackIDs],\n (audioTrackID, videoTrackID, auxiliaryTrackIDs) => {\n const trackIDs: string[] = auxiliaryTrackIDs ? [...auxiliaryTrackIDs] : [];\n audioTrackID && trackIDs.unshift(audioTrackID);\n videoTrackID && trackIDs.unshift(videoTrackID);\n return trackIDs;\n },\n);\n\n/**\n * Select remote peers(other users you're connected with via the internet) present in the room.\n */\nexport const selectRemotePeers = createSelector(selectPeers, peers => {\n return peers.filter(p => !p.isLocal);\n});\n\n/**\n * Select the peer who's speaking the loudest at the moment\n */\nexport const selectDominantSpeaker = createSelector(selectPeersMap, selectSpeakers, (peersMap, speakers) => {\n // sort in descending order by audio level\n const speakersInOrder = Object.entries(speakers).sort((s1, s2) => {\n const s1Level = s1[1]?.audioLevel || 0;\n const s2Level = s2[1]?.audioLevel || 0;\n return s2Level > s1Level ? 1 : -1;\n });\n if (speakersInOrder.length > 0 && speakersInOrder[0][1].audioLevel && speakersInOrder[0][1].audioLevel > 0) {\n const peerID = speakersInOrder[0][1].peerID;\n if (peerID in peersMap) {\n return peersMap[peerID];\n }\n }\n return null;\n});\n\n/**\n * Select a boolean denoting whether your local audio is unmuted\n * and the audio from your microphone is shared to remote peers\n */\nexport const selectIsLocalAudioEnabled = (store: HMSStore) => {\n const localPeer = selectLocalPeer(store);\n return isTrackEnabled(store, localPeer?.audioTrack);\n};\n\n/**\n * Select a boolean denoting whether your local video is unmuted\n * and the video from your camera is shared to remote peers\n */\nexport const selectIsLocalVideoEnabled = (store: HMSStore) => {\n const localPeer = selectLocalPeer(store);\n return isTrackEnabled(store, localPeer?.videoTrack);\n};\n\n/**\n * Select a boolean denoting whether you've chosen to unmute and share your local video.\n *\n * NOTE: Once you call `hmsActions.setLocalVideoEnabled(true)`to unmute your local video,\n * it takes some time to fetch your video from your video source.\n * This displayEnabled property gives immediate feedback for a more interactive UI,\n * without waiting for the video source\n */\nexport const selectIsLocalVideoDisplayEnabled = (store: HMSStore) => {\n const localPeer = selectLocalPeer(store);\n return isTrackDisplayEnabled(store, localPeer?.videoTrack);\n};\n\n/**\n * Select a boolean denoting whether your screen is shared to remote peers in the room.\n */\nexport const selectIsLocalScreenShared = createSelector(selectLocalPeer, selectTracksMap, (localPeer, tracksMap) => {\n const { video, audio } = getScreenSharesByPeer(tracksMap, localPeer);\n return !!(video || audio);\n});\n\n/**\n * Select the first peer who is currently sharing their screen.\n */\nexport const selectPeerScreenSharing = createSelector(selectPeersMap, selectTracksMap, (peersMap, tracksMap) => {\n let screensharePeer = undefined;\n for (const peerID in peersMap) {\n const peer = peersMap[peerID];\n const { video, audio } = getScreenSharesByPeer(tracksMap, peer);\n if (video) {\n return peer;\n } else if (audio && !screensharePeer) {\n screensharePeer = peer;\n }\n }\n return screensharePeer;\n});\n\n/**\n * Select a boolean denoting whether someone is sharing screen in the room.\n */\nexport const selectIsSomeoneScreenSharing = createSelector(selectPeerScreenSharing, peer => {\n return !!peer;\n});\n\n/**\n * Select the first peer who is currently sharing their audio only screen\n */\nexport const selectPeerSharingAudio = createSelector(selectPeersMap, selectTracksMap, (peersMap, tracksMap) => {\n for (const peerID in peersMap) {\n const peer = peersMap[peerID];\n const { audio, video } = getScreenSharesByPeer(tracksMap, peer);\n if (!video && !!audio) {\n return peer;\n }\n }\n return undefined;\n});\n\n/**\n * Select an array of peers who are currently sharing their screen.\n */\nexport const selectPeersScreenSharing = createSelector(selectPeersMap, selectTracksMap, (peersMap, tracksMap) => {\n const videoPeers = [];\n const audioPeers = [];\n for (const peerID in peersMap) {\n const peer = peersMap[peerID];\n const { video, audio } = getScreenSharesByPeer(tracksMap, peer);\n if (video) {\n videoPeers.push(peer);\n } else if (audio) {\n audioPeers.push(peer);\n }\n }\n return videoPeers.concat(audioPeers);\n});\n\nexport const selectPeerSharingVideoPlaylist = createSelector(selectPeersMap, selectTracksMap, (peersMap, tracksMap) => {\n for (const trackId in tracksMap) {\n const track = tracksMap[trackId];\n if (isVideoPlaylist(track) && isVideo(track) && track.peerId) {\n return peersMap[track.peerId];\n }\n }\n return undefined;\n});\n\nexport const selectPeerSharingAudioPlaylist = createSelector(selectPeersMap, selectTracksMap, (peersMap, tracksMap) => {\n for (const trackId in tracksMap) {\n const track = tracksMap[trackId];\n if (isAudioPlaylist(track) && track.peerId) {\n return peersMap[track.peerId];\n }\n }\n return undefined;\n});\n\n/**\n * Select an array of tracks that have been degraded(receiving lower video quality/no video) due to bad network locally.\n */\nexport const selectDegradedTracks = createSelector(selectTracks, tracks =>\n (tracks as HMSVideoTrack[]).filter(isDegraded),\n);\n\n/**\n * Select the number of messages(sent and received).\n */\nexport const selectHMSMessagesCount = createSelector(selectMessageIDsInOrder, messageIDs => messageIDs.length);\n\n/**\n * Select the number of unread messages.\n */\nexport const selectUnreadHMSMessagesCount = createSelector(selectMessagesMap, messages => {\n return Object.values(messages).filter(m => !m.read).length;\n});\n\n/**\n * Select an array of messages in the room(sent and received).\n */\nexport const selectHMSMessages = createSelector(selectMessageIDsInOrder, selectMessagesMap, (msgIDs, msgMap) => {\n const messages: HMSMessage[] = [];\n msgIDs.forEach(msgId => {\n messages.push(msgMap[msgId]);\n });\n return messages;\n});\n\n/**\n * Select the current state of the room.\n */\nexport const selectRoomState = createSelector([selectRoom], room => room && room.roomState);\n\n/**\n * Select a boolean denoting whether the room is in Preview state.\n */\nexport const selectIsInPreview = createSelector(selectRoomState, roomState => roomState === HMSRoomState.Preview);\n\nexport const selectRoomStarted = createSelector(selectRoom, room => room.roomState !== HMSRoomState.Disconnected);\n\n/**\n * Select available roles in the room as a map between the role name and {@link HMSRole} object.\n */\nexport const selectRolesMap = (store: HMSStore): Record<string, HMSRole> => {\n return store.roles;\n};\n\n/**\n * Select an array of names of available roles in the room.\n */\nexport const selectAvailableRoleNames = createSelector([selectRolesMap], rolesMap => Object.keys(rolesMap));\n\n/**\n * Select the {@link HMSRole} object of your local peer.\n */\nexport const selectLocalPeerRole = createSelector([selectLocalPeer, selectRolesMap], (localPeer, rolesMap) =>\n localPeer?.roleName ? rolesMap[localPeer.roleName] : null,\n);\n\nexport const selectPreviewRoleName = (store: HMSStore) => store.preview?.asRole;\n\n/**\n * Select the {@link HMSRole} used for preview.\n *\n */\nexport const selectPreviewRole = createSelector([selectPreviewRoleName, selectRolesMap], (roleName, rolesMap) =>\n roleName ? rolesMap[roleName] : null,\n);\n\n/**\n * Select a boolean denoting whether if your local peer is allowed to subscribe to any other role.\n */\nexport const selectIsAllowedToSubscribe = createSelector([selectLocalPeerRole], (role): boolean => {\n if (!role?.subscribeParams?.subscribeToRoles) {\n return false;\n }\n return role.subscribeParams.subscribeToRoles.length > 0;\n});\n\n/**\n * Select the permissions which determine what actions the local peer can do.\n */\nexport const selectPermissions = createSelector(selectLocalPeerRole, role => role?.permissions);\nexport const selectRecordingState = createSelector(selectRoom, room => room.recording);\nexport const selectRTMPState = createSelector(selectRoom, room => room.rtmp);\nexport const selectHLSState = createSelector(selectRoom, room => room.hls);\nexport const selectSessionId = createSelector(selectRoom, room => room.sessionId);\nexport const selectRoomStartTime = createSelector(selectRoom, room => room.startedAt);\nexport const selectTemplateAppData = (store: HMSStore) => store.templateAppData;\n/** @deprecated - use `selectSessionStore` instead */\nexport const selectSessionMetadata = (store: HMSStore) => store.sessionMetadata;\nexport const selectPollsMap = (store: HMSStore) => store.polls;\nexport const selectPolls = (store: HMSStore) => {\n return Object.values(store.polls);\n};\n", "import {\n HMSPeer,\n HMSPublishAllowed,\n HMSRole,\n HMSScreenAudioTrack,\n HMSScreenVideoTrack,\n HMSStore,\n HMSTrack,\n HMSTrackID,\n HMSVideoTrack,\n} from '../schema';\n\nexport function getScreenSharesByPeer(tracks: Record<HMSTrackID, HMSTrack>, peer?: HMSPeer | null) {\n let videoTrack = undefined;\n let audioTrack = undefined;\n if (peer) {\n for (const trackID of peer.auxiliaryTracks) {\n const track = tracks[trackID];\n if (isScreenShare(track)) {\n audioTrack = isAudio(track) ? track : audioTrack;\n videoTrack = isVideo(track) ? track : videoTrack;\n }\n }\n }\n // to use the proper type, right now it is only used for screenshare.\n return { video: videoTrack as HMSScreenVideoTrack, audio: audioTrack as HMSScreenAudioTrack };\n}\n\nexport function isAudio(track: HMSTrack | undefined) {\n return track && track.type === 'audio';\n}\n\nexport function isVideo(track: HMSTrack | undefined) {\n return track && track.type === 'video';\n}\n\nexport function isScreenShare(track: HMSTrack | undefined) {\n return track && track.source === 'screen';\n}\n\nexport function isAudioPlaylist(track: HMSTrack | undefined) {\n return track && track.source === 'audioplaylist';\n}\n\nexport function isVideoPlaylist(track: HMSTrack | undefined) {\n return track && track.source === 'videoplaylist';\n}\n\nexport function isDegraded(track: HMSVideoTrack) {\n if (track) {\n return Boolean(track?.degraded);\n }\n return false;\n}\n\nexport function isTrackEnabled(store: HMSStore, trackID?: string) {\n if (trackID && store.tracks[trackID]) {\n return store.tracks[trackID].enabled;\n }\n return false;\n}\n\n/**\n * Should UI show the video track as enabled\n */\nexport function isTrackDisplayEnabled(store: HMSStore, trackID?: string) {\n if (trackID && store.tracks[trackID]) {\n return store.tracks[trackID].displayEnabled;\n }\n return false;\n}\n\nexport function isRoleAllowedToPublish(role?: HMSRole | null): HMSPublishAllowed {\n let video = false,\n audio = false,\n screen = false;\n if (role?.publishParams?.allowed) {\n video = role.publishParams.allowed.includes('video');\n audio = role.publishParams.allowed.includes('audio');\n screen = role.publishParams.allowed.includes('screen');\n }\n return {\n video,\n audio,\n screen,\n };\n}\n", "import { createSelector } from 'reselect';\nimport { HMSPlaylistSelector, HMSPlaylistType, HMSStore } from '../schema';\n\n/**\n * @internal\n */\nconst selectPlaylistMap =\n (type: HMSPlaylistType = HMSPlaylistType.audio) =>\n (store: HMSStore) =>\n store.playlist[type].list;\n\nconst selectPlaylistSelection =\n (type: HMSPlaylistType = HMSPlaylistType.audio) =>\n (store: HMSStore) =>\n store.playlist[type].selection;\n\nconst selectPlaylistProgress =\n (type: HMSPlaylistType = HMSPlaylistType.audio) =>\n (store: HMSStore) =>\n store.playlist[type].progress;\n\nconst selectPlaylistCurrentTime =\n (type: HMSPlaylistType = HMSPlaylistType.audio) =>\n (store: HMSStore) =>\n store.playlist[type].currentTime;\n\nconst selectPlaylistPlaybackRate =\n (type: HMSPlaylistType = HMSPlaylistType.audio) =>\n (store: HMSStore) =>\n store.playlist[type].playbackRate;\n\nconst selectPlaylistVolume =\n (type: HMSPlaylistType = HMSPlaylistType.audio) =>\n (store: HMSStore) =>\n store.playlist[type].volume;\n\n/**\n * Select an array of playlist items.\n */\nconst selectPlaylist = (type: HMSPlaylistType = HMSPlaylistType.audio) =>\n createSelector(selectPlaylistMap(type), storePlaylist => {\n return Object.values(storePlaylist);\n });\n\nconst selectPlaylistSelectedItem = (type: HMSPlaylistType = HMSPlaylistType.audio) =>\n createSelector(selectPlaylistMap(type), selectPlaylistSelection(type), (storePlaylist, currentSelection) => {\n if (!currentSelection.id) {\n return;\n }\n return storePlaylist[currentSelection.id];\n });\n\nexport const selectAudioPlaylist: HMSPlaylistSelector = {\n selection: selectPlaylistSelection(HMSPlaylistType.audio),\n progress: selectPlaylistProgress(HMSPlaylistType.audio),\n currentTime: selectPlaylistCurrentTime(HMSPlaylistType.audio),\n playbackRate: selectPlaylistPlaybackRate(HMSPlaylistType.audio),\n volume: selectPlaylistVolume(HMSPlaylistType.audio),\n list: selectPlaylist(HMSPlaylistType.audio),\n selectedItem: selectPlaylistSelectedItem(HMSPlaylistType.audio) as any,\n};\n\nexport const selectVideoPlaylist: HMSPlaylistSelector = {\n selection: selectPlaylistSelection(HMSPlaylistType.video),\n progress: selectPlaylistProgress(HMSPlaylistType.video),\n currentTime: selectPlaylistCurrentTime(HMSPlaylistType.video),\n playbackRate: selectPlaylistPlaybackRate(HMSPlaylistType.video),\n volume: selectPlaylistVolume(HMSPlaylistType.video),\n list: selectPlaylist(HMSPlaylistType.video),\n selectedItem: selectPlaylistSelectedItem(HMSPlaylistType.video) as any,\n};\n", "import { createSelector } from 'reselect';\nimport { byIDCurry } from './common';\nimport {\n selectFullAppData,\n selectHMSMessages,\n selectLocalPeerID,\n selectPeers,\n selectPeersMap,\n selectPollsMap,\n selectTracksMap,\n} from './selectors';\nimport {\n getScreenSharesByPeer,\n isAudio,\n isAudioPlaylist,\n isTrackEnabled,\n isVideo,\n isVideoPlaylist,\n} from './selectorUtils';\nimport { HMSLogger } from '../../common/ui-logger';\nimport {\n HMSAudioTrack,\n HMSGenericTypes,\n HMSPeer,\n HMSPeerID,\n HMSRoleName,\n HMSScreenVideoTrack,\n HMSStore,\n HMSTrack,\n HMSTrackID,\n HMSVideoTrack,\n} from '../schema';\n\nconst selectPeerID = (_store: HMSStore, peerID: HMSPeerID | undefined) => peerID;\nconst selectTrackID = (_store: HMSStore, trackID: HMSTrackID | undefined) => trackID;\nconst selectRoleName = (_store: HMSStore, roleName: HMSRoleName | undefined) => roleName;\nconst selectAppDataKey = (_store: HMSStore, key: string | undefined) => key;\nconst selectPollID = (_store: HMSStore, pollID: string | undefined) => pollID;\n\nconst selectPeerByIDBare = createSelector([selectPeersMap, selectPeerID], (storePeers, peerID) =>\n peerID ? storePeers[peerID] : null,\n);\n\nconst selectTrackByIDBare = createSelector([selectTracksMap, selectTrackID], (storeTracks, trackID) =>\n trackID ? storeTracks[trackID] : null,\n);\n\nconst selectVideoTrackByIDBare = createSelector([selectTracksMap, selectTrackID], (storeTracks, trackID) => {\n if (!trackID) {\n return null;\n }\n const track = storeTracks[trackID] as HMSVideoTrack | undefined;\n if (track?.type === 'video') {\n return track;\n }\n return null;\n});\n\nconst selectAudioTrackByIDBare = createSelector([selectTracksMap, selectTrackID], (storeTracks, trackID) => {\n if (!trackID) {\n return null;\n }\n const track = storeTracks[trackID] as HMSAudioTrack | undefined;\n if (track?.type === 'audio') {\n return track;\n }\n return null;\n});\n\nconst selectScreenAudioTrackByIDBare = createSelector([selectTracksMap, selectTrackID], (storeTracks, trackID) => {\n if (!trackID) {\n return null;\n }\n const track = storeTracks[trackID] as HMSAudioTrack | undefined;\n if (track?.type === 'audio' && track?.source === 'screen') {\n return track;\n }\n return null;\n});\nconst selectScreenVideoTrackByIDBare = createSelector([selectTracksMap, selectTrackID], (storeTracks, trackID) => {\n if (!trackID) {\n return null;\n }\n const track = storeTracks[trackID] as HMSScreenVideoTrack | undefined;\n if (track?.type === 'video' && track?.source === 'screen') {\n return track;\n }\n return null;\n});\n\nconst selectPollByIDBare = createSelector([selectPollsMap, selectPollID], (storePolls, pollID) =>\n pollID ? storePolls[pollID] : null,\n);\n\n/**\n * Select the {@link HMSPeer} object given a peer ID.\n */\nexport const selectPeerByID = byIDCurry(selectPeerByIDBare);\n\n/**\n * Select a particular key from ui app data by passed in key.\n * if key is not passed, full data is returned.\n */\nexport const selectAppData = byIDCurry(\n createSelector([selectFullAppData, selectAppDataKey], (appData, key) => {\n if (!appData) {\n return undefined;\n }\n if (key) {\n return appData[key];\n }\n return appData;\n }),\n);\n\n/**\n * Select a particular key from session store by passed in key.\n * if key is not passed, full data is returned.\n */\nexport function selectSessionStore<T extends HMSGenericTypes = { sessionStore: Record<string, any> }>(): (\n store: HMSStore<T>,\n) => T['sessionStore'] | undefined;\nexport function selectSessionStore<\n T extends HMSGenericTypes = { sessionStore: Record<string, any> },\n K extends keyof T['sessionStore'] = keyof T['sessionStore'],\n>(key: K): (store: HMSStore<T>) => T['sessionStore'][K] | undefined;\nexport function selectSessionStore<\n T extends HMSGenericTypes = { sessionStore: Record<string, any> },\n K extends keyof T['sessionStore'] = keyof T['sessionStore'],\n>(key?: K) {\n return (store: HMSStore<T>) => {\n if (!store.sessionStore) {\n return undefined;\n }\n if (key) {\n return store.sessionStore[key];\n }\n return store.sessionStore;\n };\n}\n\nexport const selectAppDataByPath = (...keys: string[]) =>\n createSelector([selectFullAppData], appData => {\n if (!appData) {\n return undefined;\n }\n if (keys && keys.length > 0) {\n let value = appData;\n for (const key of keys) {\n if (!key) {\n return value;\n }\n value = value?.[key];\n }\n return value;\n }\n return appData;\n });\n\n/**\n * Select the name of a {@link HMSPeer} given a peer ID.\n */\nexport const selectPeerNameByID = byIDCurry(createSelector(selectPeerByIDBare, peer => peer?.name));\n\n/**\n * Select the {@link HMSTrack} object given a track ID.\n */\nexport const selectTrackByID = byIDCurry(selectTrackByIDBare);\n\n/**\n * Select the {@link HMSVideoTrack} object given a track ID.\n */\nexport const selectVideoTrackByID = byIDCurry(selectVideoTrackByIDBare);\n\n/**\n * Select the {@link HMSAudioTrack} object given a track ID.\n */\nexport const selectAudioTrackByID = byIDCurry(selectAudioTrackByIDBare);\n\n/**\n * Select the {@link HMSScreenAudioTrack} object given a track ID.\n */\nexport const selectScreenAudioTrackByID = byIDCurry(selectScreenAudioTrackByIDBare);\n\n/**\n * Select the {@link HMSScreenVideoTrack} object given a track ID.\n */\nexport const selectScreenVideoTrackByID = byIDCurry(selectScreenVideoTrackByIDBare);\n\n/**\n * Select the primary video track of a peer given a peer ID.\n */\nexport const selectVideoTrackByPeerID = byIDCurry((store: HMSStore, peerID?: HMSPeerID): HMSVideoTrack | undefined => {\n const peer = selectPeerByIDBare(store, peerID);\n if (peer && peer.videoTrack && peer.videoTrack !== '') {\n return store.tracks[peer.videoTrack] as HMSVideoTrack;\n }\n return undefined;\n});\n\n/**\n * Select the primary audio track of a peer given a peer ID.\n */\nexport const selectAudioTrackByPeerID = byIDCurry((store: HMSStore, peerID?: HMSPeerID): HMSAudioTrack | undefined => {\n const peer = selectPeerByIDBare(store, peerID);\n if (peer && peer.audioTrack && peer.audioTrack !== '') {\n return store.tracks[peer.audioTrack] as HMSAudioTrack;\n }\n return undefined;\n});\n\n/**\n * Select the camera stream of a peer given a peer ID.\n * This is the primary video track of a peer.\n */\nexport const selectCameraStreamByPeerID = selectVideoTrackByPeerID;\n\n/**\n * Select an array of auxiliary tracks of a peer given a peer ID.\n */\nexport const selectAuxiliaryTracksByPeerID = byIDCurry((store: HMSStore, peerID?: HMSPeerID): HMSTrack[] => {\n const peer = selectPeerByIDBare(store, peerID);\n return peer?.auxiliaryTracks.map(trackID => store.tracks[trackID]) || [];\n});\n\nconst selectSpeakerByTrackID = (store: HMSStore, trackID: HMSTrackID | undefined) => {\n return trackID ? store.speakers[trackID] : null;\n};\n\n/**\n * Select the audio level of a track given a track ID.\n */\nexport const selectTrackAudioByID = byIDCurry(\n createSelector(selectSpeakerByTrackID, speaker => speaker?.audioLevel || 0),\n);\n\n/**\n * Select speaker object of audioTrack of a peer given a peer ID.\n */\nconst selectSpeakerByPeerID = (store: HMSStore, peerID: HMSPeerID | undefined) => {\n const peerAudioTrack = selectAudioTrackByPeerID(peerID)(store);\n return selectSpeakerByTrackID(store, peerAudioTrack?.id);\n};\n\n/**\n * Select audio level of audioTrack of a peer given a peer ID\u00DF.\n */\nexport const selectPeerAudioByID = byIDCurry(\n createSelector(selectSpeakerByPeerID, speaker => speaker?.audioLevel || 0),\n);\n\nexport const selectConnectionQualityByPeerID = byIDCurry((store: HMSStore, peerID: HMSPeerID | undefined) => {\n if (peerID) {\n return store.connectionQualities[peerID];\n }\n return undefined;\n});\n\n/**\n * Select the first auxiliary audio track of a peer given a peer ID.\n */\nexport const selectAuxiliaryAudioByPeerID = byIDCurry((store: HMSStore, peerID?: HMSPeerID) => {\n const peer = selectPeerByIDBare(store, peerID);\n if (peer) {\n const trackID = peer?.auxiliaryTracks.find(trackID => isAudio(store.tracks[trackID]));\n return trackID ? (store.tracks[trackID] as HMSAudioTrack) : undefined;\n }\n return undefined;\n});\n\nexport const selectVideoPlaylistVideoTrackByPeerID = byIDCurry(\n createSelector(selectTracksMap, selectPeerByIDBare, (tracks, peer) => {\n const trackID = peer?.auxiliaryTracks.find(trackID => {\n const track = tracks[trackID];\n return isVideoPlaylist(track) && isVideo(track);\n });\n return trackID ? (tracks[trackID] as HMSVideoTrack) : undefined;\n }),\n);\n\nexport const selectVideoPlaylistAudioTrackByPeerID = byIDCurry(\n createSelector(selectTracksMap, selectPeerByIDBare, (tracks, peer) => {\n const trackID = peer?.auxiliaryTracks.find(trackID => {\n const track = tracks[trackID];\n return isVideoPlaylist(track) && isAudio(track);\n });\n return trackID ? (tracks[trackID] as HMSAudioTrack) : undefined;\n }),\n);\n\nexport const selectAudioPlaylistTrackByPeerID = byIDCurry(\n createSelector(selectTracksMap, selectPeerByIDBare, (tracks, peer) => {\n const trackID = peer?.auxiliaryTracks.find(trackID => {\n const track = tracks[trackID];\n return isAudioPlaylist(track) && isAudio(track);\n });\n return trackID ? (tracks[trackID] as HMSAudioTrack) : undefined;\n }),\n);\n\nexport const selectScreenSharesByPeerId = byIDCurry(\n createSelector(selectTracksMap, selectPeerByIDBare, (tracks, peer) => {\n return getScreenSharesByPeer(tracks, peer);\n }),\n);\n\n/**\n * Select the screen share video track of a peer given a peer ID.\n */\nexport const selectScreenShareByPeerID = (id?: string) =>\n createSelector(selectScreenSharesByPeerId(id), screenshare => {\n return screenshare.video;\n });\n\n/**\n * Select the screen share audio track of a peer given a peer ID.\n */\nexport const selectScreenShareAudioByPeerID = (id?: string) =>\n createSelector(selectScreenSharesByPeerId(id), screenshare => {\n return screenshare.audio;\n });\n\n/**\n * Select a boolean denoting whether a peer has unmuted audio and sharing it to other peers.\n */\nexport const selectIsPeerAudioEnabled = byIDCurry((store: HMSStore, peerID?: string) => {\n const peer = selectPeerByIDBare(store, peerID);\n return isTrackEnabled(store, peer?.audioTrack);\n});\n\n/**\n * Select a boolean denoting whether a peer has unmuted video and sharing it to other peers.\n */\nexport const selectIsPeerVideoEnabled = byIDCurry((store: HMSStore, peerID?: string) => {\n const peer = selectPeerByIDBare(store, peerID);\n return isTrackEnabled(store, peer?.videoTrack);\n});\n\n/**\n * Select a boolean denoting whether you've muted an audio track locally(only for you) given a track ID.\n */\nexport const selectIsAudioLocallyMuted = byIDCurry((store: HMSStore, trackID?: string) => {\n if (trackID && store.tracks[trackID]) {\n return (store.tracks[trackID] as HMSAudioTrack).volume === 0;\n }\n return undefined;\n});\n\n/**\n * Select a boolean denoting whether you've muted the primary audio track of a peer locally(only for you) given a peer ID.\n */\nexport const selectIsLocallyMutedByPeerID = byIDCurry((store: HMSStore, peerID?: string) => {\n const peer = selectPeerByIDBare(store, peerID);\n return selectIsAudioLocallyMuted(peer?.audioTrack)(store);\n});\n\n/**\n * Select a boolean denoting whether you've muted the screen share audio track of a peer locally(only for you) given a peer ID.\n */\nexport const selectIsScreenShareLocallyMutedByPeerID = byIDCurry((store: HMSStore, peerID?: string) => {\n const track = selectScreenShareAudioByPeerID(peerID)(store);\n return selectIsAudioLocallyMuted(track?.id)(store);\n});\n\n/**\n * Select the local audio volume of an audio track given a track ID.\n *\n * NOTE: **Volume** of a track is different from **Audio Level** of a track,\n * - Audio Level measures the audio of a track and it comes from 100ms's servers.\n * - Volume is how loud you hear the audio of a track, this is controlled by you at the client side.\n */\nexport const selectAudioTrackVolume = byIDCurry((store: HMSStore, trackID?: string) => {\n const track = selectTrackByIDBare(store, trackID);\n if (track) {\n if (track.type !== 'audio') {\n HMSLogger.w('Please pass audio track here');\n return undefined;\n }\n return track.volume;\n }\n return undefined;\n});\n\n/**\n * Select the local audio volume of the primary audio track of a peer given a peer ID.\n */\nexport const selectAudioVolumeByPeerID = byIDCurry((store: HMSStore, peerID?: string) => {\n const peer = selectPeerByIDBare(store, peerID);\n return selectAudioTrackVolume(peer?.audioTrack)(store);\n});\n\n/**\n * Select the local audio volume of the screen share of a peer given a peer ID.\n */\nexport const selectScreenshareAudioVolumeByPeerID = byIDCurry((store: HMSStore, peerID?: string) => {\n const track = selectScreenShareAudioByPeerID(peerID)(store);\n return selectAudioTrackVolume(track?.id)(store);\n});\n\n/**\n * Select the current simulcast layer of a track given a track ID.\n */\nexport const selectSimulcastLayerByTrack = byIDCurry((store: HMSStore, trackID?: string) => {\n const track = selectTrackByIDBare(store, trackID);\n if (track) {\n if (track.type !== 'video') {\n HMSLogger.w('Please pass video track here');\n return undefined;\n }\n return track.layer;\n }\n return undefined;\n});\n\nconst selectMessagesByPeerIDInternal = createSelector(\n [selectHMSMessages, selectLocalPeerID, selectPeerID],\n (messages, localPeerID, peerID) => {\n if (!peerID) {\n return undefined;\n }\n return messages.filter(message => {\n // Broadcast message\n if (!message.recipientPeer && !message.recipientRoles?.length) {\n return false;\n }\n // if localPeer or peerID is not a sender remove this\n if (message.sender && ![localPeerID, peerID].includes(message.sender)) {\n return false;\n }\n // at this point we know the sender is one of local or passed in peer, check the recipient side\n return [localPeerID, peerID].includes(message.recipientPeer!);\n });\n },\n);\n\nconst selectMessagesByRoleInternal = createSelector([selectHMSMessages, selectRoleName], (messages, roleName) => {\n if (!roleName) {\n return undefined;\n }\n return messages.filter(message => {\n // Not Role message - Broadcast message or Private Peer message\n if (!message.recipientRoles?.length) {\n return false;\n }\n return message.recipientRoles?.includes(roleName);\n });\n});\n\nexport const selectBroadcastMessages = createSelector(selectHMSMessages, messages => {\n return messages.filter(message => {\n return !message.recipientPeer && !message.recipientRoles?.length;\n });\n});\n\nconst selectUnreadMessageCountByRole = createSelector([selectMessagesByRoleInternal, selectRoleName], messages => {\n if (!messages) {\n return 0;\n }\n return messages.filter(m => !m.read).length;\n});\n\nconst selectUnreadMessageCountByPeerID = createSelector([selectMessagesByPeerIDInternal, selectPeerID], messages => {\n if (!messages) {\n return 0;\n }\n return messages.filter(m => !m.read).length;\n});\n\nexport const selectBroadcastMessagesUnreadCount = createSelector(selectBroadcastMessages, messages => {\n return messages.filter(m => !m.read).length;\n});\n\nexport const selectMessagesByPeerID = byIDCurry(selectMessagesByPeerIDInternal);\n\nexport const selectMessagesByRole = byIDCurry(selectMessagesByRoleInternal);\n\nexport const selectMessagesUnreadCountByRole = byIDCurry(selectUnreadMessageCountByRole);\nexport const selectMessagesUnreadCountByPeerID = byIDCurry(selectUnreadMessageCountByPeerID);\n\n/**\n * Select an array of peers of a particular role\n * @param role HMSRoleName\n * @returns HMSPeer[]\n */\nexport const selectPeersByRole = (role: HMSRoleName) =>\n createSelector([selectPeers], peers => {\n return peers.filter(p => p.roleName === role);\n });\n\n/**\n * Select an array of peers of a particular role\n * @param roles HMSRoleName[]\n * @returns HMSPeer[]\n */\nexport const selectPeersByRoles = (roles: HMSRoleName[]) =>\n createSelector([selectPeers], (peers: HMSPeer[]) => {\n return peers.filter((peer: HMSPeer) => {\n return peer.roleName ? roles.includes(peer.roleName) : false;\n });\n });\n/**\n * Selects the peer metadata for the passed in peer and returns it as JSON. If metadata is not present\n * or conversion to JSON gives an error, an empty object is returned.\n * Please directly use peer.metadata in case the metadata is not JSON by design.\n */\nexport const selectPeerMetadata = (peerId: HMSPeerID) =>\n createSelector(selectPeerByID(peerId), peer => {\n try {\n return peer?.metadata && peer.metadata !== '' ? JSON.parse(peer.metadata) : {};\n } catch (error) {\n console.error('cannot parse peer metadata', error);\n return {};\n }\n });\n\nexport const selectPeerName = (peerId: HMSPeerID) => createSelector(selectPeerByID(peerId), peer => peer?.name);\n\nexport const selectPollByID = byIDCurry(selectPollByIDBare);\n", "import { HMSStatsStore, HMSStore } from '../schema';\n\nexport type StoreTypes = HMSStore | HMSStatsStore;\n\ntype byIDSelector<S extends StoreTypes, T> = (store: S, id?: string) => T;\n\n/**\n * StoreSelector is a function that takes in {@link HMSStore} as argument\n * and returns a part of the store that is queried using the selector.\n * @typeParam T Part of the store that you wish to query.\n */\nexport type StoreSelector<S extends StoreTypes, T> = (store: S) => T;\n\n/**\n * takes in a normal selector which has store and id as input and curries it to make it easier to use.\n * Before: store.getState((store) => normalSelector(store, peerID))\n * After: store.getState(curriedSelector(peerID))\n */\nexport function byIDCurry<S extends StoreTypes, T>(selector: byIDSelector<S, T>): (id?: string) => StoreSelector<S, T> {\n return (id?: string) => {\n return (store: S) => selector(store, id);\n };\n}\n", "import {\n DeviceMap,\n DeviceType,\n HLSConfig,\n HLSMeetingURLVariant,\n HLSTimedMetadata,\n HMSAudioPlugin,\n HMSAudioPluginType,\n HMSAudioTrackSettings,\n HMSConfig,\n HMSConfigInitialSettings,\n HMSConnectionQuality,\n HMSDeviceChangeEvent,\n HMSException,\n HMSFrameworkInfo,\n HMSHLS,\n HMSLocalPeer,\n HMSLocalTrackStats,\n HMSLogLevel,\n HMSMessage,\n HMSPeer,\n HMSPeerStats,\n HMSPeerUpdate,\n HMSPlaylistItem,\n HMSPlaylistManager,\n HMSPlaylistProgressEvent,\n HMSPoll,\n HMSPollCreateParams,\n HMSPollQuestionAnswer,\n HMSPollQuestionCreateParams,\n HMSPollQuestionType,\n HMSPollsUpdate,\n HMSPreferredSimulcastLayer,\n HMSPreviewConfig,\n HMSRecording,\n HMSRemotePeer,\n HMSRemoteTrackStats,\n HMSRole,\n HMSRoom,\n HMSRoomUpdate,\n HMSRTMP,\n HMSScreenShareConfig,\n HMSSimulcastLayer,\n HMSSimulcastLayerDefinition,\n HMSSpeaker,\n HMSTrackStats,\n HMSTrackUpdate,\n HMSVideoPlugin,\n HMSVideoPluginCanvasContextType,\n HMSVideoPluginType,\n HMSVideoTrackSettings,\n HMSWebrtcInternals,\n HMSWebrtcStats,\n parsedUserAgent,\n RID,\n RTMPRecordingConfig,\n ScreenCaptureHandle,\n simulcastMapping,\n TokenRequest,\n TokenRequestOptions,\n} from '@100mslive/hms-video';\n\nexport {\n HMSException,\n HMSSimulcastLayer,\n HMSRoomUpdate,\n HMSPeerUpdate,\n HMSTrackUpdate,\n HMSPollsUpdate,\n HMSLogLevel,\n HMSAudioPluginType,\n HMSVideoPluginType,\n HMSVideoPluginCanvasContextType,\n parsedUserAgent,\n simulcastMapping,\n DeviceType,\n HMSPollQuestionType,\n};\n\nexport type {\n DeviceMap,\n HMSPeer,\n HMSRoom,\n HMSMessage,\n HMSSpeaker,\n HMSConfig,\n HMSConfigInitialSettings,\n HMSPreviewConfig,\n HMSAudioTrackSettings,\n HMSVideoTrackSettings,\n HMSRole,\n HMSLocalPeer,\n HMSRemotePeer,\n HMSSimulcastLayerDefinition,\n HMSDeviceChangeEvent,\n HMSPlaylistItem,\n HMSPlaylistManager,\n HMSPlaylistProgressEvent,\n RTMPRecordingConfig,\n HMSRecording,\n HMSRTMP,\n HMSWebrtcInternals,\n HMSWebrtcStats,\n HMSVideoPlugin,\n HMSAudioPlugin,\n HLSConfig,\n HLSTimedMetadata,\n HLSMeetingURLVariant,\n HMSHLS,\n HMSPeerStats,\n HMSTrackStats,\n HMSLocalTrackStats,\n HMSRemoteTrackStats,\n HMSConnectionQuality,\n HMSScreenShareConfig,\n HMSFrameworkInfo,\n RID,\n ScreenCaptureHandle,\n HMSPreferredSimulcastLayer,\n TokenRequest,\n TokenRequestOptions,\n HMSPoll,\n HMSPollCreateParams,\n HMSPollQuestionAnswer,\n HMSPollQuestionCreateParams,\n};\n", "import { HMSLogLevel } from '../core/hmsSDKStore/sdkTypes';\n\nconst HMS_STORE_TAG = 'HMS-Store:';\n\nexport class HMSLogger {\n static level: HMSLogLevel = HMSLogLevel.VERBOSE;\n\n static v(tag: string, ...data: any[]) {\n this.log(HMSLogLevel.VERBOSE, tag, ...data);\n }\n\n static d(...data: any[]) {\n this.log(HMSLogLevel.DEBUG, ...data);\n }\n\n static i(...data: any[]) {\n this.log(HMSLogLevel.INFO, ...data);\n }\n\n static w(...data: any[]) {\n this.log(HMSLogLevel.WARN, ...data);\n }\n\n static e(...data: any[]) {\n this.log(HMSLogLevel.ERROR, ...data);\n }\n\n static time(mark: string) {\n this.log(HMSLogLevel.TIME, '[HMSPerformanceTiming]', mark);\n }\n\n static timeEnd(mark: string) {\n this.log(HMSLogLevel.TIMEEND, '[HMSPerformanceTiming]', mark, mark);\n }\n\n static cleanup() {\n performance.clearMarks();\n performance.clearMeasures();\n }\n\n /* eslint-disable */\n private static log(level: HMSLogLevel, ...data: any[]) {\n if (this.level.valueOf() > level.valueOf()) {\n return;\n }\n\n switch (level) {\n case HMSLogLevel.VERBOSE: {\n console.log(HMS_STORE_TAG, ...data);\n break;\n }\n case HMSLogLevel.DEBUG: {\n console.debug(HMS_STORE_TAG, ...data);\n break;\n }\n case HMSLogLevel.INFO: {\n console.info(HMS_STORE_TAG, ...data);\n break;\n }\n case HMSLogLevel.WARN: {\n console.warn(HMS_STORE_TAG, ...data);\n break;\n }\n case HMSLogLevel.ERROR: {\n console.error(HMS_STORE_TAG, ...data);\n break;\n }\n case HMSLogLevel.TIME: {\n performance.mark(data[1]);\n break;\n }\n case HMSLogLevel.TIMEEND: {\n const tag = data[0];\n const mark = data[1];\n try {\n const entry = performance.measure(mark, mark);\n //@ts-ignore\n this.log(HMSLogLevel.DEBUG, tag, mark, entry?.duration);\n performance.clearMarks(mark);\n performance.clearMeasures(mark);\n } catch (error) {\n this.log(HMSLogLevel.DEBUG, tag, mark, error);\n }\n break;\n }\n }\n }\n}\n", "import { createSelector } from 'reselect';\nimport { HMSRole } from '@100mslive/hms-video';\nimport { selectLocalPeerRole, selectPeersMap, selectPreviewRole, selectRolesMap, selectTracksMap } from './selectors';\nimport { isRoleAllowedToPublish } from './selectorUtils';\nimport { HMSPeer, HMSStore } from '../schema';\n\nexport interface HMSPeerWithMuteStatus {\n peer: HMSPeer;\n isAudioEnabled?: boolean;\n}\n\n/**\n * @privateRemarks\n * this is more friendly to UI format, the object in store has only peer id and role name instead of the full objects\n */\nexport interface HMSRoleChangeRequest {\n requestedBy?: HMSPeer;\n role: HMSRole;\n token: string;\n}\n\nexport const selectPeersWithAudioStatus = createSelector([selectPeersMap, selectTracksMap], (peersMap, tracksMap) => {\n const participants: HMSPeerWithMuteStatus[] = Object.values(peersMap).map(peer => {\n return {\n peer: peer,\n isAudioEnabled: peer.audioTrack ? tracksMap[peer.audioTrack]?.enabled : false,\n };\n });\n return participants;\n});\n\nconst selectRoleChangeStoreRequest = (store: HMSStore) => {\n return store.roleChangeRequests[0] || null;\n};\n\n/**\n * Select the role change request received for your local peer.\n */\nexport const selectRoleChangeRequest = createSelector(\n [selectRoleChangeStoreRequest, selectPeersMap, selectRolesMap],\n (request, peersMap, rolesMap): HMSRoleChangeRequest | null => {\n if (!request) {\n return null;\n }\n return {\n requestedBy: request.requestedBy ? peersMap[request.requestedBy] : undefined,\n role: rolesMap[request.roleName],\n token: request.token,\n };\n },\n);\n\n/**\n * Select what streams is the local peer allowed to publish from video, audio and screenshare.\n */\nexport const selectIsAllowedToPublish = createSelector([selectLocalPeerRole], role => isRoleAllowedToPublish(role));\n\n/**\n * Select what streams is the local peer allowed to preview from video, audio\n */\nexport const selectIsAllowedToPreviewMedia = createSelector([selectPreviewRole], role => isRoleAllowedToPublish(role));\n", "import { createSelector } from 'reselect';\nimport {\n selectLocalAudioTrackID,\n selectLocalVideoTrackID,\n selectPeers,\n selectRolesMap,\n selectRoom,\n selectTracksMap,\n} from './selectors';\nimport { isRoleAllowedToPublish } from './selectorUtils';\nimport { HMSPeer, HMSTrack } from '../schema';\n\nexport const selectRoleByRoleName = (roleName: string) =>\n createSelector([selectRolesMap], rolesMap => rolesMap[roleName]);\n\nexport const selectIsRoleAllowedToPublish = (roleName: string) => {\n return createSelector(selectRoleByRoleName(roleName), role => isRoleAllowedToPublish(role));\n};\n\nconst selectLocalVideoPlugins = createSelector([selectLocalVideoTrackID, selectTracksMap], (trackID, tracksMap) => {\n let track: HMSTrack | null = null;\n if (trackID) {\n track = tracksMap[trackID];\n }\n return track?.plugins || [];\n});\n\nconst selectLocalAudioPlugins = createSelector([selectLocalAudioTrackID, selectTracksMap], (trackID, tracksMap) => {\n let track: HMSTrack | null = null;\n if (trackID) {\n track = tracksMap[trackID];\n }\n return track?.plugins || [];\n});\n\nexport const selectIsLocalVideoPluginPresent = (pluginName: string) => {\n return createSelector([selectLocalVideoPlugins], plugins => {\n return plugins.includes(pluginName);\n });\n};\n\nexport const selectIsLocalAudioPluginPresent = (pluginName: string) => {\n return createSelector([selectLocalAudioPlugins], plugins => {\n return plugins.includes(pluginName);\n });\n};\n\n/**\n * Selects the first peer passing the condition given by the argument predicate function\n *\n * Ex: to select a peer whose metadata has spotlight set to true(assuming peer.metadata is a valid json string), use\n * ```js\n * const spotlightPeer = useHMSStore(selectPeerByCondition(peer => JSON.parse(peer.metadata).spotlight))\n * ```\n */\nexport const selectPeerByCondition = (predicate: (peer: HMSPeer) => boolean) =>\n createSelector(selectPeers, peers => {\n return peers.find(predicate);\n });\n\n/**\n * Selects all peers passing the condition given by the argument predicate function\n *\n * Ex: to select peers with isHandRaised set to true in their metadata(assuming peer.metadata is a valid json string), use\n * ```js\n * const handRaisedPeers = useHMSStore(selectPeersByCondition(peer => JSON.parse(peer.metadata).isHandRaised))\n * ```\n */\nexport const selectPeersByCondition = (predicate: (peer: HMSPeer) => boolean) =>\n createSelector(selectPeers, peers => {\n return peers.filter(predicate);\n });\n\n/**\n * Returns a boolean to indicate if the local peer joined within the past `timeMs` milliseconds.\n *\n * Ex: to know if the local peer joined within the last one second\n * ```js\n * const joinedWithinASecond = useHMSStore(selectDidIJoinWithin(1000));\n * ```\n */\nexport const selectDidIJoinWithin = (timeMs: number) =>\n createSelector(selectRoom, room => room.joinedAt && Date.now() - room.joinedAt.getTime() <= timeMs);\n", "import { HMSLocalTrack as SDKHMSLocalTrack, HMSPoll } from '@100mslive/hms-video';\nimport { HMSPeer, HMSPeerID, HMSScreenVideoTrack, HMSTrack, HMSTrackID, HMSVideoTrack } from '../../schema';\nimport { HMSPeerStats, HMSTrackStats } from '../sdkTypes';\n\n/**\n * updates draftPeers with newPeers ensuring minimal reference changes\n * @remarks\n * This is mutable and impure function, it modifies the passed in data to ensure\n * minimal reference changes\n * @param draftPeers the current peers object in store, an immer draft object\n * @param newPeers the latest update which needs to be stored\n * @param newHmsTracks this will be update if required\n * @param newHmsSDkTracks this is future value of local hms tacks map\n */\nexport const mergeNewPeersInDraft = (\n draftPeers: Record<HMSPeerID, HMSPeer>,\n newPeers: Record<HMSPeerID, Partial<HMSPeer>>,\n) => {\n const peerIDs = union(Object.keys(draftPeers), Object.keys(newPeers));\n for (const peerID of peerIDs) {\n const oldPeer = draftPeers[peerID];\n const newPeer = newPeers[peerID];\n if (isEntityUpdated(oldPeer, newPeer)) {\n if (areArraysEqual(oldPeer.auxiliaryTracks, newPeer.auxiliaryTracks)) {\n newPeer.auxiliaryTracks = oldPeer.auxiliaryTracks;\n }\n Object.assign(oldPeer, newPeer);\n } else if (isEntityRemoved(oldPeer, newPeer)) {\n delete draftPeers[peerID];\n } else if (isEntityAdded(oldPeer, newPeer)) {\n draftPeers[peerID] = newPeer as HMSPeer;\n }\n }\n};\n\nexport const mergeNewTracksInDraft = (\n draftTracks: Record<HMSTrackID, HMSTrack>,\n newTracks: Record<HMSTrackID, Partial<HMSTrack>>,\n) => {\n const trackIDs = union(Object.keys(draftTracks), Object.keys(newTracks));\n for (const trackID of trackIDs) {\n const oldTrack = draftTracks[trackID];\n const newTrack = newTracks[trackID];\n if (isEntityUpdated(oldTrack, newTrack)) {\n mergeTrackArrayFields(oldTrack, newTrack);\n Object.assign(oldTrack, newTrack);\n } else if (isEntityRemoved(oldTrack, newTrack)) {\n delete draftTracks[trackID];\n } else if (isEntityAdded(oldTrack, newTrack)) {\n draftTracks[trackID] = newTrack as HMSTrack;\n }\n }\n};\n\nexport const mergeNewPollsInDraft = (\n draftPolls: Record<string, HMSPoll>,\n newPolls: Record<string, Partial<HMSPoll>>,\n) => {\n const pollIDs = union(Object.keys(draftPolls), Object.keys(newPolls));\n for (const pollID of pollIDs) {\n const oldPoll = draftPolls[pollID];\n const newPoll = newPolls[pollID];\n if (isEntityUpdated(oldPoll, newPoll)) {\n if (oldPoll.questions && areArraysEqual(oldPoll.questions, newPoll.questions)) {\n newPoll.questions = oldPoll.questions;\n }\n Object.assign(oldPoll, newPoll);\n } else if (isEntityAdded(oldPoll, newPoll)) {\n draftPolls[pollID] = newPoll as HMSPoll;\n }\n }\n};\n\nexport const mergeNewIndividualStatsInDraft = <TID extends string, T extends HMSPeerStats | HMSTrackStats>(\n draftStats: Record<TID, T | undefined>,\n newStats: Record<TID, Partial<T | undefined>>,\n) => {\n const IDs = union(Object.keys(draftStats), Object.keys(newStats)) as TID[];\n for (const trackID of IDs) {\n const oldStat = draftStats[trackID];\n const newStat = newStats[trackID];\n if (isEntityUpdated(oldStat, newStat)) {\n Object.assign(oldStat!, newStat);\n } else if (isEntityRemoved(oldStat, newStat)) {\n delete draftStats[trackID];\n } else if (isEntityAdded(oldStat, newStat)) {\n draftStats[trackID] = newStat as T;\n }\n }\n};\n\nexport const mergeLocalTrackStats = (\n draftStats: Record<HMSTrackID, HMSTrackStats[] | undefined>,\n newStats: Record<HMSTrackID, Record<string, HMSTrackStats>>,\n tracks: SDKHMSLocalTrack[],\n) => {\n const trackMap: Record<string, HMSTrackStats[]> = tracks.reduce((acc, track) => {\n // @ts-ignore\n acc[track.firstTrackId] = Object.values(newStats[track.getTrackIDBeingSent()] || {}).sort((a, b) => {\n if (!a.rid || !b.rid) {\n return 0;\n }\n return a.rid < b.rid ? -1 : 1;\n });\n return acc;\n }, {});\n const IDs = union(Object.keys(draftStats), Object.keys(trackMap));\n for (const trackID of IDs) {\n if (!trackMap[trackID]) {\n delete draftStats[trackID];\n continue;\n }\n draftStats[trackID] = trackMap[trackID];\n }\n};\n\n/**\n * array's are usually created with new reference, avoid that update if both arrays are same\n */\nexport const mergeTrackArrayFields = (oldTrack: HMSTrack, newTrack: Partial<HMSTrack>) => {\n if (oldTrack.plugins && areArraysEqual(oldTrack.plugins, newTrack.plugins)) {\n newTrack.plugins = oldTrack.plugins;\n }\n if (\n oldTrack.type === 'video' &&\n oldTrack.layerDefinitions &&\n areArraysEqual(oldTrack.layerDefinitions, (newTrack as HMSVideoTrack | HMSScreenVideoTrack).layerDefinitions)\n ) {\n (newTrack as HMSVideoTrack | HMSScreenVideoTrack).layerDefinitions = oldTrack.layerDefinitions;\n }\n};\n\nexport const isEntityUpdated = <T>(oldItem: T, newItem: T) => oldItem && newItem;\nconst isEntityRemoved = <T>(oldItem: T, newItem: T) => oldItem && !newItem;\nconst isEntityAdded = <T>(oldItem: T, newItem: T) => !oldItem && newItem;\n\n// eslint-disable-next-line complexity\nexport const areArraysEqual = <T>(arr1: T[], arr2?: T[]): boolean => {\n if (arr1 === arr2 || (arr1.length === 0 && arr2?.length === 0)) {\n // reference check\n return true;\n }\n if (!arr1 || !arr2 || !(arr1.length === arr2.length)) {\n return false;\n }\n for (let i = 0; i < arr1.length; i++) {\n if (arr1[i] !== arr2[i]) {\n return false;\n }\n }\n return true;\n};\n\nconst union = <T>(arr1: T[], arr2: T[]): T[] => {\n const set: Set<T> = new Set();\n for (const elem of arr1) {\n set.add(elem);\n }\n for (const elem of arr2) {\n set.add(elem);\n }\n return Array.from(set);\n};\n", "import { HMSPeerStats, HMSSdk, HMSTrackStats, HMSWebrtcStats } from '@100mslive/hms-video';\nimport { mergeLocalTrackStats, mergeNewIndividualStatsInDraft } from '../hmsSDKStore/sdkUtils/storeMergeUtils';\nimport { IHMSStatsStore, IHMSStore } from '../IHMSStore';\nimport { createDefaultStatsStore, HMSPeerID, HMSRoomState, HMSTrack, HMSTrackID } from '../schema';\nimport {\n selectLocalAudioTrackID,\n selectLocalPeerID,\n selectLocalVideoTrackID,\n selectRoomState,\n selectTracksMap,\n} from '../selectors';\n\ntype Unsubscribe = (() => void) | undefined;\nexport const subscribeToSdkWebrtcStats = (sdk: HMSSdk, webrtcStore: IHMSStatsStore, store: IHMSStore) => {\n // also used as flag to check if webrtc internals has been initialised\n let unsubscribe: Unsubscribe;\n /**\n * Connected to room, webrtc internals can be initialized\n */\n if (store.getState(selectRoomState) === HMSRoomState.Connected) {\n unsubscribe = initAndSubscribeWebrtcStore(sdk, webrtcStore, store);\n }\n\n /**\n * Subscribe to room state for 2 purposes:\n * - unsubscribe on leave\n * - if internals is called before join is completed, init internals when roomState changes to connected\n */\n store.subscribe(roomState => {\n if ([HMSRoomState.Connected, HMSRoomState.Reconnecting].includes(roomState)) {\n if (!unsubscribe) {\n unsubscribe = initAndSubscribeWebrtcStore(sdk, webrtcStore, store);\n }\n // room state can go to disconnecting and back to connected if leave fails, we don't want to resubscribe in that case\n } else if ([HMSRoomState.Disconnected, HMSRoomState.Failed].includes(roomState)) {\n if (unsubscribe) {\n resetHMSStatsStore(webrtcStore, roomState);\n unsubscribe();\n // set flag to defined after unsubscribing to enable subscribing again\n unsubscribe = undefined;\n }\n }\n }, selectRoomState);\n};\n\nconst initAndSubscribeWebrtcStore = (sdk: HMSSdk, webrtcStore: IHMSStatsStore, store: IHMSStore) => {\n const unsubLocalPeer = updateLocalPeerInWebrtcStore(store, webrtcStore);\n\n sdk.getWebrtcInternals()?.start();\n const unsubSdkStats = sdk\n .getWebrtcInternals()\n ?.onStatsChange(stats => updateWebrtcStoreStats(webrtcStore, stats, store, sdk));\n\n return () => {\n unsubLocalPeer();\n unsubSdkStats && unsubSdkStats();\n };\n};\n\nconst updateLocalPeerInWebrtcStore = (store: IHMSStore, webrtcStore: IHMSStatsStore) => {\n let unsubID: Unsubscribe, unsubVideoTrackID: Unsubscribe, unsubAudioTrackID: Unsubscribe;\n if (store.getState(selectLocalPeerID)) {\n webrtcStore.namedSetState(draft => {\n draft.localPeer.id = store.getState(selectLocalPeerID);\n }, 'localpeer-id');\n } else {\n unsubID = store.subscribe(localPeerID => {\n localPeerID &&\n webrtcStore.namedSetState(draft => {\n draft.localPeer.id = localPeerID;\n }, 'localpeer-id');\n }, selectLocalPeerID);\n }\n\n if (store.getState(selectLocalVideoTrackID)) {\n webrtcStore.namedSetState(draft => {\n draft.localPeer.videoTrack = store.getState(selectLocalVideoTrackID);\n }, 'localpeer-videotrack-id');\n } else {\n unsubVideoTrackID = store.subscribe(videoTrackID => {\n videoTrackID &&\n webrtcStore.namedSetState(draft => {\n draft.localPeer.videoTrack = videoTrackID;\n }, 'localpeer-videotrack-id');\n }, selectLocalVideoTrackID);\n }\n\n if (store.getState(selectLocalAudioTrackID)) {\n webrtcStore.namedSetState(draft => {\n draft.localPeer.audioTrack = store.getState(selectLocalAudioTrackID);\n }, 'localpeer-audiotrack-id');\n } else {\n unsubAudioTrackID = store.subscribe(audioTrackID => {\n audioTrackID &&\n webrtcStore.namedSetState(draft => {\n draft.localPeer.audioTrack = audioTrackID;\n }, 'localpeer-audiotrack-id');\n }, selectLocalAudioTrackID);\n }\n\n return () => {\n unsubID?.();\n unsubVideoTrackID?.();\n unsubAudioTrackID?.();\n };\n};\n\nconst updateWebrtcStoreStats = (\n webrtcStore: IHMSStatsStore,\n stats: HMSWebrtcStats,\n hmsStore: IHMSStore,\n sdk: HMSSdk,\n) => {\n const tracks: Record<HMSTrackID, HMSTrack> = hmsStore.getState(selectTracksMap);\n webrtcStore.namedSetState(store => {\n const localPeerID = hmsStore.getState(selectLocalPeerID);\n const newTrackStats: Record<HMSTrackID, HMSTrackStats> = {};\n const trackIDs = Object.keys(tracks).filter(trackID => tracks[trackID].peerId !== localPeerID);\n\n for (const trackID of trackIDs) {\n const sdkTrackStats = stats.getRemoteTrackStats(trackID);\n if (sdkTrackStats) {\n newTrackStats[trackID] = sdkTrackStats;\n }\n }\n\n mergeNewIndividualStatsInDraft<HMSTrackID, HMSTrackStats>(store.remoteTrackStats, newTrackStats);\n\n // @TODO: Include all peer stats, own ticket, transmit local peer stats to other peer's using biz\n const newPeerStats = { [localPeerID]: stats.getLocalPeerStats() };\n mergeNewIndividualStatsInDraft<HMSPeerID, HMSPeerStats>(store.peerStats, newPeerStats);\n // @ts-ignore\n mergeLocalTrackStats(store.localTrackStats, stats.getLocalTrackStats(), sdk.store.getLocalPeerTracks());\n }, 'webrtc-stats');\n};\n\nconst resetHMSStatsStore = (store: IHMSStatsStore, reason = 'resetState') => {\n store.namedSetState(draft => {\n Object.assign(draft, createDefaultStatsStore());\n }, reason);\n};\n", "import { isBrowser } from '@100mslive/hms-video';\n\nexport const storeNameWithTabTitle = (storeName: string) => {\n return isBrowser ? `${storeName} ${document.title}` : storeName;\n};\n", "import { produce } from 'immer';\nimport shallow from 'zustand/shallow';\nimport create, {\n EqualityChecker,\n PartialState,\n SetState,\n State,\n StateSelector,\n StateSliceListener,\n StoreApi,\n} from 'zustand/vanilla';\nimport { HMSSdk, isBrowser } from '@100mslive/hms-video';\nimport { HMSNotifications } from './HMSNotifications';\nimport { HMSSDKActions } from './HMSSDKActions';\nimport { NamedSetState } from './internalTypes';\nimport { storeNameWithTabTitle } from '../../common/storeName';\nimport { IHMSActions } from '../IHMSActions';\nimport { IHMSStatsStoreReadOnly, IHMSStore, IHMSStoreReadOnly, IStore } from '../IHMSStore';\nimport { createDefaultStoreState, HMSGenericTypes, HMSStore } from '../schema';\nimport { IHMSNotifications } from '../schema/notification';\nimport { HMSStats } from '../';\n\ndeclare global {\n interface Window {\n __hms: HMSReactiveStore;\n __triggerBeamEvent__: (args: any) => void;\n }\n}\n\nexport class HMSReactiveStore<T extends HMSGenericTypes = { sessionStore: Record<string, any> }> {\n private readonly sdk?: HMSSdk;\n private readonly actions: IHMSActions<T>;\n private readonly store: IHMSStore<T>;\n private readonly notifications: HMSNotifications<T>;\n private stats?: HMSStats;\n /** @TODO store flag for both HMSStore and HMSInternalsStore */\n private initialTriggerOnSubscribe: boolean;\n\n constructor(hmsStore?: IHMSStore<T>, hmsActions?: IHMSActions<T>, hmsNotifications?: HMSNotifications<T>) {\n if (hmsStore) {\n this.store = hmsStore;\n } else {\n this.store = HMSReactiveStore.createNewHMSStore<HMSStore<T>>(\n storeNameWithTabTitle('HMSStore'),\n createDefaultStoreState,\n );\n }\n if (hmsNotifications) {\n this.notifications = hmsNotifications;\n } else {\n this.notifications = new HMSNotifications(this.store);\n }\n if (hmsActions) {\n this.actions = hmsActions;\n } else {\n this.sdk = new HMSSdk();\n this.actions = new HMSSDKActions(this.store, this.sdk, this.notifications);\n }\n\n // @ts-ignore\n this.actions.setFrameworkInfo({ type: 'js', sdkVersion: require('../../../package.json').version });\n\n this.initialTriggerOnSubscribe = false;\n\n if (isBrowser) {\n // @ts-ignore\n window.__hms = this;\n }\n }\n\n /**\n * By default store.subscribe does not call the handler with the current state at time of subscription,\n * this behaviour can be modified by calling this function. What it means is that instead of calling the\n * handler only for changes which happen post subscription we'll also call it exactly once at the time\n * of subscription with the current state. This behaviour is similar to that of BehaviourSubject in rxjs.\n * This will be an irreversible change\n *\n * Note: you don't need this if you're using our react hooks, it takes care of this requirement.\n */\n triggerOnSubscribe(): void {\n if (this.initialTriggerOnSubscribe) {\n // already done\n return;\n }\n HMSReactiveStore.makeStoreTriggerOnSubscribe(this.store);\n this.initialTriggerOnSubscribe = true;\n }\n\n /**\n * A reactive store which has a subscribe method you can use in combination with selectors\n * to subscribe to a subset of the store. The store serves as a single source of truth for\n * all data related to the corresponding HMS Room.\n */\n getStore(): IHMSStoreReadOnly {\n return this.store;\n }\n\n /**\n * Any action which may modify the store or may need to talk to the SDK will happen\n * through the IHMSActions instance returned by this\n *\n * @deprecated use getActions\n */\n getHMSActions(): IHMSActions<T> {\n return this.actions;\n }\n\n /**\n * Any action which may modify the store or may need to talk to the SDK will happen\n * through the IHMSActions instance returned by this\n */\n getActions(): IHMSActions<T> {\n return this.actions;\n }\n\n /**\n * This return notification handler function to which you can pass your callback to\n * receive notifications like peer joined, peer left, etc. to show in your UI or use\n * for analytics\n */\n getNotifications(): IHMSNotifications {\n return { onNotification: this.notifications.onNotification };\n }\n\n /**\n * @alpha\n */\n getStats = (): IHMSStatsStoreReadOnly => {\n if (!this.stats) {\n this.stats = new HMSStats(this.store as unknown as IHMSStore, this.sdk);\n }\n return this.stats;\n };\n\n /**\n * @internal\n */\n static createNewHMSStore<T extends State>(storeName: string, defaultCreatorFn: () => T): IStore<T> {\n const hmsStore = create<T>(() => defaultCreatorFn());\n // make set state immutable, by passing functions through immer\n const savedSetState = hmsStore.setState;\n hmsStore.setState = (partial: any) => {\n const nextState = typeof partial === 'function' ? produce(partial) : partial;\n savedSetState(nextState);\n };\n // add option to pass selector to getState\n const prevGetState = hmsStore.getState;\n // eslint-disable-next-line complexity\n hmsStore.getState = <StateSlice>(selector?: StateSelector<T, StateSlice>) => {\n return selector ? selector(prevGetState()) : prevGetState();\n };\n HMSReactiveStore.compareWithShallowCheckInSubscribe(hmsStore);\n const namedSetState = HMSReactiveStore.setUpDevtools(hmsStore, storeName);\n return { ...hmsStore, namedSetState };\n }\n\n /**\n * @internal\n */\n static makeStoreTriggerOnSubscribe<T extends State>(store: IStore<T>) {\n const prevSubscribe = store.subscribe;\n store.subscribe = <StateSlice>(\n listener: StateSliceListener<StateSlice>,\n selector?: StateSelector<T, StateSlice>,\n equalityFn?: EqualityChecker<StateSlice>,\n ): (() => void) => {\n // initial call, the prev state will always be null for this\n listener(store.getState(selector), undefined as unknown as StateSlice);\n // then subscribe\n return prevSubscribe(listener, selector!, equalityFn);\n };\n }\n\n /**\n * use shallow equality check by default for subscribe to optimize for array/object selectors.\n * by default zustand does only reference matching so something like, getPeers for eg. would trigger\n * the corresponding component even if peers didn't actually change, as selectPeers creates a new array every time.\n * Although the array reference changes, the order of peers and peer objects don't themselves change in this case,\n * and a shallow check avoids that triggering.\n * @private\n */\n private static compareWithShallowCheckInSubscribe<T extends State>(hmsStore: StoreApi<T>) {\n const prevSubscribe = hmsStore.subscribe;\n hmsStore.subscribe = <StateSlice>(\n listener: StateSliceListener<StateSlice>,\n selector?: StateSelector<T, StateSlice>,\n equalityFn?: EqualityChecker<StateSlice>,\n ): (() => void) => {\n if (!selector) {\n selector = (store): StateSlice => store as unknown as StateSlice;\n }\n equalityFn = equalityFn || shallow;\n return prevSubscribe(listener, selector, equalityFn);\n };\n }\n\n /**\n * @private\n * @privateRemarks\n * sets up redux devtools for the store, so redux extension can be used to visualize the store.\n * zustand's default devtool middleware only enhances the set function, we're here creating another nameSetState in\n * IHMStore which behaves like setState but takes an extra parameter for action name\n * https://github.com/zalmoxisus/redux-devtools-extension/blob/master/docs/API/Methods.md\n * modified version of zustand's devtools - https://github.com/pmndrs/zustand/blob/v3.5.7/src/middleware.ts#L46\n */\n private static setUpDevtools<T extends State>(api: StoreApi<T>, prefix: string): NamedSetState<T> {\n let extension;\n try {\n extension = (window as any).__REDUX_DEVTOOLS_EXTENSION__ || (window as any).top.__REDUX_DEVTOOLS_EXTENSION__;\n } catch {}\n if (!extension) {\n return (fn: any) => {\n api.setState(fn);\n };\n }\n const devtools = extension.connect(HMSReactiveStore.devtoolsOptions(prefix));\n devtools.prefix = prefix ? `${prefix} > ` : '';\n const savedSetState = api.setState;\n api.setState = (fn: any) => {\n savedSetState(fn);\n devtools.send(`${devtools.prefix}setState`, api.getState());\n };\n\n devtools.subscribe(HMSReactiveStore.devtoolsSubscribe(devtools, api, savedSetState));\n\n devtools.send('setUpStore', api.getState());\n\n return (fn: any, action?: string) => {\n savedSetState(fn);\n const actionName = action ? action : `${devtools.prefix}action`;\n devtools.send(actionName, api.getState());\n };\n }\n\n /**\n * https://github.com/zalmoxisus/redux-devtools-extension/blob/master/docs/API/Arguments.md\n */\n private static devtoolsOptions(prefix: string) {\n return {\n name: prefix,\n actionsBlacklist: ['audioLevel', 'playlistProgress', 'connectionQuality'], // very high frequency update, pollutes the action history\n };\n }\n\n /**\n * redux devtools allows for time travel debugging where it sends an action to update the store, users can\n * also export and import state in the devtools, listen to the corresponding functions from devtools and take\n * required action.\n * @param devtools - reference to devtools extension object\n * @param api\n * @param savedSetState - setState saved before its modified to update devtools\n * @private\n */\n private static devtoolsSubscribe<T extends State>(devtools: any, api: StoreApi<T>, savedSetState: SetState<T>) {\n // disabling complexity check instead of refactoring so as to keep the code close to zustand's and make\n // any future update based on upstream changes easier.\n // eslint-disable-next-line complexity\n return (message: any) => {\n if (message.type === 'DISPATCH' && message.state) {\n const ignoreState = ['JUMP_TO_ACTION', 'JUMP_TO_STATE'].includes(message.payload.type);\n if (!ignoreState) {\n // manual dispatch from the extension\n api.setState(JSON.parse(message.state));\n } else {\n // for time travel, no need to add new state changes in devtools\n savedSetState(JSON.parse(message.state));\n }\n } else if (message.type === 'DISPATCH' && message.payload?.type === 'COMMIT') {\n devtools.init(api.getState());\n } else if (message.type === 'DISPATCH' && message.payload?.type === 'IMPORT_STATE') {\n const actions = message.payload.nextLiftedState?.actionsById;\n const computedStates = message.payload.nextLiftedState?.computedStates || [];\n\n computedStates.forEach(({ state }: { state: PartialState<T> }, index: number) => {\n const action = actions[index] || `${devtools.prefix}setState`;\n if (index === 0) {\n devtools.init(state);\n } else {\n savedSetState(state);\n devtools.send(action, api.getState());\n }\n });\n }\n };\n }\n}\n", "import { EventEmitter2 as EventEmitter } from 'eventemitter2';\nimport { PEER_NOTIFICATION_TYPES, POLL_NOTIFICATION_TYPES, TRACK_NOTIFICATION_TYPES } from './common/mapping';\nimport * as sdkTypes from './sdkTypes';\nimport { IHMSStore } from '../IHMSStore';\nimport {\n HMSChangeMultiTrackStateRequest,\n HMSChangeTrackStateRequest,\n HMSDeviceChangeEvent,\n HMSException,\n HMSGenericTypes,\n HMSLeaveRoomRequest,\n HMSMessage,\n HMSNotification,\n HMSNotificationSeverity,\n HMSNotificationTypes,\n HMSPeer,\n HMSPlaylistItem,\n HMSTrack,\n HMSTrackID,\n} from '../schema';\nimport {\n HMSNotificationCallback,\n HMSNotificationInCallback,\n HMSNotificationTypeParam,\n IHMSNotifications,\n} from '../schema/notification';\nimport { selectPeerByID, selectPollByID, selectTrackByID } from '../selectors';\n\nconst HMS_NOTIFICATION_EVENT = 'hmsNotification';\n\nexport class HMSNotifications<T extends HMSGenericTypes = { sessionStore: Record<string, any> }>\n implements IHMSNotifications\n{\n private id = 0;\n private eventEmitter: EventEmitter;\n private store: IHMSStore<T>;\n\n constructor(store: IHMSStore<T>) {\n this.store = store;\n this.eventEmitter = new EventEmitter({ maxListeners: Object.keys(HMSNotificationTypes).length });\n }\n onNotification = <T extends HMSNotificationTypeParam>(cb: HMSNotificationCallback<T>, type?: T) => {\n const eventCallback = (notification: HMSNotificationInCallback<T>) => {\n if (type) {\n let matchesType: boolean;\n if (Array.isArray(type)) {\n matchesType = type.includes(notification.type as HMSNotificationTypes);\n } else {\n matchesType = type === notification.type;\n }\n if (!matchesType) {\n return;\n }\n }\n cb(notification);\n };\n this.eventEmitter.addListener(HMS_NOTIFICATION_EVENT, eventCallback);\n return () => {\n this.eventEmitter.removeListener(HMS_NOTIFICATION_EVENT, eventCallback);\n };\n };\n\n sendPlaylistTrackEnded<T>(item: HMSPlaylistItem<T>): void {\n const notification = this.createNotification(\n HMSNotificationTypes.PLAYLIST_TRACK_ENDED,\n item,\n HMSNotificationSeverity.INFO,\n );\n this.emitEvent(notification);\n }\n\n sendDeviceChange(request: HMSDeviceChangeEvent) {\n const notification = this.createNotification(\n HMSNotificationTypes.DEVICE_CHANGE_UPDATE,\n request,\n request.error ? HMSNotificationSeverity.ERROR : HMSNotificationSeverity.INFO,\n `Selected ${request.type} device - ${request.selection?.label}`,\n );\n this.emitEvent(notification);\n }\n\n sendLeaveRoom(request: HMSLeaveRoomRequest) {\n const peerName = request.requestedBy?.name;\n const notification = this.createNotification(\n request.roomEnded || !peerName ? HMSNotificationTypes.ROOM_ENDED : HMSNotificationTypes.REMOVED_FROM_ROOM,\n request,\n HMSNotificationSeverity.INFO,\n `${request.roomEnded ? `Room ended` : 'Removed from room'} ${peerName ? `by ${peerName}` : ''}`,\n );\n this.emitEvent(notification);\n }\n\n sendPeerList(peers: HMSPeer[]) {\n const notification = this.createNotification(HMSNotificationTypes.PEER_LIST, peers, HMSNotificationSeverity.INFO);\n this.emitEvent(notification);\n }\n\n sendPeerUpdate(type: sdkTypes.HMSPeerUpdate, peer: HMSPeer | null) {\n const hmsPeer = this.store.getState(selectPeerByID(peer?.id)) || peer;\n const notificationType = PEER_NOTIFICATION_TYPES[type];\n if (notificationType) {\n const notification = this.createNotification(notificationType, hmsPeer, HMSNotificationSeverity.INFO);\n this.emitEvent(notification);\n }\n }\n\n sendTrackUpdate(type: sdkTypes.HMSTrackUpdate, trackID: HMSTrackID) {\n const hmsTrack = this.store.getState(selectTrackByID(trackID));\n const notificationType = TRACK_NOTIFICATION_TYPES[type];\n if (notificationType) {\n const notification = this.createNotification(notificationType, hmsTrack, HMSNotificationSeverity.INFO);\n this.emitEvent(notification);\n }\n }\n\n sendMessageReceived(message: HMSMessage) {\n const notification = this.createNotification(\n HMSNotificationTypes.NEW_MESSAGE,\n message,\n HMSNotificationSeverity.INFO,\n );\n this.emitEvent(notification);\n }\n\n sendError(error: HMSException) {\n const notification = this.createNotification(HMSNotificationTypes.ERROR, error, HMSNotificationSeverity.ERROR);\n this.emitEvent(notification);\n }\n\n sendReconnecting(error: HMSException) {\n const notification = this.createNotification(\n HMSNotificationTypes.RECONNECTING,\n error,\n HMSNotificationSeverity.ERROR,\n );\n this.emitEvent(notification);\n }\n\n sendReconnected() {\n const notification = this.createNotification(HMSNotificationTypes.RECONNECTED, null, HMSNotificationSeverity.INFO);\n this.emitEvent(notification);\n }\n\n sendChangeTrackStateRequest(request: HMSChangeTrackStateRequest) {\n const notification = this.createNotification(\n HMSNotificationTypes.CHANGE_TRACK_STATE_REQUEST,\n request,\n HMSNotificationSeverity.INFO,\n );\n this.emitEvent(notification);\n }\n\n sendChangeMultiTrackStateRequest(request: HMSChangeMultiTrackStateRequest) {\n const notification = this.createNotification(\n HMSNotificationTypes.CHANGE_MULTI_TRACK_STATE_REQUEST,\n request,\n HMSNotificationSeverity.INFO,\n );\n this.emitEvent(notification);\n }\n\n sendPollUpdate(type: sdkTypes.HMSPollsUpdate, pollID: string) {\n const notificationType = POLL_NOTIFICATION_TYPES[type];\n const poll = this.store.getState(selectPollByID(pollID));\n\n if (notificationType) {\n const notification = this.createNotification(notificationType, poll, HMSNotificationSeverity.INFO);\n this.emitEvent(notification);\n }\n }\n\n private emitEvent(notification: HMSNotification) {\n this.eventEmitter.emit(HMS_NOTIFICATION_EVENT, notification);\n }\n\n private createNotification<T>(\n type: HMSNotificationTypes,\n data?:\n | HMSPeer\n | HMSPeer[]\n | HMSTrack\n | HMSMessage\n | HMSException\n | HMSChangeTrackStateRequest\n | HMSChangeMultiTrackStateRequest\n | HMSLeaveRoomRequest\n | HMSDeviceChangeEvent\n | HMSPlaylistItem<T>\n | sdkTypes.HMSPoll\n | null,\n severity?: HMSNotificationSeverity,\n message = '',\n ): HMSNotification {\n this.id++;\n return {\n id: this.id,\n type,\n message,\n data,\n severity,\n } as HMSNotification;\n }\n}\n", "import { HMSNotificationTypes } from '../../schema/notification';\nimport * as sdkTypes from '../sdkTypes';\n\ntype PeerNotificationMap = { [key in sdkTypes.HMSPeerUpdate]?: HMSNotificationTypes };\n\nexport const PEER_NOTIFICATION_TYPES: PeerNotificationMap = {\n [sdkTypes.HMSPeerUpdate.PEER_JOINED]: HMSNotificationTypes.PEER_JOINED,\n [sdkTypes.HMSPeerUpdate.PEER_LEFT]: HMSNotificationTypes.PEER_LEFT,\n [sdkTypes.HMSPeerUpdate.ROLE_UPDATED]: HMSNotificationTypes.ROLE_UPDATED,\n [sdkTypes.HMSPeerUpdate.NAME_UPDATED]: HMSNotificationTypes.NAME_UPDATED,\n [sdkTypes.HMSPeerUpdate.METADATA_UPDATED]: HMSNotificationTypes.METADATA_UPDATED,\n};\n\ntype TrackNotificationMap = { [key in sdkTypes.HMSTrackUpdate]: HMSNotificationTypes };\nexport const TRACK_NOTIFICATION_TYPES: TrackNotificationMap = {\n [sdkTypes.HMSTrackUpdate.TRACK_ADDED]: HMSNotificationTypes.TRACK_ADDED,\n [sdkTypes.HMSTrackUpdate.TRACK_REMOVED]: HMSNotificationTypes.TRACK_REMOVED,\n [sdkTypes.HMSTrackUpdate.TRACK_MUTED]: HMSNotificationTypes.TRACK_MUTED,\n [sdkTypes.HMSTrackUpdate.TRACK_UNMUTED]: HMSNotificationTypes.TRACK_UNMUTED,\n [sdkTypes.HMSTrackUpdate.TRACK_DEGRADED]: HMSNotificationTypes.TRACK_DEGRADED,\n [sdkTypes.HMSTrackUpdate.TRACK_RESTORED]: HMSNotificationTypes.TRACK_RESTORED,\n [sdkTypes.HMSTrackUpdate.TRACK_DESCRIPTION_CHANGED]: HMSNotificationTypes.TRACK_DESCRIPTION_CHANGED,\n};\n\ntype PollNotificationMap = { [key in sdkTypes.HMSPollsUpdate]: HMSNotificationTypes };\n\nexport const POLL_NOTIFICATION_TYPES: PollNotificationMap = {\n [sdkTypes.HMSPollsUpdate.POLL_CREATED]: HMSNotificationTypes.POLL_CREATED,\n [sdkTypes.HMSPollsUpdate.POLL_STARTED]: HMSNotificationTypes.POLL_STARTED,\n [sdkTypes.HMSPollsUpdate.POLL_STOPPED]: HMSNotificationTypes.POLL_STOPPED,\n [sdkTypes.HMSPollsUpdate.POLL_STATS_UPDATED]: HMSNotificationTypes.POLL_VOTES_UPDATED,\n};\n", "import {\n HMSAudioPlugin,\n HMSAudioTrack as SDKHMSAudioTrack,\n HMSChangeMultiTrackStateParams as SDKHMSChangeMultiTrackStateParams,\n HMSChangeMultiTrackStateRequest as SDKHMSChangeMultiTrackStateRequest,\n HMSChangeTrackStateRequest as SDKHMSChangeTrackStateRequest,\n HMSException as SDKHMSException,\n HMSLeaveRoomRequest as SDKHMSLeaveRoomRequest,\n HMSLocalAudioTrack as SDKHMSLocalAudioTrack,\n HMSLocalTrack as SDKHMSLocalTrack,\n HMSLocalVideoTrack as SDKHMSLocalVideoTrack,\n HMSLogLevel,\n HMSPluginSupportResult,\n HMSRemoteTrack as SDKHMSRemoteTrack,\n HMSRemoteVideoTrack as SDKHMSRemoteVideoTrack,\n HMSRoleChangeRequest as SDKHMSRoleChangeRequest,\n HMSScreenShareConfig,\n HMSSdk,\n HMSSimulcastLayer,\n HMSTrack as SDKHMSTrack,\n HMSVideoPlugin,\n HMSVideoTrack as SDKHMSVideoTrack,\n SessionStoreUpdate,\n} from '@100mslive/hms-video';\nimport { PEER_NOTIFICATION_TYPES, POLL_NOTIFICATION_TYPES, TRACK_NOTIFICATION_TYPES } from './common/mapping';\nimport { isRemoteTrack } from './sdkUtils/sdkUtils';\nimport {\n areArraysEqual,\n isEntityUpdated,\n mergeNewPeersInDraft,\n mergeNewPollsInDraft,\n mergeNewTracksInDraft,\n mergeTrackArrayFields,\n} from './sdkUtils/storeMergeUtils';\nimport { SDKToHMS } from './adapter';\nimport { HMSInteractivityCenter, IHMSInteractivityCenter } from './HMSInteractivityCenter';\nimport { HMSNotifications } from './HMSNotifications';\nimport { HMSPlaylist } from './HMSPlaylist';\nimport { HMSSessionStore } from './HMSSessionStore';\nimport { NamedSetState } from './internalTypes';\nimport * as sdkTypes from './sdkTypes';\nimport { HMSLogger } from '../../common/ui-logger';\nimport { BeamSpeakerLabelsLogger } from '../../controller/beam/BeamSpeakerLabelsLogger';\nimport { IHMSActions } from '../IHMSActions';\nimport { IHMSStore } from '../IHMSStore';\nimport {\n createDefaultStoreState,\n HMSChangeMultiTrackStateParams,\n HMSGenericTypes,\n HMSMediaSettings,\n HMSMessage,\n HMSMessageInput,\n HMSPeer,\n HMSPeerID,\n HMSPlaylistType,\n HMSRoleName,\n HMSRoomState,\n HMSStore,\n HMSTrack,\n HMSTrackID,\n HMSTrackSource,\n HMSVideoTrack,\n IHMSPlaylistActions,\n IHMSSessionStoreActions,\n} from '../schema';\nimport {\n HMSRoleChangeRequest,\n selectIsConnectedToRoom,\n selectIsLocalScreenShared,\n selectIsLocalVideoDisplayEnabled,\n selectIsLocalVideoEnabled,\n selectLocalAudioTrackID,\n selectLocalMediaSettings,\n selectLocalPeer,\n selectLocalTrackIDs,\n selectLocalVideoTrackID,\n selectPeerByID,\n selectPeersMap,\n selectPermissions,\n selectRolesMap,\n selectRoomState,\n selectTrackByID,\n selectTracksMap,\n selectVideoTrackByID,\n} from '../selectors';\n\n// import { ActionBatcher } from './sdkUtils/ActionBatcher';\n\n/**\n * This class implements the IHMSActions interface for 100ms SDK. It connects with SDK\n * and takes control of data management by letting every action pass through it. The\n * passed in store is ensured to be the single source of truth reflecting current\n * room related data at any point in time.\n *\n * @privateRemarks\n * Things to keep in mind while updating store -\n * 1. Treat setState as an atomic operation, if an action results in multiple changes,\n * the changes should all happen within single setState function.\n * 2. While updating the state it's very important to not update the reference if\n * something is unchanged. Copy data in same reference object don't assign new\n * object.\n * 3. Mental Model(1) - Actions from backend -> Listeners of this class -> update store -> views update themselves\n * eg. for this - peer added, remote muted etc.\n * 4. Mental Model(2) - Actions from local -> View calls actions -> update store -> views update themselves\n * eg. local track enabled, join, leave etc.\n * 5. State is immutable, a new copy with new references is created when there is a change,\n * if you try to modify state outside of setState, there'll be an error.\n */\nexport class HMSSDKActions<T extends HMSGenericTypes = { sessionStore: Record<string, any> }>\n implements IHMSActions<T>\n{\n private hmsSDKTracks: Record<string, SDKHMSTrack> = {};\n private readonly sdk: HMSSdk;\n private readonly store: IHMSStore<T>;\n private isRoomJoinCalled = false;\n private hmsNotifications: HMSNotifications<T>;\n private ignoredMessageTypes: string[] = [];\n // private actionBatcher: ActionBatcher;\n audioPlaylist!: IHMSPlaylistActions;\n videoPlaylist!: IHMSPlaylistActions;\n sessionStore: IHMSSessionStoreActions<T['sessionStore']>;\n interactivityCenter: IHMSInteractivityCenter;\n private beamSpeakerLabelsLogger?: BeamSpeakerLabelsLogger<T>;\n\n constructor(store: IHMSStore<T>, sdk: HMSSdk, notificationManager: HMSNotifications<T>) {\n this.store = store;\n this.sdk = sdk;\n this.hmsNotifications = notificationManager;\n\n this.sessionStore = new HMSSessionStore<T['sessionStore']>(this.sdk, this.setSessionStoreValueLocally.bind(this));\n this.interactivityCenter = new HMSInteractivityCenter(this.sdk);\n // this.actionBatcher = new ActionBatcher(store);\n }\n\n async refreshDevices() {\n await this.sdk.refreshDevices();\n }\n\n async unblockAudio() {\n await this.sdk.getAudioOutput().unblockAutoplay();\n }\n\n async setVolume(value: number, trackId?: HMSTrackID) {\n if (trackId) {\n await this.setTrackVolume(value, trackId);\n } else {\n await this.sdk.getAudioOutput().setVolume(value);\n this.syncRoomState('setOutputVolume');\n }\n }\n\n async setAudioOutputDevice(deviceId: string): Promise<void> {\n const deviceInfo = await this.sdk.getAudioOutput().setDevice(deviceId);\n if (deviceInfo) {\n this.setState(draftStore => {\n draftStore.settings.audioOutputDeviceId = deviceId;\n }, 'setAudioOutputDevice');\n }\n }\n\n async setPreferredLayer(trackId: string, layer: sdkTypes.HMSPreferredSimulcastLayer) {\n const track = this.hmsSDKTracks[trackId];\n if (track) {\n if (track instanceof SDKHMSRemoteVideoTrack) {\n //@ts-ignore\n if (layer === HMSSimulcastLayer.NONE) {\n HMSLogger.d(`layer ${HMSSimulcastLayer.NONE} will be ignored`);\n return;\n }\n const alreadyInSameState = this.store.getState(selectVideoTrackByID(trackId))?.preferredLayer === layer;\n if (alreadyInSameState) {\n HMSLogger.d(`preferred layer is already ${layer}`);\n return;\n }\n this.setState(draftStore => {\n const track = draftStore.tracks[trackId] as HMSVideoTrack;\n if (track) {\n track.preferredLayer = layer;\n }\n }, 'setPreferredLayer');\n await track.setPreferredLayer(layer);\n } else {\n HMSLogger.d(`track ${trackId} is not a remote video track`);\n }\n } else {\n this.logPossibleInconsistency(`track ${trackId} not present, unable to set preffer layer`);\n }\n }\n\n getAuthTokenByRoomCode(\n tokenRequest: sdkTypes.TokenRequest,\n tokenRequestOptions?: sdkTypes.TokenRequestOptions,\n ): Promise<string> {\n return this.sdk.getAuthTokenByRoomCode(tokenRequest, tokenRequestOptions);\n }\n\n async preview(config: sdkTypes.HMSPreviewConfig) {\n if (this.isRoomJoinCalled) {\n this.logPossibleInconsistency('attempting to call preview after join was called');\n return; // ignore\n }\n const roomState = this.store.getState(selectRoomState);\n if (roomState === HMSRoomState.Preview || roomState === HMSRoomState.Connecting) {\n this.logPossibleInconsistency('attempting to call preview while room is in preview/connecting');\n return;\n }\n\n try {\n this.setState(store => {\n store.room.roomState = HMSRoomState.Connecting;\n }, 'connecting');\n await this.sdkPreviewWithListeners(config);\n } catch (err) {\n HMSLogger.e('Cannot show preview. Failed to connect to room - ', err);\n throw err;\n }\n }\n\n async join(config: sdkTypes.HMSConfig) {\n if (this.isRoomJoinCalled) {\n this.logPossibleInconsistency('room join is called again');\n return; // ignore\n }\n try {\n this.isRoomJoinCalled = true;\n this.setState(store => {\n store.room.roomState = HMSRoomState.Connecting;\n }, 'join');\n await this.sdkJoinWithListeners(config);\n } catch (err) {\n this.isRoomJoinCalled = false; // so it can be called again if needed\n HMSLogger.e('Failed to connect to room - ', err);\n throw err;\n }\n }\n\n async leave() {\n const isConnectedToRoom = this.store.getState(selectIsConnectedToRoom);\n let notifyServer = true;\n if (!isConnectedToRoom) {\n notifyServer = false;\n this.logPossibleInconsistency('room leave is called when no room is connected');\n }\n const currentRoomState = this.store.getState(selectRoomState);\n this.setState(store => {\n store.room.roomState = HMSRoomState.Disconnecting;\n }, 'leaving');\n return this.sdk\n .leave(notifyServer)\n .then(() => {\n this.resetState('leave');\n if (this.beamSpeakerLabelsLogger) {\n this.beamSpeakerLabelsLogger.stop().catch(HMSLogger.e);\n }\n HMSLogger.i('left room');\n })\n .catch(err => {\n HMSLogger.e('error in leaving room - ', err);\n this.setState(store => {\n store.room.roomState = currentRoomState;\n }, 'revertLeave');\n });\n }\n\n async setScreenShareEnabled(enabled: boolean, config?: HMSScreenShareConfig) {\n // TODO: remove this, purely for backward compatibility\n if (typeof config === 'boolean') {\n config = { audioOnly: config };\n }\n try {\n if (enabled) {\n await this.startScreenShare(config);\n } else {\n await this.stopScreenShare();\n }\n } catch (error) {\n this.hmsNotifications.sendError(SDKToHMS.convertException(error as SDKHMSException));\n throw error;\n }\n }\n\n async addTrack(track: MediaStreamTrack, type: HMSTrackSource = 'regular') {\n await this.sdk.addTrack(track, type);\n this.syncRoomState('addTrack');\n }\n\n async removeTrack(trackId: string) {\n await this.sdk.removeTrack(trackId);\n this.syncRoomState('removeTrack');\n }\n\n async setLocalAudioEnabled(enabled: boolean) {\n const trackID = this.store.getState(selectLocalAudioTrackID);\n if (trackID) {\n await this.setEnabledTrack(trackID, enabled);\n }\n }\n\n async setLocalVideoEnabled(enabled: boolean) {\n const trackID = this.store.getState(selectLocalVideoTrackID);\n if (trackID) {\n await this.setEnabledTrack(trackID, enabled);\n }\n }\n\n async setEnabledTrack(trackID: string, enabled: boolean) {\n // if mute/unmute is clicked multiple times for same operation, ignore repeated ones\n const alreadyInSameState = this.store.getState().tracks[trackID]?.enabled === enabled;\n if (alreadyInSameState) {\n // it could also be a case of possible inconsistency where UI state is out of sync with truth\n this.logPossibleInconsistency(`local track[${trackID}] enabled state - ${enabled}`);\n return;\n }\n this.setState(store => {\n // show on UI immediately\n if (!store.tracks[trackID]) {\n this.logPossibleInconsistency('track id not found for setEnabled');\n } else {\n store.tracks[trackID].displayEnabled = enabled;\n }\n }, 'displayEnabled');\n try {\n await this.setEnabledSDKTrack(trackID, enabled); // do the operation\n this.syncRoomState('setEnabled');\n } catch (err) {\n // rollback on failure\n this.setState(store => {\n store.tracks[trackID].displayEnabled = !enabled;\n }, 'rollbackDisplayEnabled');\n this.hmsNotifications.sendError(SDKToHMS.convertException(err as SDKHMSException));\n throw err;\n }\n const type = enabled ? sdkTypes.HMSTrackUpdate.TRACK_UNMUTED : sdkTypes.HMSTrackUpdate.TRACK_MUTED;\n this.hmsNotifications.sendTrackUpdate(type, trackID);\n }\n\n async setAudioSettings(settings: Partial<sdkTypes.HMSAudioTrackSettings>) {\n const trackID = this.store.getState(selectLocalAudioTrackID);\n if (trackID) {\n await this.setSDKLocalAudioTrackSettings(trackID, settings);\n this.syncRoomState('setAudioSettings');\n }\n }\n\n async setVideoSettings(settings: Partial<sdkTypes.HMSVideoTrackSettings>) {\n const trackID = this.store.getState(selectLocalVideoTrackID);\n if (trackID) {\n await this.setSDKLocalVideoTrackSettings(trackID, settings);\n this.syncRoomState('setVideoSettings');\n }\n }\n\n async switchCamera(): Promise<void> {\n const trackID = this.store.getState(selectLocalVideoTrackID);\n if (trackID) {\n const sdkTrack = this.hmsSDKTracks[trackID] as SDKHMSLocalVideoTrack;\n if (sdkTrack) {\n await sdkTrack.switchCamera();\n this.syncRoomState('switchCamera');\n }\n }\n }\n\n sendMessage(message: string) {\n this.sendBroadcastMessage(message);\n }\n\n async sendBroadcastMessage(message: string, type?: string) {\n const sdkMessage = await this.sdk.sendBroadcastMessage(message, type);\n this.updateMessageInStore(sdkMessage, { message, type });\n }\n\n async sendGroupMessage(message: string, roles: string[], type?: string) {\n const storeRoles = this.store.getState(selectRolesMap);\n const hmsRoles = roles.map(roleName => {\n return storeRoles[roleName];\n });\n const sdkMessage = await this.sdk.sendGroupMessage(message, hmsRoles, type);\n this.updateMessageInStore(sdkMessage, { message, recipientRoles: roles, type });\n }\n\n async sendDirectMessage(message: string, peerID: string, type?: string) {\n const hmsPeer = this.getSDKHMSPeer(peerID);\n if (!hmsPeer) {\n HMSLogger.w('sendMessage', 'Failed to send message');\n throw Error(`sendMessage Failed - peer ${peerID} not found`);\n }\n const sdkMessage = await this.sdk.sendDirectMessage(message, hmsPeer, type);\n this.updateMessageInStore(sdkMessage, { message, recipientPeer: hmsPeer.peerId, type });\n }\n\n private updateMessageInStore(sdkMessage: sdkTypes.HMSMessage | void, messageInput: string | HMSMessageInput) {\n if (!sdkMessage) {\n HMSLogger.w('sendMessage', 'Failed to send message', messageInput);\n throw Error(`sendMessage Failed - ${JSON.stringify(messageInput)}`);\n }\n const hmsMessage = SDKToHMS.convertMessage(sdkMessage) as HMSMessage;\n hmsMessage.read = true;\n hmsMessage.senderName = 'You';\n hmsMessage.ignored = this.ignoredMessageTypes.includes(hmsMessage.type);\n this.putMessageInStore(hmsMessage);\n return hmsMessage;\n }\n\n setMessageRead(readStatus: boolean, messageId?: string) {\n this.setState(store => {\n if (messageId) {\n if (!store.messages.byID[messageId]) {\n this.logPossibleInconsistency('no message with id is found');\n } else {\n store.messages.byID[messageId].read = readStatus;\n }\n } else {\n store.messages.allIDs.forEach((id: string) => {\n store.messages.byID[id].read = readStatus;\n });\n }\n }, 'setMessageRead');\n }\n\n async attachVideo(trackID: string, videoElement: HTMLVideoElement) {\n if (this.localAndVideoUnmuting(trackID)) {\n // wait till video unmute has finished\n return new Promise<void>(resolve => {\n const unsub = this.store.subscribe(async enabled => {\n if (enabled) {\n await this.attachVideoInternal(trackID, videoElement);\n unsub();\n resolve();\n }\n }, selectIsLocalVideoEnabled);\n });\n } else {\n await this.attachVideoInternal(trackID, videoElement);\n }\n }\n\n async detachVideo(trackID: string, videoElement: HTMLVideoElement) {\n const sdkTrack = this.hmsSDKTracks[trackID];\n if (sdkTrack?.type === 'video') {\n await this.sdk.detachVideo(sdkTrack as SDKHMSVideoTrack, videoElement);\n } else {\n if (videoElement) {\n videoElement.srcObject = null; // so chrome can clean up\n }\n HMSLogger.d('possible inconsistency detected - no video track found to remove sink');\n }\n }\n\n async addPluginToVideoTrack(plugin: HMSVideoPlugin, pluginFrameRate?: number): Promise<void> {\n return this.addRemoveVideoPlugin(plugin, 'add', pluginFrameRate);\n }\n async addPluginToAudioTrack(plugin: HMSAudioPlugin): Promise<void> {\n return this.addRemoveAudioPlugin(plugin, 'add');\n }\n\n validateVideoPluginSupport(plugin: HMSVideoPlugin): HMSPluginSupportResult {\n let result = {} as HMSPluginSupportResult;\n result.isSupported = false; //Setting default to false\n if (!plugin) {\n HMSLogger.w('no plugin passed in for checking support');\n result.errMsg = 'no plugin passed in for checking support';\n return result;\n }\n const trackID = this.store.getState(selectLocalVideoTrackID);\n if (!trackID) {\n HMSLogger.w('video Track not added to local peer yet');\n result.errMsg = 'call this function only after local peer has video track';\n return result;\n }\n const sdkTrack = this.hmsSDKTracks[trackID];\n if (sdkTrack) {\n result = (sdkTrack as SDKHMSLocalVideoTrack).validatePlugin(plugin);\n } else {\n HMSLogger.w(`track ${trackID} not present, unable to validate plugin`);\n result.errMsg = `track ${trackID} not present, unable to validate plugin`;\n }\n\n return result;\n }\n\n validateAudioPluginSupport(plugin: HMSAudioPlugin): HMSPluginSupportResult {\n let result = {} as HMSPluginSupportResult;\n result.isSupported = false; //Setting default to false\n if (!plugin) {\n HMSLogger.w('no plugin passed in for checking support\"');\n result.errMsg = 'no plugin passed in for checking support\"';\n return result;\n }\n const trackID = this.store.getState(selectLocalAudioTrackID);\n if (!trackID) {\n HMSLogger.w('audio track not added to local peer yet');\n result.errMsg = 'call this function only after local peer has audio track';\n return result;\n }\n const sdkTrack = this.hmsSDKTracks[trackID];\n if (sdkTrack) {\n result = (sdkTrack as SDKHMSLocalAudioTrack).validatePlugin(plugin);\n } else {\n HMSLogger.w(`track ${trackID} not present, unable to validate plugin`);\n result.errMsg = `track ${trackID} not present, unable to validate plugin`;\n }\n\n return result;\n }\n\n async removePluginFromVideoTrack(plugin: HMSVideoPlugin): Promise<void> {\n return this.addRemoveVideoPlugin(plugin, 'remove');\n }\n async removePluginFromAudioTrack(plugin: HMSAudioPlugin): Promise<void> {\n return this.addRemoveAudioPlugin(plugin, 'remove');\n }\n\n async changeRole(forPeerId: string, toRole: string, force = false) {\n const peer = this.getSDKHMSPeer(forPeerId);\n if (!peer) {\n this.logPossibleInconsistency(`Unknown peer ID given ${forPeerId} for changerole`);\n return;\n }\n\n await this.sdk.changeRoleOfPeer(peer, toRole, force);\n }\n\n async changeRoleOfPeer(forPeerId: string, toRole: string, force = false) {\n const peer = this.getSDKHMSPeer(forPeerId);\n if (!peer) {\n this.logPossibleInconsistency(`Unknown peer ID given ${forPeerId} for changerole`);\n return;\n }\n\n await this.sdk.changeRoleOfPeer(peer, toRole, force);\n }\n\n async changeRoleOfPeersWithRoles(roles: HMSRoleName[], toRole: HMSRoleName) {\n const rolesToBeChanged = this.sdk.getRoles().filter(role => roles.includes(role.name));\n await this.sdk.changeRoleOfPeersWithRoles(rolesToBeChanged, toRole);\n }\n\n // TODO: separate out role related things in another file\n async acceptChangeRole(request: HMSRoleChangeRequest) {\n const sdkPeer: sdkTypes.HMSPeer | undefined = request.requestedBy\n ? this.getSDKHMSPeer(request.requestedBy.id)\n : undefined;\n if (!sdkPeer) {\n HMSLogger.w(`peer for which role change is requested no longer available - ${request.requestedBy}`);\n }\n const sdkRequest = {\n requestedBy: sdkPeer,\n role: request.role,\n token: request.token,\n };\n // TODO: hotfix for HMS-3639. Needs a better solution\n await this.sdk.acceptChangeRole(sdkRequest);\n this.removeRoleChangeRequest(request);\n }\n\n initAppData(appData: Record<string, any>) {\n this.setState(store => {\n store.appData = appData;\n }, 'initAppData');\n }\n\n setAppData(key: string, value: any, merge?: boolean) {\n const isValueObject = value?.constructor.name === 'Object';\n this.setState(store => {\n if (store.appData) {\n if (merge && isValueObject) {\n Object.assign(store.appData[key], value);\n } else {\n store.appData[key] = value;\n }\n } else {\n const newAppData = {\n [key]: value,\n };\n store.appData = newAppData;\n }\n }, `setAppData-${key}`);\n }\n\n /**\n * @privateRemarks\n * there is no corresponding sdk method for rejecting change role but as the store also maintains the full\n * state of current pending requests, this method allows it to clean up when the request is rejected\n */\n rejectChangeRole(request: HMSRoleChangeRequest) {\n this.removeRoleChangeRequest(request);\n }\n\n async endRoom(lock: boolean, reason: string) {\n const permissions = this.store.getState(selectPermissions);\n if (!permissions?.endRoom) {\n HMSLogger.w('You are not allowed to perform this action - endRoom');\n return;\n }\n const currentRoomState = this.store.getState(selectRoomState);\n this.setState(store => {\n store.room.roomState = HMSRoomState.Disconnecting;\n }, 'endingRoom');\n try {\n await this.sdk.endRoom(lock, reason);\n this.resetState('endRoom');\n } catch (err) {\n HMSLogger.e('error in ending room - ', err);\n this.setState(store => {\n store.room.roomState = currentRoomState;\n }, 'revertEndRoom');\n }\n }\n\n async removePeer(peerID: string, reason: string) {\n const peer = this.getSDKHMSPeer(peerID);\n if (peer && !peer.isLocal) {\n await this.sdk.removePeer(peer as sdkTypes.HMSRemotePeer, reason);\n } else {\n this.logPossibleInconsistency(`No remote peer found for peerID - ${peerID}`);\n return;\n }\n }\n\n async startRTMPOrRecording(params: sdkTypes.RTMPRecordingConfig) {\n await this.sdk.startRTMPOrRecording(params);\n }\n\n async stopRTMPAndRecording() {\n await this.sdk.stopRTMPAndRecording();\n }\n\n async startHLSStreaming(params?: sdkTypes.HLSConfig) {\n await this.sdk.startHLSStreaming(params);\n }\n\n async stopHLSStreaming(params?: sdkTypes.HLSConfig): Promise<void> {\n await this.sdk.stopHLSStreaming(params);\n }\n\n async sendHLSTimedMetadata(metadataList: sdkTypes.HLSTimedMetadata[]): Promise<void> {\n await this.sdk.sendHLSTimedMetadata(metadataList);\n }\n async changeName(name: string) {\n await this.sdk.changeName(name);\n }\n\n async changeMetadata(metadata: string | any) {\n if (typeof metadata !== 'string') {\n metadata = JSON.stringify(metadata);\n }\n await this.sdk.changeMetadata(metadata);\n }\n\n async setSessionMetadata(metadata: any) {\n await this.sdk.setSessionMetadata(metadata);\n this.setState(draftStore => {\n draftStore.sessionMetadata = metadata;\n }, 'setSessionMetadata');\n this.setSessionStoreValueLocally({ key: 'default', value: metadata }, 'setSessionMetadata');\n }\n\n async populateSessionMetadata(): Promise<void> {\n const metadata = await this.sdk.getSessionMetadata();\n this.setState(draftStore => {\n draftStore.sessionMetadata = metadata;\n }, 'populateSessionMetadata');\n this.setSessionStoreValueLocally({ key: 'default', value: metadata }, 'populateSessionmetadata');\n }\n\n async setRemoteTrackEnabled(trackID: HMSTrackID | HMSTrackID[], enabled: boolean) {\n if (typeof trackID === 'string') {\n const track = this.hmsSDKTracks[trackID];\n if (track && isRemoteTrack(track)) {\n await this.sdk.changeTrackState(track as SDKHMSRemoteTrack, enabled);\n } else {\n this.logPossibleInconsistency(`No remote track with ID ${trackID} found for change track state`);\n }\n } else if (Array.isArray(trackID)) {\n trackID.forEach(id => this.setRemoteTrackEnabled(id, enabled));\n }\n }\n\n async setRemoteTracksEnabled(params: HMSChangeMultiTrackStateParams) {\n const sdkRequest: SDKHMSChangeMultiTrackStateParams = {\n enabled: params.enabled,\n type: params.type,\n source: params.source,\n };\n if (params.roles) {\n const rolesMap = this.store.getState(selectRolesMap);\n sdkRequest.roles = params.roles.map(role => rolesMap[role]);\n }\n await this.sdk.changeMultiTrackState(sdkRequest);\n }\n\n setLogLevel(level: HMSLogLevel) {\n HMSLogger.level = level;\n this.sdk.setLogLevel(level);\n }\n\n setFrameworkInfo(frameworkInfo: sdkTypes.HMSFrameworkInfo) {\n this.sdk.setFrameworkInfo(frameworkInfo);\n }\n\n ignoreMessageTypes(msgTypes: string[], replace = false) {\n if (replace) {\n this.ignoredMessageTypes = msgTypes;\n } else {\n for (const msgType of msgTypes) {\n if (!this.ignoredMessageTypes.includes(msgType)) {\n this.ignoredMessageTypes.push(msgType);\n }\n }\n }\n }\n\n async enableBeamSpeakerLabelsLogging() {\n if (!this.beamSpeakerLabelsLogger) {\n HMSLogger.i('enabling beam speaker labels logging');\n this.beamSpeakerLabelsLogger = new BeamSpeakerLabelsLogger(this.store, this);\n await this.beamSpeakerLabelsLogger.start();\n }\n }\n\n private resetState(reason = 'resetState') {\n this.isRoomJoinCalled = false;\n this.hmsSDKTracks = {};\n HMSLogger.cleanup();\n this.setState(store => {\n Object.assign(store, createDefaultStoreState());\n }, reason);\n }\n\n private async sdkJoinWithListeners(config: sdkTypes.HMSConfig) {\n await this.sdk.join(config, {\n onJoin: this.onJoin.bind(this),\n onRoomUpdate: this.onRoomUpdate.bind(this),\n onPeerUpdate: this.onPeerUpdate.bind(this),\n onTrackUpdate: this.onTrackUpdate.bind(this),\n onMessageReceived: this.onMessageReceived.bind(this),\n onError: this.onError.bind(this),\n onReconnected: this.onReconnected.bind(this),\n onReconnecting: this.onReconnecting.bind(this),\n onRoleChangeRequest: this.onRoleChangeRequest.bind(this),\n onRoleUpdate: this.onRoleUpdate.bind(this),\n onDeviceChange: this.onDeviceChange.bind(this),\n onChangeTrackStateRequest: this.onChangeTrackStateRequest.bind(this),\n onChangeMultiTrackStateRequest: this.onChangeMultiTrackStateRequest.bind(this),\n onRemovedFromRoom: this.onRemovedFromRoom.bind(this),\n onNetworkQuality: this.onNetworkQuality.bind(this),\n onSessionStoreUpdate: this.onSessionStoreUpdate.bind(this),\n onPollsUpdate: this.onPollsUpdate.bind(this),\n });\n this.sdk.addAudioListener({\n onAudioLevelUpdate: this.onAudioLevelUpdate.bind(this),\n });\n this.sdk.addConnectionQualityListener({\n onConnectionQualityUpdate: this.onConnectionQualityUpdate.bind(this),\n });\n }\n\n private onRemovedFromRoom(request: SDKHMSLeaveRoomRequest) {\n const requestedBy = this.store.getState(selectPeerByID(request.requestedBy?.peerId));\n this.hmsNotifications.sendLeaveRoom({\n ...request,\n requestedBy: requestedBy || undefined,\n });\n const action = request.roomEnded || !requestedBy ? 'roomEnded' : 'removedFromRoom';\n HMSLogger.i(`resetting state after peer removed ${action}`, request);\n this.resetState(action);\n }\n\n private onDeviceChange(event: sdkTypes.HMSDeviceChangeEvent) {\n const devices = event.devices;\n if (!devices) {\n return;\n }\n const localPeer = this.store.getState(selectLocalPeer);\n this.setState(store => {\n if (!areArraysEqual(store.devices.audioInput, devices.audioInput)) {\n store.devices.audioInput = devices.audioInput;\n }\n if (!areArraysEqual(store.devices.videoInput, devices.videoInput)) {\n store.devices.videoInput = devices.videoInput;\n }\n if (!areArraysEqual(store.devices.audioOutput, devices.audioOutput)) {\n store.devices.audioOutput = devices.audioOutput;\n }\n const sdkLocalPeer = this.sdk.getLocalPeer();\n if (localPeer?.id && sdkLocalPeer) {\n Object.assign(store.settings, this.getMediaSettings(sdkLocalPeer));\n }\n }, 'deviceChange');\n // send notification only on device change - selection is present\n if (event.selection) {\n const notification = SDKToHMS.convertDeviceChangeUpdate(event);\n this.hmsNotifications.sendDeviceChange(notification);\n }\n }\n\n private async sdkPreviewWithListeners(config: sdkTypes.HMSPreviewConfig) {\n await this.sdk.preview(config, {\n onPreview: this.onPreview.bind(this),\n onError: this.onError.bind(this),\n onReconnected: this.onReconnected.bind(this),\n onReconnecting: this.onReconnecting.bind(this),\n onDeviceChange: this.onDeviceChange.bind(this),\n onRoomUpdate: this.onRoomUpdate.bind(this),\n onPeerUpdate: this.onPeerUpdate.bind(this),\n onNetworkQuality: this.onNetworkQuality.bind(this),\n });\n this.sdk.addAudioListener({\n onAudioLevelUpdate: this.onAudioLevelUpdate.bind(this),\n });\n }\n\n private onNetworkQuality(quality: number) {\n this.setState(store => {\n /*\n * if store does not have peerId yet, fetch from sdk directly.\n * sdk will have the localpeer already set.\n */\n const peerId = store.room.localPeer || this.sdk.getLocalPeer()?.peerId;\n if (peerId) {\n store.connectionQualities[peerId] = { peerID: peerId, downlinkQuality: quality };\n }\n }, 'ConnectionQuality');\n }\n\n private onSessionStoreUpdate(updates: SessionStoreUpdate[]) {\n this.setSessionStoreValueLocally(updates, 'sessionStoreUpdate');\n }\n\n private onPollsUpdate(actionType: sdkTypes.HMSPollsUpdate, polls: sdkTypes.HMSPoll[]) {\n const actionName = POLL_NOTIFICATION_TYPES[actionType];\n this.setState(draftStore => {\n const pollsObject = polls.reduce((acc, poll) => {\n acc[poll.id] = {\n ...poll,\n questions: poll.questions?.map(question => ({\n ...question,\n answer: question.answer ? { ...question.answer } : undefined,\n options: question.options?.map(option => ({ ...option })),\n responses: question.responses?.map(response => ({ ...response })),\n })),\n };\n return acc;\n }, {} as { [key: string]: sdkTypes.HMSPoll });\n mergeNewPollsInDraft(draftStore.polls, pollsObject);\n }, actionName);\n\n polls.forEach(poll => this.hmsNotifications.sendPollUpdate(actionType, poll.id));\n }\n\n private async startScreenShare(config?: HMSScreenShareConfig) {\n const isScreenShared = this.store.getState(selectIsLocalScreenShared);\n if (!isScreenShared) {\n await this.sdk.startScreenShare(() => this.syncRoomState('screenshareStopped'), config);\n this.syncRoomState('startScreenShare');\n } else {\n this.logPossibleInconsistency(\"start screenshare is called while it's on\");\n }\n }\n\n private async stopScreenShare() {\n const isScreenShared = this.store.getState(selectIsLocalScreenShared);\n if (isScreenShared) {\n await this.sdk.stopScreenShare();\n this.syncRoomState('stopScreenShare');\n } else {\n this.logPossibleInconsistency(\"stop screenshare is called while it's not on\");\n }\n }\n\n private async attachVideoInternal(trackID: string, videoElement: HTMLVideoElement) {\n const sdkTrack = this.hmsSDKTracks[trackID];\n if (sdkTrack && sdkTrack.type === 'video') {\n await this.sdk.attachVideo(sdkTrack as SDKHMSVideoTrack, videoElement);\n } else {\n this.logPossibleInconsistency('no video track found to add sink');\n }\n }\n\n /**\n * This is a very important function as it's responsible for maintaining the source of\n * truth with maximum efficiency. The efficiency comes from the fact that the only\n * those portions of the store are updated which have actually changed.\n * While making a change in this function don't use functions like map, reduce etc.\n * which return a new copy of the data. Use Object.assign etc. to ensure that if the data\n * doesn't change reference is also not changed.\n * The UI and selectors rely on the fact that the store is immutable that is if there is\n * any change and only if there is a change, they'll get a new copy of the data they're\n * interested in with a new reference.\n * @protected\n */\n protected syncRoomState(action: string) {\n action = `${action}_fullSync`;\n HMSLogger.time(`store-sync-${action}`);\n const newHmsPeers: Record<HMSPeerID, Partial<HMSPeer>> = {};\n const newHmsPeerIDs: HMSPeerID[] = []; // to add in room.peers\n const newHmsTracks: Record<HMSTrackID, Partial<HMSTrack>> = {};\n const newHmsSDkTracks: Record<HMSTrackID, SDKHMSTrack> = {};\n const newMediaSettings: Partial<HMSMediaSettings> = {};\n let newPreview: HMSStore['preview'];\n\n const sdkPeers: sdkTypes.HMSPeer[] = this.sdk.getPeers();\n\n // first convert everything in the new format\n for (const sdkPeer of sdkPeers) {\n const hmsPeer = SDKToHMS.convertPeer(sdkPeer);\n newHmsPeers[hmsPeer.id] = hmsPeer;\n newHmsPeerIDs.push(hmsPeer.id);\n\n const sdkTracks = [sdkPeer.audioTrack, sdkPeer.videoTrack, ...sdkPeer.auxiliaryTracks];\n for (const sdkTrack of sdkTracks) {\n if (!sdkTrack) {\n continue;\n }\n const hmsTrack = SDKToHMS.convertTrack(sdkTrack);\n newHmsTracks[hmsTrack.id] = hmsTrack;\n newHmsSDkTracks[sdkTrack.trackId] = sdkTrack;\n }\n\n if (sdkPeer.isLocal) {\n const localPeer = sdkPeer as sdkTypes.HMSLocalPeer;\n newPreview = this.getPreviewFields(localPeer);\n Object.assign(newMediaSettings, this.getMediaSettings(localPeer));\n }\n }\n\n const recording = this.sdk.getRecordingState();\n const rtmp = this.sdk.getRTMPState();\n const hls = this.sdk.getHLSState();\n\n // then merge them carefully with our store so if something hasn't changed\n // the reference shouldn't change. Note that the draftStore is an immer draft\n // object.\n this.setState(draftStore => {\n draftStore.room.peers = newHmsPeerIDs;\n const draftPeers = draftStore.peers;\n const draftTracks = draftStore.tracks;\n // the order of below statements are important as merge functions are mutating\n mergeNewPeersInDraft(draftPeers, newHmsPeers);\n mergeNewTracksInDraft(draftTracks, newHmsTracks);\n Object.assign(draftStore.settings, newMediaSettings);\n this.hmsSDKTracks = newHmsSDkTracks;\n\n /**\n * if preview is already present merge,\n * else set as is(which will create/delete)\n */\n if (draftStore.preview?.localPeer && newPreview?.localPeer) {\n Object.assign(draftStore.preview, newPreview);\n } else {\n draftStore.preview = newPreview;\n }\n Object.assign(draftStore.roles, SDKToHMS.convertRoles(this.sdk.getRoles()));\n Object.assign(draftStore.playlist, SDKToHMS.convertPlaylist(this.sdk.getPlaylistManager()));\n Object.assign(draftStore.room, SDKToHMS.convertRecordingStreamingState(recording, rtmp, hls));\n Object.assign(draftStore.templateAppData, this.sdk.getTemplateAppData());\n }, action);\n HMSLogger.timeEnd(`store-sync-${action}`);\n }\n\n protected onPreview(sdkRoom: sdkTypes.HMSRoom) {\n this.setState(store => {\n Object.assign(store.room, SDKToHMS.convertRoom(sdkRoom, this.sdk.getLocalPeer()?.peerId));\n store.room.roomState = HMSRoomState.Preview;\n }, 'previewStart');\n this.syncRoomState('previewSync');\n }\n\n protected onJoin(sdkRoom: sdkTypes.HMSRoom) {\n const playlistManager = this.sdk.getPlaylistManager();\n this.audioPlaylist = new HMSPlaylist(\n playlistManager,\n HMSPlaylistType.audio,\n this.syncPlaylistState.bind(this),\n this.store,\n );\n this.videoPlaylist = new HMSPlaylist(\n playlistManager,\n HMSPlaylistType.video,\n this.syncRoomState.bind(this),\n this.store,\n );\n this.syncRoomState('joinSync');\n this.setState(store => {\n Object.assign(store.room, SDKToHMS.convertRoom(sdkRoom, this.sdk.getLocalPeer()?.peerId));\n store.room.isConnected = true;\n store.room.roomState = HMSRoomState.Connected;\n }, 'joined');\n playlistManager.onProgress(this.setProgress);\n playlistManager.onNewTrackStart((item: sdkTypes.HMSPlaylistItem<any>) => {\n this.syncPlaylistState(`${item.type}PlaylistUpdate`);\n });\n playlistManager.onPlaylistEnded((type: HMSPlaylistType) => {\n this.syncPlaylistState(`${type}PlaylistEnded`);\n });\n playlistManager.onCurrentTrackEnded((item: sdkTypes.HMSPlaylistItem<any>) => {\n this.hmsNotifications.sendPlaylistTrackEnded(SDKToHMS.convertPlaylistItem(playlistManager, item));\n this.syncPlaylistState(`${item.type}PlaylistItemEnded`);\n });\n }\n\n protected onRoomUpdate(type: sdkTypes.HMSRoomUpdate, room: sdkTypes.HMSRoom) {\n this.setState(store => {\n Object.assign(store.room, SDKToHMS.convertRoom(room, this.sdk.getLocalPeer()?.peerId));\n }, type);\n }\n\n protected onPeerUpdate(type: sdkTypes.HMSPeerUpdate, sdkPeer: sdkTypes.HMSPeer | sdkTypes.HMSPeer[]) {\n if (\n [sdkTypes.HMSPeerUpdate.BECAME_DOMINANT_SPEAKER, sdkTypes.HMSPeerUpdate.RESIGNED_DOMINANT_SPEAKER].includes(type)\n ) {\n return; // ignore, high frequency update so no point of syncing peers\n }\n if (Array.isArray(sdkPeer)) {\n const storePeers = this.store.getState(selectPeersMap);\n const newPeerIds = sdkPeer.filter(peer => !storePeers[peer.peerId]);\n this.syncRoomState('peersJoined');\n const connected = this.store.getState(selectIsConnectedToRoom);\n // This is not send unnecessary notifications while in preview\n // now room state also call peer list to handle large peers\n if (connected) {\n const hmsPeers = [];\n for (const peer of sdkPeer) {\n const hmsPeer = this.store.getState(selectPeerByID(peer.peerId));\n if (hmsPeer) {\n hmsPeers.push(hmsPeer);\n }\n }\n this.hmsNotifications.sendPeerList(hmsPeers);\n } else {\n newPeerIds.forEach(peer => {\n const hmsPeer = this.store.getState(selectPeerByID(peer.peerId));\n if (hmsPeer) {\n this.hmsNotifications.sendPeerUpdate(sdkTypes.HMSPeerUpdate.PEER_JOINED, hmsPeer);\n }\n });\n }\n return;\n }\n this.sendPeerUpdateNotification(type, sdkPeer);\n }\n\n protected onTrackUpdate(type: sdkTypes.HMSTrackUpdate, track: SDKHMSTrack, peer: sdkTypes.HMSPeer) {\n // this check is needed because for track removed case, the notification needs to\n // be send before the track is removed from store\n if (type === sdkTypes.HMSTrackUpdate.TRACK_REMOVED) {\n this.hmsNotifications.sendTrackUpdate(type, track.trackId);\n this.handleTrackRemove(track, peer);\n } else if ([sdkTypes.HMSTrackUpdate.TRACK_ADDED, sdkTypes.HMSTrackUpdate.TRACK_REMOVED].includes(type)) {\n const actionName = TRACK_NOTIFICATION_TYPES[type];\n this.syncRoomState(actionName);\n this.hmsNotifications.sendTrackUpdate(type, track.trackId);\n } else {\n const actionName = TRACK_NOTIFICATION_TYPES[type] || 'trackUpdate';\n const hmsTrack = SDKToHMS.convertTrack(track);\n this.setState(draftStore => {\n const storeTrack = draftStore.tracks[hmsTrack.id];\n if (isEntityUpdated(storeTrack, hmsTrack)) {\n mergeTrackArrayFields(storeTrack, hmsTrack);\n Object.assign(storeTrack, hmsTrack);\n }\n }, actionName);\n this.hmsNotifications.sendTrackUpdate(type, track.trackId);\n }\n }\n\n protected onMessageReceived(sdkMessage: sdkTypes.HMSMessage) {\n const hmsMessage = SDKToHMS.convertMessage(sdkMessage) as HMSMessage;\n hmsMessage.read = false;\n hmsMessage.ignored = this.ignoredMessageTypes.includes(hmsMessage.type);\n this.putMessageInStore(hmsMessage);\n this.hmsNotifications.sendMessageReceived(hmsMessage);\n }\n\n protected putMessageInStore(hmsMessage: HMSMessage) {\n if (hmsMessage.ignored) {\n return;\n }\n this.setState(store => {\n store.messages.byID[hmsMessage.id] = hmsMessage;\n store.messages.allIDs.push(hmsMessage.id);\n }, 'newMessage');\n }\n\n /*\n * Note: speakers array contain the value only for peers who have audioLevel != 0\n */\n protected onAudioLevelUpdate(sdkSpeakers: sdkTypes.HMSSpeaker[]) {\n this.setState(store => {\n const trackIDAudioLevelMap: Record<HMSPeerID, number> = {};\n sdkSpeakers.forEach(sdkSpeaker => {\n if (!sdkSpeaker.track || !sdkSpeaker.peer) {\n return;\n }\n const trackID = sdkSpeaker.track.trackId;\n trackIDAudioLevelMap[trackID] = sdkSpeaker.audioLevel;\n if (!store.speakers[trackID]) {\n // Set store instances(peers, tracks) references in speaker, not the new ones received.\n store.speakers[trackID] = {\n audioLevel: sdkSpeaker.audioLevel,\n peerID: sdkSpeaker.peer.peerId,\n trackID: trackID,\n };\n }\n });\n const speakerEntries = Object.entries(store.speakers);\n for (const [trackID, speaker] of speakerEntries) {\n speaker.audioLevel = trackIDAudioLevelMap[trackID] || 0;\n if (speaker.audioLevel === 0) {\n delete store.speakers[trackID];\n }\n }\n }, 'audioLevel');\n }\n\n /**\n * The connection quality update is sent for all peers(one needs to know of) every time.\n */\n protected onConnectionQualityUpdate(newQualities: sdkTypes.HMSConnectionQuality[]) {\n this.setState(store => {\n const currentPeerIDs = new Set();\n newQualities.forEach(sdkUpdate => {\n const peerID = sdkUpdate.peerID;\n if (!peerID) {\n return;\n }\n currentPeerIDs.add(peerID);\n if (!store.connectionQualities[peerID]) {\n store.connectionQualities[peerID] = sdkUpdate;\n } else {\n Object.assign(store.connectionQualities[peerID], sdkUpdate);\n }\n });\n const peerIDsStored = Object.keys(store.connectionQualities);\n for (const storedPeerID of peerIDsStored) {\n if (!currentPeerIDs.has(storedPeerID)) {\n // peer is likely no longer there, it wasn't in the update sent by the server\n delete store.connectionQualities[storedPeerID];\n }\n }\n }, 'connectionQuality');\n }\n\n protected onChangeTrackStateRequest(request: SDKHMSChangeTrackStateRequest) {\n const requestedBy = this.store.getState(selectPeerByID(request.requestedBy?.peerId));\n const storeTrackID = this.getStoreLocalTrackIDfromSDKTrack(request.track);\n const track = this.store.getState(selectTrackByID(storeTrackID));\n\n if (!track) {\n return this.logPossibleInconsistency(\n `Not found track for which track state change was requested, ${request.track}`,\n );\n }\n\n if (!request.enabled) {\n this.syncRoomState('changeTrackStateRequest');\n }\n\n this.hmsNotifications.sendChangeTrackStateRequest({\n requestedBy: requestedBy || undefined,\n track,\n enabled: request.enabled,\n });\n }\n\n protected onChangeMultiTrackStateRequest(request: SDKHMSChangeMultiTrackStateRequest) {\n const requestedBy = this.store.getState(selectPeerByID(request.requestedBy?.peerId));\n\n if (!request.enabled) {\n this.syncRoomState('changeMultiTrackStateRequest');\n }\n\n const tracks: HMSTrack[] = [];\n const tracksMap = this.store.getState(selectTracksMap);\n for (const track of request.tracks) {\n const storeTrackID = this.getStoreLocalTrackIDfromSDKTrack(track);\n if (storeTrackID && tracksMap[storeTrackID]) {\n tracks.push(tracksMap[storeTrackID]);\n }\n }\n\n this.hmsNotifications.sendChangeMultiTrackStateRequest({\n requestedBy: requestedBy || undefined,\n tracks,\n enabled: request.enabled,\n type: request.type,\n source: request.source,\n });\n }\n\n protected onReconnected() {\n this.syncRoomState('reconnectedSync');\n this.hmsNotifications.sendReconnected();\n this.setState(store => {\n store.room.roomState = store.room.isConnected ? HMSRoomState.Connected : HMSRoomState.Preview;\n }, 'reconnected');\n }\n\n protected onReconnecting(sdkError: SDKHMSException) {\n const error = SDKToHMS.convertException(sdkError);\n HMSLogger.e('Reconnection: received error from sdk', error);\n this.hmsNotifications.sendReconnecting(error);\n this.setState(store => {\n store.room.roomState = HMSRoomState.Reconnecting;\n store.errors.push(error);\n }, 'reconnecting');\n }\n\n protected onError(sdkException: SDKHMSException) {\n const error = SDKToHMS.convertException(sdkException);\n if (error.isTerminal) {\n // terminal error leave room as it is not recoverable\n this.leave().then(() => HMSLogger.e('error from SDK, left room.'));\n this.setState(store => {\n store.room.roomState = HMSRoomState.Failed;\n store.errors.push(error);\n }, 'errorTerminal');\n } else {\n const numExistingErrors = this.store.getState().errors.length;\n // just in case there is some infinite loop sending errors\n if (numExistingErrors < 50) {\n this.setState(store => {\n store.errors.push(error);\n }, 'error');\n }\n }\n this.syncRoomState('errorSync'); //TODO: check if need to be done in a different way\n // send notification\n this.hmsNotifications.sendError(error);\n HMSLogger.e('received error from sdk', error instanceof SDKHMSException ? `${error}` : error);\n }\n\n private handleTrackRemove(sdkTrack: SDKHMSTrack, sdkPeer: sdkTypes.HMSPeer) {\n this.setState(draftStore => {\n const hmsPeer = draftStore.peers[sdkPeer.peerId];\n const draftTracks = draftStore.tracks;\n const trackId = sdkTrack.trackId;\n // find and remove the exact track from hmsPeer\n if (this.isSameStoreSDKTrack(trackId, hmsPeer?.audioTrack)) {\n delete hmsPeer?.audioTrack;\n } else if (this.isSameStoreSDKTrack(trackId, hmsPeer?.videoTrack)) {\n delete hmsPeer?.videoTrack;\n } else {\n const auxiliaryIndex = hmsPeer?.auxiliaryTracks.indexOf(trackId);\n if (auxiliaryIndex > -1 && this.isSameStoreSDKTrack(trackId, hmsPeer?.auxiliaryTracks[auxiliaryIndex])) {\n hmsPeer?.auxiliaryTracks.splice(auxiliaryIndex, 1);\n }\n }\n delete draftTracks[trackId];\n delete this.hmsSDKTracks[trackId];\n }, 'trackRemoved');\n }\n\n private async setEnabledSDKTrack(trackID: string, enabled: boolean) {\n const track = this.hmsSDKTracks[trackID];\n if (track) {\n await track.setEnabled(enabled);\n } else {\n this.logPossibleInconsistency(`track ${trackID} not present, unable to enabled/disable`);\n }\n }\n\n private async setSDKLocalVideoTrackSettings(trackID: string, settings: Partial<sdkTypes.HMSVideoTrackSettings>) {\n const track = this.hmsSDKTracks[trackID] as SDKHMSLocalVideoTrack;\n if (track) {\n await track.setSettings(settings);\n } else {\n this.logPossibleInconsistency(`local track ${trackID} not present, unable to set settings`);\n }\n }\n\n private async setSDKLocalAudioTrackSettings(trackID: string, settings: Partial<sdkTypes.HMSAudioTrackSettings>) {\n const track = this.hmsSDKTracks[trackID] as SDKHMSLocalAudioTrack;\n if (track) {\n await track.setSettings(settings);\n } else {\n this.logPossibleInconsistency(`local track ${trackID} not present, unable to set settings`);\n }\n }\n\n private getMediaSettings(sdkPeer: sdkTypes.HMSPeer): Partial<HMSMediaSettings> {\n const settings = this.store.getState(selectLocalMediaSettings);\n const audioTrack = sdkPeer.audioTrack as SDKHMSLocalAudioTrack;\n const videoTrack = sdkPeer.videoTrack as SDKHMSLocalVideoTrack;\n return {\n audioInputDeviceId: audioTrack?.settings.deviceId || settings.audioInputDeviceId,\n videoInputDeviceId: videoTrack?.settings.deviceId || settings.videoInputDeviceId,\n audioOutputDeviceId: this.sdk.getAudioOutput().getDevice()?.deviceId,\n };\n }\n\n private getPreviewFields(sdkLocalPeer: sdkTypes.HMSLocalPeer): HMSStore['preview'] {\n // if room is not in preview, clear preview fields\n if (!sdkLocalPeer.isInPreview()) {\n return;\n }\n\n const hmsLocalPeer = SDKToHMS.convertPeer(sdkLocalPeer);\n\n return {\n localPeer: hmsLocalPeer.id,\n audioTrack: hmsLocalPeer.audioTrack,\n videoTrack: hmsLocalPeer.videoTrack,\n asRole: sdkLocalPeer.asRole?.name || sdkLocalPeer.role?.name,\n };\n }\n\n private async setTrackVolume(value: number, trackId: HMSTrackID) {\n const track = this.hmsSDKTracks[trackId];\n if (track) {\n if (track instanceof SDKHMSAudioTrack) {\n await track.setVolume(value);\n this.setState(draftStore => {\n const track = draftStore.tracks[trackId];\n if (track && track.type === 'audio') {\n track.volume = value;\n }\n }, 'trackVolume');\n } else {\n HMSLogger.w(`track ${trackId} is not an audio track`);\n }\n } else {\n this.logPossibleInconsistency(`track ${trackId} not present, unable to set volume`);\n }\n }\n\n /**\n * Tells if the trackID is for local peer and video unmute is in process\n * @private\n */\n private localAndVideoUnmuting(trackID: string) {\n const localPeer = this.store.getState(selectLocalPeer);\n if (localPeer?.videoTrack !== trackID) {\n return false;\n }\n const displayEnabled = this.store.getState(selectIsLocalVideoDisplayEnabled);\n const actuallyEnabled = this.store.getState(selectIsLocalVideoEnabled);\n return displayEnabled && !actuallyEnabled;\n }\n\n private logPossibleInconsistency(inconsistency: string) {\n HMSLogger.w('possible inconsistency detected - ', inconsistency);\n }\n\n private async addRemoveVideoPlugin(plugin: HMSVideoPlugin, action: 'add' | 'remove', pluginFrameRate?: number) {\n if (!plugin) {\n HMSLogger.w('Invalid plugin received in store');\n return;\n }\n const trackID = this.store.getState(selectLocalVideoTrackID);\n if (trackID) {\n const sdkTrack = this.hmsSDKTracks[trackID];\n if (sdkTrack) {\n if (action === 'add') {\n await (sdkTrack as SDKHMSLocalVideoTrack).addPlugin(plugin, pluginFrameRate);\n } else if (action === 'remove') {\n await (sdkTrack as SDKHMSLocalVideoTrack).removePlugin(plugin);\n }\n this.syncRoomState(`${action}VideoPlugin`);\n } else {\n this.logPossibleInconsistency(`track ${trackID} not present, unable to remove plugin`);\n }\n }\n }\n private async addRemoveAudioPlugin(plugin: HMSAudioPlugin, action: 'add' | 'remove') {\n if (!plugin) {\n HMSLogger.w('Invalid plugin received in store');\n return;\n }\n const trackID = this.store.getState(selectLocalAudioTrackID);\n if (trackID) {\n const sdkTrack = this.hmsSDKTracks[trackID];\n if (sdkTrack) {\n if (action === 'add') {\n await (sdkTrack as SDKHMSLocalAudioTrack).addPlugin(plugin);\n } else if (action === 'remove') {\n await (sdkTrack as SDKHMSLocalAudioTrack).removePlugin(plugin);\n }\n this.syncRoomState(`${action}AudioPlugin`);\n } else {\n this.logPossibleInconsistency(`track ${trackID} not present, unable to remove plugin`);\n }\n }\n }\n\n /**\n * In case of replace track id is changed but not in store. Given the store id, check the real id\n * sdk is using to refer to the track and match them.\n */\n private isSameStoreSDKTrack(sdkTrackID: string, storeTrackID?: string): boolean {\n if (!storeTrackID) {\n return false;\n }\n return this.hmsSDKTracks[storeTrackID]?.trackId === sdkTrackID;\n }\n\n /**\n * convert new role change requests to store format and save.\n * keep only one request at a time in store till we figure out how to handle multiple requests at the same time\n */\n private onRoleChangeRequest(request: SDKHMSRoleChangeRequest) {\n this.setState(store => {\n if (store.roleChangeRequests.length === 0) {\n store.roleChangeRequests.push(SDKToHMS.convertRoleChangeRequest(request));\n }\n }, 'roleChangeRequest');\n }\n\n private removeRoleChangeRequest(toRemove: HMSRoleChangeRequest) {\n this.setState(store => {\n const index = store.roleChangeRequests.findIndex(req => {\n return req.token === toRemove.token;\n });\n if (index !== -1) {\n store.roleChangeRequests.splice(index, 1);\n }\n }, 'removeRoleChangeRequest');\n }\n\n private onRoleUpdate() {\n this.syncRoomState('roleUpdate');\n }\n\n private getStoreLocalTrackIDfromSDKTrack(sdkTrack: SDKHMSLocalTrack) {\n const trackIDs = this.store.getState(selectLocalTrackIDs);\n return trackIDs.find(trackID => this.hmsSDKTracks[trackID].trackId === sdkTrack.trackId);\n }\n\n private setProgress = ({ type, progress }: sdkTypes.HMSPlaylistProgressEvent) => {\n this.setState(draftStore => {\n draftStore.playlist[type].progress = progress;\n draftStore.playlist[type].currentTime = this.sdk.getPlaylistManager().getCurrentTime(type);\n }, 'playlistProgress');\n };\n\n private syncPlaylistState = (action: string) => {\n this.setState(draftStore => {\n Object.assign(draftStore.playlist, SDKToHMS.convertPlaylist(this.sdk.getPlaylistManager()));\n }, action);\n };\n\n private sendPeerUpdateNotification = (type: sdkTypes.HMSPeerUpdate, sdkPeer: sdkTypes.HMSPeer) => {\n let peer = this.store.getState(selectPeerByID(sdkPeer.peerId));\n const actionName = PEER_NOTIFICATION_TYPES[type] || 'peerUpdate';\n if ([sdkTypes.HMSPeerUpdate.PEER_JOINED, sdkTypes.HMSPeerUpdate.PEER_LEFT].includes(type)) {\n this.syncRoomState(actionName);\n // if peer wasn't available before sync(will happen if event is peer join)\n if (!peer) {\n peer = this.store.getState(selectPeerByID(sdkPeer.peerId));\n }\n } else {\n const hmsPeer = SDKToHMS.convertPeer(sdkPeer) as HMSPeer;\n this.setState(draftStore => {\n const storePeer = draftStore.peers[hmsPeer.id];\n if (isEntityUpdated(storePeer, hmsPeer)) {\n if (areArraysEqual(storePeer.auxiliaryTracks, hmsPeer.auxiliaryTracks)) {\n storePeer.auxiliaryTracks = hmsPeer.auxiliaryTracks;\n }\n Object.assign(storePeer, hmsPeer);\n }\n peer = hmsPeer;\n }, actionName);\n }\n this.hmsNotifications.sendPeerUpdate(type, peer);\n };\n\n private setSessionStoreValueLocally(\n updates: SessionStoreUpdate | SessionStoreUpdate[],\n actionName = 'setSessionStore',\n ) {\n const updatesList: SessionStoreUpdate[] = Array.isArray(updates) ? updates : [updates];\n this.setState(store => {\n updatesList.forEach(update => {\n store.sessionStore[update.key as keyof T['sessionStore']] = update.value;\n });\n }, actionName);\n }\n\n private getSDKHMSPeer = (peerID: HMSPeerID) => {\n return this.sdk.getPeerMap()[peerID];\n };\n\n /**\n * setState is separate so any future changes to how state change can be done from one place.\n * @param fn\n * @param name\n */\n private setState: NamedSetState<HMSStore<T>> = (fn, name) => {\n return this.store.namedSetState(fn, name);\n };\n}\n", "import { HMSRemoteAudioTrack, HMSRemoteVideoTrack, HMSTrack } from '@100mslive/hms-video';\n\nexport function isRemoteTrack(track: HMSTrack) {\n return track instanceof HMSRemoteAudioTrack || track instanceof HMSRemoteVideoTrack;\n}\n", "import {\n HMSLocalAudioTrack as SDKHMSLocalAudioTrack,\n HMSLocalVideoTrack as SDKHMSLocalVideoTrack,\n HMSRemoteAudioTrack as SDKHMSRemoteAudioTrack,\n HMSRemoteVideoTrack as SDKHMSRemoteVideoTrack,\n HMSRoleChangeRequest as SDKHMSRoleChangeRequest,\n HMSTrack as SDKHMSTrack,\n} from '@100mslive/hms-video';\nimport { areArraysEqual } from './sdkUtils/storeMergeUtils';\nimport * as sdkTypes from './sdkTypes';\nimport {\n HMSAudioTrack,\n HMSDeviceChangeEvent,\n HMSException,\n HMSMessage,\n HMSPeer,\n HMSPeerID,\n HMSPlaylistItem,\n HMSPlaylistType,\n HMSRole,\n HMSRoleChangeStoreRequest,\n HMSRoleName,\n HMSRoom,\n HMSScreenVideoTrack,\n HMSTrack,\n HMSTrackFacingMode,\n HMSVideoTrack,\n} from '../schema';\n\n/**\n * This file has conversion functions from schema defined in sdk to normalised schema defined in store.\n * A lot of conversions below involve deep clone as once the object goes into store it becomes unmodifiable\n * due to immer, so it can't be mutated later.\n *\n * Objects directly from the SDK are not stored as is and cloned because the SDK might modify it later\n */\n\nexport class SDKToHMS {\n static convertPeer(sdkPeer: sdkTypes.HMSPeer): Partial<HMSPeer> & Pick<HMSPeer, 'id'> {\n return {\n id: sdkPeer.peerId,\n name: sdkPeer.name,\n roleName: sdkPeer.role?.name,\n isLocal: sdkPeer.isLocal,\n videoTrack: sdkPeer.videoTrack?.trackId,\n audioTrack: sdkPeer.audioTrack?.trackId,\n auxiliaryTracks: sdkPeer.auxiliaryTracks.map(track => track.trackId),\n customerUserId: sdkPeer.customerUserId,\n metadata: sdkPeer.metadata,\n joinedAt: sdkPeer.joinedAt,\n };\n }\n\n static convertTrack(sdkTrack: SDKHMSTrack, peerId?: HMSPeerID): HMSTrack {\n const track: HMSTrack = {\n id: sdkTrack.trackId,\n source: sdkTrack.source,\n type: sdkTrack.type,\n enabled: sdkTrack.enabled,\n displayEnabled: sdkTrack.enabled,\n peerId: sdkTrack.peerId || peerId,\n } as HMSTrack;\n this.enrichTrack(track, sdkTrack);\n return track;\n }\n\n static enrichTrack(track: HMSTrack, sdkTrack: SDKHMSTrack) {\n const mediaSettings = sdkTrack.getMediaTrackSettings();\n\n if (sdkTrack instanceof SDKHMSRemoteAudioTrack) {\n (track as HMSAudioTrack).volume = sdkTrack.getVolume() || 0;\n }\n SDKToHMS.updateDeviceID(track, sdkTrack);\n SDKToHMS.enrichLocalTrack(track, sdkTrack);\n if (track.type === 'video') {\n if (track.source === 'screen') {\n // @ts-ignore\n track.displaySurface = mediaSettings.displaySurface;\n SDKToHMS.enrichScreenTrack(track as HMSScreenVideoTrack, sdkTrack);\n } else if (track.source === 'regular') {\n (track as HMSVideoTrack).facingMode = mediaSettings.facingMode as HMSTrackFacingMode;\n }\n track.height = mediaSettings.height;\n track.width = mediaSettings.width;\n SDKToHMS.enrichVideoTrack(track as HMSVideoTrack, sdkTrack);\n }\n SDKToHMS.enrichPluginsDetails(track, sdkTrack);\n }\n\n static enrichLocalTrack(track: HMSTrack, sdkTrack: SDKHMSTrack) {\n if (sdkTrack instanceof SDKHMSLocalVideoTrack || sdkTrack instanceof SDKHMSLocalAudioTrack) {\n track.isPublished = sdkTrack.isPublished;\n }\n }\n\n static updateDeviceID(track: HMSTrack, sdkTrack: SDKHMSTrack) {\n if (sdkTrack instanceof SDKHMSLocalVideoTrack || sdkTrack instanceof SDKHMSLocalAudioTrack) {\n track.deviceID = sdkTrack.settings.deviceId;\n } else {\n track.deviceID = sdkTrack.getMediaTrackSettings()?.deviceId;\n }\n }\n\n static enrichVideoTrack(track: HMSVideoTrack, sdkTrack: SDKHMSTrack) {\n if (sdkTrack instanceof SDKHMSRemoteVideoTrack) {\n track.layer = sdkTrack.getLayer();\n track.preferredLayer = sdkTrack.getPreferredLayer();\n track.degraded = sdkTrack.degraded;\n }\n if (sdkTrack instanceof SDKHMSRemoteVideoTrack || sdkTrack instanceof SDKHMSLocalVideoTrack) {\n if (!areArraysEqual(sdkTrack.getSimulcastDefinitions(), track.layerDefinitions)) {\n track.layerDefinitions = sdkTrack.getSimulcastDefinitions();\n }\n }\n }\n\n static enrichScreenTrack(track: HMSScreenVideoTrack, sdkTrack: SDKHMSTrack) {\n if (sdkTrack instanceof SDKHMSLocalVideoTrack) {\n const newCaptureHandle = sdkTrack.getCaptureHandle?.();\n if (newCaptureHandle?.handle !== track.captureHandle?.handle) {\n track.captureHandle = newCaptureHandle;\n }\n if (sdkTrack.isCurrentTab) {\n track.displaySurface = 'selfBrowser';\n }\n }\n }\n\n static enrichPluginsDetails(track: HMSTrack, sdkTrack: SDKHMSTrack) {\n if (sdkTrack instanceof SDKHMSLocalVideoTrack || sdkTrack instanceof SDKHMSLocalAudioTrack) {\n if (!areArraysEqual(sdkTrack.getPlugins(), track.plugins)) {\n track.plugins = sdkTrack.getPlugins();\n }\n }\n }\n\n static convertRoom(sdkRoom: sdkTypes.HMSRoom, sdkLocalPeerId?: string): Partial<HMSRoom> {\n const { recording, rtmp, hls } = SDKToHMS.convertRecordingStreamingState(\n sdkRoom?.recording,\n sdkRoom?.rtmp,\n sdkRoom?.hls,\n );\n return {\n id: sdkRoom.id,\n name: sdkRoom.name,\n localPeer: sdkLocalPeerId,\n recording,\n rtmp,\n hls,\n sessionId: sdkRoom.sessionId,\n startedAt: sdkRoom.startedAt,\n joinedAt: sdkRoom.joinedAt,\n peerCount: sdkRoom.peerCount,\n };\n }\n\n static convertMessage(sdkMessage: sdkTypes.HMSMessage): Partial<HMSMessage> & Pick<HMSMessage, 'sender'> {\n return {\n sender: sdkMessage.sender?.peerId,\n senderName: sdkMessage.sender?.name,\n senderRole: sdkMessage.sender?.role?.name,\n senderUserId: sdkMessage.sender?.customerUserId,\n recipientPeer: sdkMessage.recipientPeer?.peerId,\n recipientRoles: sdkMessage.recipientRoles?.map(role => role.name),\n time: sdkMessage.time,\n type: sdkMessage.type,\n message: sdkMessage.message,\n id: sdkMessage.id,\n };\n }\n\n static convertRoles(sdkRoles: HMSRole[]): Record<HMSRoleName, HMSRole> {\n const roles: Record<HMSRoleName, HMSRole> = {};\n if (sdkRoles) {\n sdkRoles.forEach(role => {\n roles[role.name] = role;\n });\n }\n return roles;\n }\n\n static convertRoleChangeRequest(req: SDKHMSRoleChangeRequest): HMSRoleChangeStoreRequest {\n return {\n requestedBy: req.requestedBy?.peerId,\n roleName: req.role.name,\n token: req.token,\n };\n }\n\n static convertException(sdkException: sdkTypes.HMSException): HMSException {\n return {\n code: sdkException.code,\n action: sdkException.action,\n name: sdkException.name,\n message: sdkException.message,\n description: sdkException.description,\n isTerminal: sdkException.isTerminal,\n nativeError: sdkException.nativeError,\n timestamp: new Date(),\n };\n }\n\n static convertDeviceChangeUpdate(sdkDeviceChangeEvent: sdkTypes.HMSDeviceChangeEvent): HMSDeviceChangeEvent {\n const convertedData: HMSDeviceChangeEvent = {\n devices: sdkDeviceChangeEvent.devices,\n selection: sdkDeviceChangeEvent.selection,\n type: sdkDeviceChangeEvent.type,\n };\n if (sdkDeviceChangeEvent.error) {\n convertedData.error = this.convertException(sdkDeviceChangeEvent.error);\n }\n return convertedData;\n }\n\n static convertPlaylist(playlistManager: sdkTypes.HMSPlaylistManager) {\n const audioPlaylist = this.getConvertedPlaylistType(playlistManager, HMSPlaylistType.audio);\n const videoPlaylist = this.getConvertedPlaylistType(playlistManager, HMSPlaylistType.video);\n return { audio: audioPlaylist, video: videoPlaylist };\n }\n\n static convertPlaylistItem<T>(\n playlistManager: sdkTypes.HMSPlaylistManager,\n playlistItem: sdkTypes.HMSPlaylistItem<T>,\n ): HMSPlaylistItem<T> {\n const type = playlistItem.type;\n const currentSelection = playlistManager.getCurrentSelection(type);\n const isPlaying = playlistManager.isPlaying(type);\n const isSelected = playlistItem.url === currentSelection?.url;\n\n return {\n ...playlistItem,\n type: playlistItem.type as HMSPlaylistType,\n selected: isSelected,\n playing: isSelected && isPlaying,\n };\n }\n\n private static getConvertedPlaylistType(playlistManager: sdkTypes.HMSPlaylistManager, type: HMSPlaylistType) {\n const convertedPlaylist: Record<string, HMSPlaylistItem<any>> = {};\n const currentSelection = playlistManager.getCurrentSelection(type);\n const progress = playlistManager.getCurrentProgress(type);\n const volume = playlistManager.getVolume(type);\n const list = playlistManager.getList(type);\n const currentIndex = playlistManager.getCurrentIndex(type);\n\n playlistManager.getList(type).forEach(playlistItem => {\n convertedPlaylist[playlistItem.id] = SDKToHMS.convertPlaylistItem(playlistManager, playlistItem);\n });\n return {\n list: convertedPlaylist,\n selection: {\n id: currentSelection?.id,\n hasPrevious: currentIndex > 0,\n hasNext: currentIndex < list.length - 1,\n },\n progress,\n volume,\n currentTime: playlistManager.getCurrentTime(type),\n playbackRate: playlistManager.getPlaybackRate(type),\n };\n }\n\n static convertRecordingStreamingState(\n recording?: sdkTypes.HMSRecording,\n rtmp?: sdkTypes.HMSRTMP,\n hls?: sdkTypes.HMSHLS,\n ): { recording: sdkTypes.HMSRecording; rtmp: sdkTypes.HMSRTMP; hls: sdkTypes.HMSHLS } {\n return {\n recording: {\n browser: {\n running: false,\n ...recording?.browser,\n },\n server: {\n running: false,\n ...recording?.server,\n },\n hls: { running: false, ...recording?.hls },\n },\n rtmp: { running: false, ...rtmp },\n hls: {\n variants: hls?.variants?.map(variant => variant) || [],\n running: !!hls?.running,\n error: hls?.error,\n },\n };\n }\n}\n", "import {\n HMSPollCreateParams,\n HMSPollQuestionCreateParams,\n HMSPollQuestionResponseCreateParams,\n HMSSdk,\n} from '@100mslive/hms-video';\n\nexport interface IHMSInteractivityCenter {\n createPoll(poll: HMSPollCreateParams): Promise<void>;\n startPoll(poll: string | HMSPollCreateParams): Promise<void>;\n stopPoll(poll: string): Promise<void>;\n addQuestionsToPoll(pollID: string, questions: HMSPollQuestionCreateParams[]): Promise<void>;\n addResponsesToPoll(pollID: string, responses: HMSPollQuestionResponseCreateParams[]): Promise<void>;\n}\n\nexport class HMSInteractivityCenter implements IHMSInteractivityCenter {\n constructor(private sdk: HMSSdk) {}\n\n private get sdkInteractivityCenter() {\n return this.sdk.getInteractivityCenter();\n }\n\n createPoll(poll: HMSPollCreateParams) {\n return this.sdkInteractivityCenter.createPoll(poll);\n }\n\n startPoll(poll: string | HMSPollCreateParams) {\n return this.sdkInteractivityCenter.startPoll(poll);\n }\n\n stopPoll(poll: string) {\n return this.sdkInteractivityCenter.stopPoll(poll);\n }\n\n addQuestionsToPoll(poll: string, questions: HMSPollQuestionCreateParams[]) {\n return this.sdkInteractivityCenter.addQuestionsToPoll(poll, questions);\n }\n\n addResponsesToPoll(pollID: string, responses: HMSPollQuestionResponseCreateParams[]) {\n return this.sdkInteractivityCenter.addResponsesToPoll(pollID, responses);\n }\n}\n", "import { HMSPlaylistManager } from './sdkTypes';\nimport { HMSLogger } from '../../common/ui-logger';\nimport { IHMSStore } from '../IHMSStore';\nimport { HMSGenericTypes, HMSPlaylistItem, HMSPlaylistType, IHMSPlaylistActions } from '../schema';\nimport { selectAudioPlaylist, selectVideoPlaylist } from '../selectors';\n\nexport class HMSPlaylist<T extends HMSGenericTypes> implements IHMSPlaylistActions {\n private type: HMSPlaylistType;\n constructor(\n private playlistManager: HMSPlaylistManager,\n type: HMSPlaylistType,\n private syncPlaylistState: (action: string) => void,\n private store: IHMSStore<T>,\n ) {\n this.type = type;\n }\n\n async play(id: string): Promise<void> {\n if (!id) {\n HMSLogger.w('Please pass id to play');\n return;\n }\n await this.playlistManager.setEnabled(true, { id, type: this.type });\n }\n\n async pause(): Promise<void> {\n const selector = this.type === HMSPlaylistType.audio ? selectAudioPlaylist : selectVideoPlaylist;\n const selection = this.store.getState(selector.selection);\n if (!selection.id) {\n HMSLogger.w('No item is currently playing to pause');\n return;\n }\n await this.playlistManager.setEnabled(false, { id: selection.id, type: this.type });\n }\n\n async playNext(): Promise<void> {\n await this.playlistManager.playNext(this.type);\n }\n\n async playPrevious(): Promise<void> {\n await this.playlistManager.playPrevious(this.type);\n }\n\n seek(seekValue: number): void {\n this.playlistManager.seek(seekValue, this.type);\n this.syncPlaylistState(`seekOn${this.type}Playlist`);\n }\n\n seekTo(seekValue: number): void {\n this.playlistManager.seekTo(seekValue, this.type);\n this.syncPlaylistState(`seekToOn${this.type}Playlist`);\n }\n\n setVolume(volume: number): void {\n this.playlistManager.setVolume(volume, this.type);\n this.syncPlaylistState(`setVolumeOn${this.type}Playlist`);\n }\n\n setList<T>(list: HMSPlaylistItem<T>[]): void {\n this.playlistManager.setList(list);\n this.syncPlaylistState(`setListOn${this.type}Playlist`);\n }\n\n async stop(): Promise<void> {\n await this.playlistManager.stop(this.type);\n this.syncPlaylistState(`stop${this.type}Playlist`);\n }\n\n setIsAutoplayOn(autoplay: boolean) {\n this.playlistManager.setIsAutoplayOn(this.type, autoplay);\n }\n\n setPlaybackRate(playbackRate: number) {\n this.playlistManager.setPlaybackRate(this.type, playbackRate);\n this.syncPlaylistState(`set${this.type}PlaybackRate`);\n }\n\n async removeItem(id: string) {\n const removed = await this.playlistManager.removeItem(id, this.type);\n if (removed) {\n this.syncPlaylistState(`remove${this.type}PlaylistItem`);\n }\n return removed;\n }\n\n async clearList() {\n await this.playlistManager.clearList(this.type);\n this.syncPlaylistState(`clear${this.type}Playlist`);\n }\n}\n", "import { HMSSdk, SessionStoreUpdate } from '@100mslive/hms-video';\nimport { IHMSSessionStoreActions } from '../schema';\n\nexport class HMSSessionStore<T extends Record<string, any>> implements IHMSSessionStoreActions<T> {\n constructor(\n private sdk: HMSSdk,\n private setLocally: (updates: SessionStoreUpdate | SessionStoreUpdate[], actionName?: string) => void,\n ) {}\n\n private get sdkSessionStore() {\n return this.sdk.getSessionStore();\n }\n\n async set<K extends keyof T>(key: K, value?: T[K]) {\n const { value: latestValue } = await this.sdkSessionStore.set(String(key), value);\n this.setLocally({ key: key as string, value: latestValue });\n }\n\n async observe(keys: keyof T | Array<keyof T>) {\n const stringifiedKeys: string[] = Array.isArray(keys) ? keys.map(key => String(key)) : [String(keys)];\n await this.sdkSessionStore.observe(stringifiedKeys);\n }\n\n async unobserve(keys: keyof T | Array<keyof T>) {\n const stringifiedKeys: string[] = Array.isArray(keys) ? keys.map(key => String(key)) : [String(keys)];\n await this.sdkSessionStore.unobserve(stringifiedKeys);\n }\n}\n", "import { HMSLogger } from '../../common/ui-logger';\nimport { HMSGenericTypes, HMSPeer, IHMSStore, selectIsConnectedToRoom, selectPeers } from '../../core';\nimport { IHMSActions } from '../../core/IHMSActions';\n\n/**\n * Log data of audio level and speaker speaking periodically to beam for transcript\n * diarization.\n */\nexport class BeamSpeakerLabelsLogger<T extends HMSGenericTypes> {\n private audioContext?: AudioContext;\n private readonly intervalMs: number;\n private shouldMonitor: boolean;\n private hasStarted: boolean;\n private unsubs: any[];\n private readonly analysers: Record<string, AnalyserNode>;\n private readonly store: IHMSStore<T>;\n private actions: IHMSActions<T>;\n constructor(store: IHMSStore<T>, actions: IHMSActions<T>) {\n this.intervalMs = 100;\n this.shouldMonitor = false;\n this.hasStarted = false;\n this.unsubs = [];\n this.analysers = {};\n this.store = store;\n this.actions = actions;\n }\n\n async start() {\n if (this.hasStarted) {\n return;\n }\n this.hasStarted = true;\n HMSLogger.d('starting audio level monitor for remote peers', this.store);\n const isConnected = this.store.getState(selectIsConnectedToRoom);\n HMSLogger.d('starting audio levels is connected to room', isConnected);\n if (isConnected) {\n await this.monitorAudioLevels();\n }\n const unsub = this.store.subscribe(this.monitorAudioLevels.bind(this), selectIsConnectedToRoom);\n this.unsubs.push(unsub);\n }\n\n async stop() {\n if (!this.hasStarted) {\n return;\n }\n this.hasStarted = false;\n this.shouldMonitor = false;\n this.unsubs.forEach(unsub => unsub());\n HMSLogger.d('stopped audio level monitor for remote peers');\n }\n\n async monitorAudioLevels() {\n const isConnected = this.store.getState(selectIsConnectedToRoom);\n if (!isConnected) {\n if (this.shouldMonitor) {\n HMSLogger.i('room no longer connected, stopping audio level monitoring for remote');\n this.shouldMonitor = false;\n }\n return;\n }\n if (this.shouldMonitor) {\n return;\n }\n HMSLogger.i('monitoring audio levels');\n this.shouldMonitor = true;\n const loop = () => {\n if (this.shouldMonitor) {\n this.logAllPeersAudioLevels();\n setTimeout(loop, this.intervalMs);\n } else {\n HMSLogger.i('stopped monitoring audio levels');\n }\n };\n setTimeout(loop, 1000);\n }\n\n // eslint-disable-next-line complexity\n async logAllPeersAudioLevels() {\n if (!window.__triggerBeamEvent__) {\n return;\n }\n // optimise this to selectTracks instead of selecting peers\n const allPeers = this.store.getState(selectPeers);\n const peers = allPeers.filter(peer => !!peer.audioTrack);\n const peerAudioLevels = [];\n for (const peer of peers) {\n // @ts-ignore\n const sdkTrack = this.actions.hmsSDKTracks[peer.audioTrack];\n const nativeStream: MediaStream = sdkTrack?.stream?.nativeStream;\n if (!peer.joinedAt) {\n continue;\n }\n if (nativeStream) {\n const peerLevel = await this.getAudioLevel(peer, nativeStream);\n if (peerLevel.level > 0) {\n peerAudioLevels.push(peerLevel);\n }\n }\n }\n if (peerAudioLevels.length > 0) {\n const payload = {\n event: 'app-audio-level',\n data: peerAudioLevels,\n };\n // HMSLogger.d('logging audio levels', peerAudioLevels);\n window.__triggerBeamEvent__(JSON.stringify(payload));\n }\n }\n\n async getAudioLevel(peer: HMSPeer, stream: MediaStream) {\n if (!this.analysers[stream.id]) {\n this.analysers[stream.id] = this.createAnalyserNode(stream);\n }\n const analyserNode = this.analysers[stream.id];\n const level = this.calculateAudioLevel(analyserNode);\n return {\n peerId: peer.id,\n peerName: peer.name,\n level,\n };\n }\n\n createAnalyserNode(stream: MediaStream) {\n if (!this.audioContext) {\n this.audioContext = new AudioContext();\n }\n const analyser = this.audioContext.createAnalyser();\n const source = this.audioContext.createMediaStreamSource(stream);\n source.connect(analyser);\n return analyser;\n }\n\n calculateAudioLevel(analyserNode: AnalyserNode) {\n const data = new Uint8Array(analyserNode.fftSize);\n analyserNode.getByteTimeDomainData(data);\n const lowest = 0.009;\n let max = lowest;\n for (const frequency of data) {\n max = Math.max(max, (frequency - 128) / 128);\n }\n const normalized = (Math.log(lowest) - Math.log(max)) / Math.log(lowest);\n const percent = Math.ceil(Math.min(Math.max(normalized * 100, 0), 100));\n return percent;\n }\n}\n", "import { Subscribe } from 'zustand/vanilla';\nimport { HMSSdk } from '@100mslive/hms-video';\nimport { subscribeToSdkWebrtcStats } from './webrtc-stats-store';\nimport { storeNameWithTabTitle } from '../../common/storeName';\nimport { HMSReactiveStore } from '../hmsSDKStore/HMSReactiveStore';\nimport { GetState, IHMSStatsStore, IHMSStatsStoreReadOnly, IHMSStore } from '../IHMSStore';\nimport { createDefaultStatsStore, HMSStatsStore } from '../schema';\nimport { selectRoomState } from '../selectors';\n\n/**\n * @internal\n */\nexport class HMSStats implements IHMSStatsStoreReadOnly {\n readonly getState: GetState<HMSStatsStore>;\n readonly subscribe: Subscribe<HMSStatsStore>;\n readonly getPublishPeerConnection: () => Promise<RTCPeerConnection | undefined>;\n readonly getSubscribePeerConnection: () => Promise<RTCPeerConnection | undefined>;\n private readonly store: IHMSStatsStore;\n\n constructor(private hmsStore: IHMSStore, private sdk?: HMSSdk) {\n this.store = HMSReactiveStore.createNewHMSStore<HMSStatsStore>(\n storeNameWithTabTitle('HMSStatsStore'),\n createDefaultStatsStore,\n );\n\n this.getState = this.store.getState;\n this.subscribe = this.store.subscribe;\n\n this.getPublishPeerConnection = () =>\n new Promise<RTCPeerConnection | undefined>(resolve => {\n if (this.hmsStore.getState(selectRoomState) === 'Connected') {\n resolve(this.sdk?.getWebrtcInternals()?.getPublishPeerConnection());\n } else {\n this.hmsStore.subscribe(roomState => {\n if (roomState === 'Connected') {\n resolve(this.sdk?.getWebrtcInternals()?.getPublishPeerConnection());\n }\n }, selectRoomState);\n }\n });\n\n this.getSubscribePeerConnection = () =>\n new Promise<RTCPeerConnection | undefined>(resolve => {\n if (this.hmsStore.getState(selectRoomState) === 'Connected') {\n resolve(this.sdk?.getWebrtcInternals()?.getSubscribePeerConnection());\n } else {\n this.hmsStore.subscribe(roomState => {\n if (roomState === 'Connected') {\n resolve(this.sdk?.getWebrtcInternals()?.getSubscribePeerConnection());\n }\n }, selectRoomState);\n }\n });\n\n if (!this.sdk) {\n return;\n }\n\n subscribeToSdkWebrtcStats(this.sdk, this.store, this.hmsStore);\n }\n}\n", "import { createSelector } from 'reselect';\nimport { HMSPreferredSimulcastLayer, RID, simulcastMapping } from '../hmsSDKStore/sdkTypes';\nimport { HMSPeerID, HMSStatsStore, HMSTrackID } from '../schema';\nimport { byIDCurry } from '../selectors/common';\n\nconst selectLocalPeerID = (store: HMSStatsStore) => store.localPeer.id;\nconst selectLocalAudioTrackID = (store: HMSStatsStore) => store.localPeer.audioTrack;\nconst selectLocalVideoTrackID = (store: HMSStatsStore) => store.localPeer.videoTrack;\nconst selectPeerID = (_store: HMSStatsStore, peerID: HMSPeerID | undefined) => peerID;\nconst selectTrackID = (_store: HMSStatsStore, trackID: HMSTrackID | undefined) => trackID;\nconst selectRemoteTrackStatsMap = (store: HMSStatsStore) => store.remoteTrackStats;\nconst selectPeerStatsMap = (store: HMSStatsStore) => store.peerStats;\nconst selectLocalTrackStatsMap = (store: HMSStatsStore) => store.localTrackStats;\n\n/**\n * Local peer stats selectors\n */\nconst localPeerStats = createSelector(\n [selectPeerStatsMap, selectLocalPeerID],\n (storePeerStats, localPeerID) => storePeerStats[localPeerID],\n);\n\n/**\n * The total number of packets lost during the call\n */\nconst packetsLost = createSelector(localPeerStats, localPeerStats => localPeerStats?.subscribe?.packetsLost);\n\nconst jitter = createSelector(localPeerStats, localPeerStats => localPeerStats?.subscribe?.jitter);\n\n/**\n * The bitrate at which all the local tracks are being published at\n */\nconst publishBitrate = createSelector(localPeerStats, localPeerStats => localPeerStats?.publish?.bitrate);\n\n/**\n * The bitrate at which all the remote tracks are being received at\n */\nconst subscribeBitrate = createSelector(localPeerStats, localPeerStats => localPeerStats?.subscribe?.bitrate);\n\n/**\n * The total bitrate available for publishing\n */\nconst availablePublishBitrate = createSelector(\n localPeerStats,\n localPeerStats => localPeerStats?.publish?.availableOutgoingBitrate,\n);\n\n/**\n * The total bitrate available for subscribing to remote peers\n */\nconst availableSubscribeBitrate = createSelector(\n localPeerStats,\n localPeerStats => localPeerStats?.subscribe?.availableIncomingBitrate,\n);\n\n/**\n * The total bytes sent by the local peer\n */\nconst totalBytesSent = createSelector(localPeerStats, localPeerStats => localPeerStats?.publish?.bytesSent);\n\n/**\n * The total bytes received by the local peer\n */\nconst totalBytesReceived = createSelector(localPeerStats, localPeerStats => localPeerStats?.subscribe?.bytesReceived);\n\n/**\n * By ID Selectors\n * To be used for remote tracks\n */\n\nconst selectPeerStatsByIDBare = createSelector([selectPeerStatsMap, selectPeerID], (storePeerStats, peerID) =>\n peerID ? storePeerStats[peerID] : undefined,\n);\n\nconst selectTrackStatsByIDBare = createSelector(\n [selectRemoteTrackStatsMap, selectTrackID],\n (storeTrackStats, trackID) => (trackID ? storeTrackStats[trackID] : undefined),\n);\n\nconst selectLocalTrackStatsByIDBare = createSelector(\n [selectLocalTrackStatsMap, selectTrackID],\n (storeLocalTrackStats, trackID) => (trackID ? storeLocalTrackStats[trackID] : undefined),\n);\n\n/**\n * Stats(bitrate, bytes sent/received, etc...) for a single peer given the peer ID\n */\nconst peerStatsByID = byIDCurry(selectPeerStatsByIDBare);\n\n/**\n * Stats(bitrate, bytes sent/received, framerate, FPS, etc...) for a remote track\n */\nconst trackStatsByID = byIDCurry(selectTrackStatsByIDBare);\n\n/**\n * Local track stats selectors\n */\n\nconst localAudioTrackStats = createSelector(\n [selectLocalTrackStatsMap, selectLocalAudioTrackID],\n (trackStats, trackID) => (trackID ? trackStats[trackID]?.[0] : undefined),\n);\n\nconst localAudioTrackStatsByID = byIDCurry(\n createSelector(selectLocalTrackStatsByIDBare, trackStats => trackStats?.[0]),\n);\n\nconst localVideoTrackStats = createSelector(\n [selectLocalTrackStatsMap, selectLocalVideoTrackID],\n (trackStats, trackID) => (trackID ? trackStats[trackID]?.[0] : undefined),\n);\n\nconst localVideoTrackStatsByID = byIDCurry(createSelector(selectLocalTrackStatsByIDBare, trackStats => trackStats));\n\nconst localVideoTrackStatsByLayer = (layer?: HMSPreferredSimulcastLayer) =>\n byIDCurry(\n createSelector(selectLocalTrackStatsByIDBare, stats => {\n const rid = (Object.keys(simulcastMapping) as RID[]).find(key => simulcastMapping[key] === layer);\n return layer ? stats?.find(stat => stat.rid === rid) || stats?.[0] : stats?.[0];\n }),\n );\n\nexport const selectHMSStats = {\n localPeerStats,\n packetsLost,\n jitter,\n publishBitrate,\n subscribeBitrate,\n availablePublishBitrate,\n availableSubscribeBitrate,\n totalBytesSent,\n totalBytesReceived,\n peerStatsByID,\n trackStatsByID,\n localAudioTrackStatsByID,\n localVideoTrackStatsByID,\n localVideoTrackStatsByLayer,\n localAudioTrackStats,\n localVideoTrackStats,\n};\n"],
|
5
|
-
"mappings": "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",
|
6
|
-
"names": ["window", "undefined", "regexes", "j", "k", "ua", "$", "SDPUtils", "sdp", "pt", "j", "o", "v", "sdp", "j", "n", "sdp", "React", "Fragment", "useState", "selectLocalPeerID", "selectVideoTrackByPeerID", "useHMSStore", "MicOffIcon", "useHMSStore", "useHMSStore", "selectVideoTrackByPeerID", "useHMSStore", "selectLocalPeerID", "useState", "React", "MicOffIcon", "Fragment", "React", "Fragment", "React", "React", "React", "Fragment", "useEffect", "useState", "selectLocalPeerID", "useHMSStore", "React", "React", "_", "React", "useState", "selectLocalPeerID", "useHMSStore", "Tile", "useHMSStore", "selectLocalPeerID", "useState", "React", "useRef", "useState", "b", "uuidv4", "uuid", "window", "navigator", "window", "navigator", "b", "cs", "window", "window", "te", "sdp", "shimGetDisplayMedia", "shimGetUserMedia", "shimOnTrack", "shimPeerConnection", "shimGetUserMedia", "window", "navigator", "b", "shimGetDisplayMedia", "window", "shimOnTrack", "window", "shimPeerConnection", "shimGetUserMedia", "window", "shimGetUserMedia", "navigator", "RTCPeerConnection", "window", "SDPUtils", "sdp", "window", "logging", "adapter", "shimPeerConnection", "shimGetUserMedia", "shimOnTrack", "uuid", "EventEmitter", "require_package", "__commonJSMin", "exports", "module", "HMSIdFactory", "uuidv4", "parsedUserAgent", "UAParser", "isBrowser", "_a", "isNode", "ENV", "checkIsSupported", "isSupported", "getDomainCategory", "isBrowser", "baseurl", "domainCategory", "LocalStorage", "key", "val", "i", "initializeLocalstoragePolyfill", "_a", "stringItem", "value", "stringValue", "getAnalyticsDeviceId", "id", "storage", "storageId", "uuid", "HMSLogLevel", "isTestEnv", "HMSLogger", "tag", "data", "mark", "level", "entry", "error", "HMSException", "_HMSException", "code", "name", "action", "message", "description", "isTerminal", "error", "_a", "isPresent", "value", "sdk_version", "createUserAgent", "sdkEnv", "frameworkInfo", "sdk", "env", "domainCategory", "isNode", "convertObjectToString", "parsedOs", "parsedUserAgent", "parsedDevice", "parsedBrowser", "os", "replaceSpaces", "os_version", "browser", "device_model", "s", "object", "delimiter", "key", "isPresent", "AnalyticsEvent", "name", "level", "properties", "includesPII", "timestamp", "uuid", "getAnalyticsDeviceId", "__spreadProps", "__spreadValues", "AnalyticsEventFactory", "error", "additionalProperties", "requestedAt", "respondedAt", "endpoint", "_a", "_b", "props", "__objRest", "_c", "_d", "devices", "settings", "selection", "type", "stats", "track", "isDegraded", "restoredAt", "duration", "device", "ok", "initialProperties", "errorProperties", "HMSException", "HMSTrackType", "DeviceStorage", "LocalStorage", "devices", "value", "type", "deviceId", "groupId", "newSelection", "device", "HMSLogger", "selectedDevices", "current", "DeviceStorageManager", "HMSAudioPluginType", "HMSPluginUnsupportedTypes", "debounce", "fn", "delay", "timer", "args", "context", "HMSRoomUpdate", "HMSPeerUpdate", "HMSTrackUpdate", "HMSPollsUpdate", "HMSSimulcastLayer", "HMSVideoCodec", "HMSAudioCodec", "HMSFacingMode", "DeviceType", "HMSPlaylistType", "HMSPollQuestionType", "HMSIntersectionObserverWrapper", "element", "onIntersection", "_a", "entries", "entry", "isSupported", "isBrowser", "HMSLogger", "HMSIntersectionObserver", "HMSResizeObserverWrapper", "onResize", "options", "debounce", "HMSResizeObserver", "HMSVideoPluginType", "HMSVideoPluginCanvasContextType", "CLIENT_ANAYLTICS_PROD_ENDPOINT", "CLIENT_ANAYLTICS_QA_ENDPOINT", "sdk_version", "HMSLogger", "adapter", "ClientAnalyticsTransport", "LocalStorage", "env", "ws", "event", "requestBody", "url", "CLIENT_ANAYLTICS_PROD_ENDPOINT", "CLIENT_ANAYLTICS_QA_ENDPOINT", "response", "error", "HMSLogger", "events", "existingEvents", "existingEvent", "index", "storageEvent", "HTTPAnalyticsTransport", "TransportFailureCategory", "TransportState", "HMSConnectionRole", "EventEmitter", "require_package", "__commonJSMin", "exports", "module", "HMSRoomState", "HMSMessageType", "HMSNotificationSeverity", "HMSNotificationTypes", "HMSPlaylistType", "getScreenSharesByPeer", "tracks", "peer", "videoTrack", "audioTrack", "trackID", "track", "isScreenShare", "isAudio", "isVideo", "isAudioPlaylist", "isVideoPlaylist", "isDegraded", "isTrackEnabled", "store", "isRoleAllowedToPublish", "role", "_a", "video", "audio", "screen", "selectRoom", "store", "selectErrors", "selectRecentError", "createSelector", "errors", "selectRoomID", "room", "selectPeersMap", "selectMessagesMap", "selectMessageIDsInOrder", "selectTracksMap", "selectFullAppData", "store", "selectSpeakers", "store", "selectIsConnectedToRoom", "createSelector", "selectRoom", "room", "selectPeerCount", "isConnected", "selectHideLocalPeer", "store", "selectPeers", "selectPeersMap", "storePeers", "hideLocalPeer", "peerID", "selectTracks", "selectTracksMap", "storeTracks", "selectLocalPeer", "peers", "selectLocalPeerID", "selectLocalPeerName", "peer", "selectLocalPeerRoleName", "selectLocalAudioTrackID", "selectLocalVideoTrackID", "selectLocalAuxiliaryTrackIDs", "selectLocalTrackIDs", "audioTrackID", "videoTrackID", "auxiliaryTrackIDs", "trackIDs", "selectRemotePeers", "p", "selectDominantSpeaker", "selectSpeakers", "peersMap", "speakers", "speakersInOrder", "s1", "s2", "_a", "_b", "s1Level", "selectIsLocalScreenShared", "createSelector", "selectLocalPeer", "selectTracksMap", "localPeer", "tracksMap", "video", "audio", "getScreenSharesByPeer", "selectPeerScreenSharing", "selectPeersMap", "peersMap", "screensharePeer", "peerID", "peer", "selectIsSomeoneScreenSharing", "selectPeerSharingAudio", "selectPeersScreenSharing", "videoPeers", "audioPeers", "selectPeerSharingVideoPlaylist", "trackId", "track", "isVideoPlaylist", "isVideo", "selectPeerSharingAudioPlaylist", "isAudioPlaylist", "selectDegradedTracks", "selectTracks", "tracks", "isDegraded", "selectHMSMessagesCount", "selectMessageIDsInOrder", "messageIDs", "selectUnreadHMSMessagesCount", "selectMessagesMap", "messages", "m", "selectHMSMessages", "msgIDs", "msgMap", "msgId", "selectRoomState", "selectRoom", "room", "selectIsInPreview", "roomState", "selectRoomStarted", "selectRolesMap", "store", "selectAvailableRoleNames", "rolesMap", "selectLocalPeerRole", "selectPreviewRoleName", "_a", "selectPreviewRole", "roleName", "selectIsAllowedToSubscribe", "role", "selectPermissions", "selectRecordingState", "selectRTMPState", "selectHLSState", "selectSessionId", "selectRoomStartTime", "selectPollsMap", "store", "selectPlaylistMap", "type", "store", "selectPlaylistSelection", "selectPlaylistProgress", "selectPlaylistCurrentTime", "selectPlaylistPlaybackRate", "selectPlaylistVolume", "selectPlaylist", "createSelector", "storePlaylist", "selectPlaylistSelectedItem", "currentSelection", "selectAudioPlaylist", "selectVideoPlaylist", "byIDCurry", "selector", "id", "HMS_STORE_TAG", "HMSLogger", "tag", "data", "HMSLogLevel", "mark", "level", "entry", "error", "selectPeerID", "_store", "peerID", "selectTrackID", "trackID", "selectRoleName", "roleName", "selectAppDataKey", "key", "selectPollID", "pollID", "selectPeerByIDBare", "selectPeersMap", "storePeers", "selectTrackByIDBare", "selectTracksMap", "storeTracks", "selectVideoTrackByIDBare", "track", "selectAudioTrackByIDBare", "selectScreenAudioTrackByIDBare", "selectScreenVideoTrackByIDBare", "selectPollByIDBare", "selectPollsMap", "storePolls", "selectPeerByID", "selectAppData", "selectFullAppData", "appData", "selectPeerNameByID", "byIDCurry", "createSelector", "selectPeerByIDBare", "peer", "selectTrackByID", "selectTrackByIDBare", "selectVideoTrackByID", "selectVideoTrackByIDBare", "selectAudioTrackByID", "selectAudioTrackByIDBare", "selectScreenAudioTrackByID", "selectScreenAudioTrackByIDBare", "selectScreenVideoTrackByID", "selectScreenVideoTrackByIDBare", "selectVideoTrackByPeerID", "store", "peerID", "selectAudioTrackByPeerID", "selectAuxiliaryTracksByPeerID", "byIDCurry", "store", "peerID", "peer", "selectPeerByIDBare", "trackID", "selectSpeakerByTrackID", "selectTrackAudioByID", "createSelector", "speaker", "selectSpeakerByPeerID", "peerAudioTrack", "selectAudioTrackByPeerID", "selectPeerAudioByID", "selectConnectionQualityByPeerID", "selectAuxiliaryAudioByPeerID", "isAudio", "selectVideoPlaylistVideoTrackByPeerID", "selectTracksMap", "tracks", "track", "isVideoPlaylist", "isVideo", "selectVideoPlaylistAudioTrackByPeerID", "selectAudioPlaylistTrackByPeerID", "isAudioPlaylist", "selectScreenSharesByPeerId", "getScreenSharesByPeer", "selectScreenShareAudioByPeerID", "id", "createSelector", "selectScreenSharesByPeerId", "screenshare", "selectIsPeerAudioEnabled", "byIDCurry", "store", "peerID", "peer", "selectPeerByIDBare", "isTrackEnabled", "selectIsPeerVideoEnabled", "selectIsAudioLocallyMuted", "trackID", "selectIsLocallyMutedByPeerID", "selectIsScreenShareLocallyMutedByPeerID", "track", "selectAudioTrackVolume", "selectTrackByIDBare", "HMSLogger", "selectAudioVolumeByPeerID", "selectScreenshareAudioVolumeByPeerID", "selectSimulcastLayerByTrack", "selectMessagesByPeerIDInternal", "selectHMSMessages", "selectLocalPeerID", "selectPeerID", "messages", "localPeerID", "message", "_a", "selectMessagesByRoleInternal", "selectRoleName", "roleName", "_b", "selectBroadcastMessages", "selectUnreadMessageCountByRole", "m", "selectUnreadMessageCountByPeerID", "selectBroadcastMessagesUnreadCount", "selectMessagesByPeerID", "selectMessagesByRole", "selectMessagesUnreadCountByRole", "selectMessagesUnreadCountByPeerID", "selectPollByID", "byIDCurry", "selectPollByIDBare", "selectPeersWithAudioStatus", "createSelector", "selectPeersMap", "selectTracksMap", "peersMap", "tracksMap", "peer", "_a", "selectRoleChangeStoreRequest", "store", "selectRoleChangeRequest", "selectRolesMap", "request", "rolesMap", "selectIsAllowedToPublish", "selectLocalPeerRole", "role", "isRoleAllowedToPublish", "selectIsAllowedToPreviewMedia", "selectPreviewRole", "selectLocalVideoPlugins", "createSelector", "selectLocalVideoTrackID", "selectTracksMap", "trackID", "tracksMap", "track", "selectLocalAudioPlugins", "selectLocalAudioTrackID", "PEER_NOTIFICATION_TYPES", "HMSPeerUpdate", "TRACK_NOTIFICATION_TYPES", "HMSTrackUpdate", "POLL_NOTIFICATION_TYPES", "HMSPollsUpdate", "selectLocalPeerID", "store", "selectLocalAudioTrackID", "selectLocalVideoTrackID", "selectPeerID", "_store", "peerID", "selectTrackID", "trackID", "selectRemoteTrackStatsMap", "selectPeerStatsMap", "selectLocalTrackStatsMap", "localPeerStats", "createSelector", "storePeerStats", "localPeerID", "packetsLost", "_a", "jitter", "publishBitrate", "subscribeBitrate", "availablePublishBitrate", "availableSubscribeBitrate", "totalBytesSent", "totalBytesReceived", "selectPeerStatsByIDBare", "selectTrackStatsByIDBare", "storeTrackStats", "selectLocalTrackStatsByIDBare", "storeLocalTrackStats", "peerStatsByID", "byIDCurry", "trackStatsByID", "localAudioTrackStats", "trackStats", "localAudioTrackStatsByID", "localVideoTrackStats", "localVideoTrackStatsByID", "_", "useState", "useRef", "useHMSStore", "selectLocalPeerID", "useState", "useEffect", "React", "_a", "Fragment", "React", "Fragment"]
|
7
|
-
}
|