@webex/plugin-meetings 3.0.0-beta.1 → 3.0.0-beta.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/common/browser-detection.js.map +1 -1
- package/dist/common/collection.js.map +1 -1
- package/dist/common/config.js.map +1 -1
- package/dist/common/errors/captcha-error.js +7 -0
- package/dist/common/errors/captcha-error.js.map +1 -1
- package/dist/common/errors/intent-to-join.js +8 -0
- package/dist/common/errors/intent-to-join.js.map +1 -1
- package/dist/common/errors/join-meeting.js +8 -0
- package/dist/common/errors/join-meeting.js.map +1 -1
- package/dist/common/errors/media.js +7 -0
- package/dist/common/errors/media.js.map +1 -1
- package/dist/common/errors/parameter.js.map +1 -1
- package/dist/common/errors/password-error.js +7 -0
- package/dist/common/errors/password-error.js.map +1 -1
- package/dist/common/errors/permission.js +7 -0
- package/dist/common/errors/permission.js.map +1 -1
- package/dist/common/errors/reconnection-in-progress.js.map +1 -1
- package/dist/common/errors/reconnection.js +7 -0
- package/dist/common/errors/reconnection.js.map +1 -1
- package/dist/common/errors/stats.js +7 -0
- package/dist/common/errors/stats.js.map +1 -1
- package/dist/common/errors/webex-errors.js +5 -29
- package/dist/common/errors/webex-errors.js.map +1 -1
- package/dist/common/errors/webex-meetings-error.js +5 -2
- package/dist/common/errors/webex-meetings-error.js.map +1 -1
- package/dist/common/events/events-scope.js.map +1 -1
- package/dist/common/events/events.js.map +1 -1
- package/dist/common/events/trigger-proxy.js.map +1 -1
- package/dist/common/events/util.js.map +1 -1
- package/dist/common/logs/logger-config.js.map +1 -1
- package/dist/common/logs/logger-proxy.js.map +1 -1
- package/dist/common/logs/request.js +3 -0
- package/dist/common/logs/request.js.map +1 -1
- package/dist/common/queue.js.map +1 -1
- package/dist/config.js +1 -0
- package/dist/config.js.map +1 -1
- package/dist/constants.js +15 -74
- package/dist/constants.js.map +1 -1
- package/dist/locus-info/controlsUtils.js.map +1 -1
- package/dist/locus-info/embeddedAppsUtils.js.map +1 -1
- package/dist/locus-info/fullState.js.map +1 -1
- package/dist/locus-info/hostUtils.js.map +1 -1
- package/dist/locus-info/index.js +43 -5
- package/dist/locus-info/index.js.map +1 -1
- package/dist/locus-info/infoUtils.js +4 -0
- package/dist/locus-info/infoUtils.js.map +1 -1
- package/dist/locus-info/mediaSharesUtils.js.map +1 -1
- package/dist/locus-info/parser.js +12 -3
- package/dist/locus-info/parser.js.map +1 -1
- package/dist/locus-info/selfUtils.js.map +1 -1
- package/dist/media/index.js +71 -210
- package/dist/media/index.js.map +1 -1
- package/dist/media/internal-media-core-wrapper.js +22 -0
- package/dist/media/internal-media-core-wrapper.js.map +1 -0
- package/dist/media/properties.js +32 -25
- package/dist/media/properties.js.map +1 -1
- package/dist/media/util.js +0 -27
- package/dist/media/util.js.map +1 -1
- package/dist/mediaQualityMetrics/config.js.map +1 -1
- package/dist/meeting/effectsState.js +8 -1
- package/dist/meeting/effectsState.js.map +1 -1
- package/dist/meeting/index.js +1146 -602
- package/dist/meeting/index.js.map +1 -1
- package/dist/meeting/muteState.js +6 -0
- package/dist/meeting/muteState.js.map +1 -1
- package/dist/meeting/request.js +83 -24
- package/dist/meeting/request.js.map +1 -1
- package/dist/meeting/state.js.map +1 -1
- package/dist/meeting/util.js +5 -44
- package/dist/meeting/util.js.map +1 -1
- package/dist/meeting-info/collection.js +4 -1
- package/dist/meeting-info/collection.js.map +1 -1
- package/dist/meeting-info/index.js +5 -0
- package/dist/meeting-info/index.js.map +1 -1
- package/dist/meeting-info/meeting-info-v2.js +14 -2
- package/dist/meeting-info/meeting-info-v2.js.map +1 -1
- package/dist/meeting-info/request.js +3 -0
- package/dist/meeting-info/request.js.map +1 -1
- package/dist/meeting-info/util.js.map +1 -1
- package/dist/meeting-info/utilv2.js.map +1 -1
- package/dist/meetings/collection.js +4 -1
- package/dist/meetings/collection.js.map +1 -1
- package/dist/meetings/index.js +136 -25
- package/dist/meetings/index.js.map +1 -1
- package/dist/meetings/request.js +4 -0
- package/dist/meetings/request.js.map +1 -1
- package/dist/meetings/util.js +24 -1
- package/dist/meetings/util.js.map +1 -1
- package/dist/member/index.js +30 -7
- package/dist/member/index.js.map +1 -1
- package/dist/member/util.js +2 -1
- package/dist/member/util.js.map +1 -1
- package/dist/members/collection.js +1 -0
- package/dist/members/collection.js.map +1 -1
- package/dist/members/index.js +82 -1
- package/dist/members/index.js.map +1 -1
- package/dist/members/request.js +19 -9
- package/dist/members/request.js.map +1 -1
- package/dist/members/util.js.map +1 -1
- package/dist/metrics/config.js.map +1 -1
- package/dist/metrics/constants.js.map +1 -1
- package/dist/metrics/index.js +8 -0
- package/dist/metrics/index.js.map +1 -1
- package/dist/multistream/mediaRequestManager.js +133 -0
- package/dist/multistream/mediaRequestManager.js.map +1 -0
- package/dist/multistream/multistreamMedia.js +116 -0
- package/dist/multistream/multistreamMedia.js.map +1 -0
- package/dist/multistream/receiveSlot.js +209 -0
- package/dist/multistream/receiveSlot.js.map +1 -0
- package/dist/multistream/receiveSlotManager.js +195 -0
- package/dist/multistream/receiveSlotManager.js.map +1 -0
- package/dist/multistream/remoteMedia.js +289 -0
- package/dist/multistream/remoteMedia.js.map +1 -0
- package/dist/multistream/remoteMediaGroup.js +243 -0
- package/dist/multistream/remoteMediaGroup.js.map +1 -0
- package/dist/multistream/remoteMediaManager.js +1113 -0
- package/dist/multistream/remoteMediaManager.js.map +1 -0
- package/dist/networkQualityMonitor/index.js +10 -2
- package/dist/networkQualityMonitor/index.js.map +1 -1
- package/dist/personal-meeting-room/index.js +11 -0
- package/dist/personal-meeting-room/index.js.map +1 -1
- package/dist/personal-meeting-room/request.js +2 -1
- package/dist/personal-meeting-room/request.js.map +1 -1
- package/dist/personal-meeting-room/util.js.map +1 -1
- package/dist/reachability/index.js +17 -7
- package/dist/reachability/index.js.map +1 -1
- package/dist/reachability/request.js +1 -0
- package/dist/reachability/request.js.map +1 -1
- package/dist/reactions/reactions.js +111 -0
- package/dist/reactions/reactions.js.map +1 -0
- package/dist/reactions/reactions.type.js +40 -0
- package/dist/reactions/reactions.type.js.map +1 -0
- package/dist/reconnection-manager/index.js +130 -132
- package/dist/reconnection-manager/index.js.map +1 -1
- package/dist/roap/index.js +58 -231
- package/dist/roap/index.js.map +1 -1
- package/dist/roap/request.js +7 -116
- package/dist/roap/request.js.map +1 -1
- package/dist/roap/turnDiscovery.js +20 -6
- package/dist/roap/turnDiscovery.js.map +1 -1
- package/dist/statsAnalyzer/global.js +2 -0
- package/dist/statsAnalyzer/global.js.map +1 -1
- package/dist/statsAnalyzer/index.js +58 -37
- package/dist/statsAnalyzer/index.js.map +1 -1
- package/dist/statsAnalyzer/mqaUtil.js +9 -3
- package/dist/statsAnalyzer/mqaUtil.js.map +1 -1
- package/dist/transcription/index.js +10 -3
- package/dist/transcription/index.js.map +1 -1
- package/package.json +21 -20
- package/src/common/{browser-detection.js → browser-detection.ts} +1 -1
- package/src/common/collection.ts +6 -6
- package/src/common/{config.js → config.ts} +1 -1
- package/src/common/errors/{captcha-error.js → captcha-error.ts} +5 -1
- package/src/common/errors/{intent-to-join.js → intent-to-join.ts} +6 -1
- package/src/common/errors/{join-meeting.js → join-meeting.ts} +6 -1
- package/src/common/errors/{media.js → media.ts} +5 -1
- package/src/common/errors/parameter.ts +3 -2
- package/src/common/errors/{password-error.js → password-error.ts} +5 -1
- package/src/common/errors/{permission.js → permission.ts} +5 -1
- package/src/common/errors/{reconnection-in-progress.js → reconnection-in-progress.ts} +0 -0
- package/src/common/errors/{reconnection.js → reconnection.ts} +5 -1
- package/src/common/errors/{stats.js → stats.ts} +5 -1
- package/src/common/errors/{webex-errors.js → webex-errors.ts} +1 -20
- package/src/common/errors/{webex-meetings-error.js → webex-meetings-error.ts} +3 -1
- package/src/common/events/{events-scope.js → events-scope.ts} +1 -1
- package/src/common/events/{events.js → events.ts} +0 -0
- package/src/common/events/{trigger-proxy.js → trigger-proxy.ts} +1 -2
- package/src/common/events/{util.js → util.ts} +1 -1
- package/src/common/logs/{logger-config.js → logger-config.ts} +1 -2
- package/src/common/logs/{logger-proxy.js → logger-proxy.ts} +1 -1
- package/src/common/logs/{request.js → request.ts} +12 -2
- package/src/common/queue.ts +1 -2
- package/src/{config.js → config.ts} +2 -0
- package/src/constants.ts +139 -179
- package/src/locus-info/{controlsUtils.js → controlsUtils.ts} +4 -4
- package/src/locus-info/{embeddedAppsUtils.js → embeddedAppsUtils.ts} +5 -6
- package/src/locus-info/{fullState.js → fullState.ts} +1 -1
- package/src/locus-info/{hostUtils.js → hostUtils.ts} +5 -5
- package/src/locus-info/{index.js → index.ts} +67 -32
- package/src/locus-info/{infoUtils.js → infoUtils.ts} +7 -4
- package/src/locus-info/{mediaSharesUtils.js → mediaSharesUtils.ts} +13 -13
- package/src/locus-info/{parser.js → parser.ts} +22 -12
- package/src/locus-info/{selfUtils.js → selfUtils.ts} +17 -19
- package/src/media/{index.js → index.ts} +130 -205
- package/src/media/internal-media-core-wrapper.ts +9 -0
- package/src/media/{properties.js → properties.ts} +35 -29
- package/src/media/util.ts +16 -0
- package/src/mediaQualityMetrics/{config.js → config.ts} +1 -1
- package/src/meeting/{effectsState.js → effectsState.ts} +12 -6
- package/src/meeting/{index.js → index.ts} +993 -474
- package/src/meeting/{muteState.js → muteState.ts} +16 -11
- package/src/meeting/{request.js → request.ts} +148 -36
- package/src/meeting/{state.js → state.ts} +6 -6
- package/src/meeting/{util.js → util.ts} +9 -51
- package/src/meeting-info/{collection.js → collection.ts} +4 -1
- package/src/meeting-info/{index.js → index.ts} +10 -6
- package/src/meeting-info/{meeting-info-v2.js → meeting-info-v2.ts} +28 -10
- package/src/meeting-info/{request.js → request.ts} +6 -2
- package/src/meeting-info/{util.js → util.ts} +6 -5
- package/src/meeting-info/{utilv2.js → utilv2.ts} +8 -7
- package/src/meetings/{collection.js → collection.ts} +5 -2
- package/src/meetings/{index.js → index.ts} +118 -22
- package/src/meetings/{request.js → request.ts} +6 -1
- package/src/meetings/{util.js → util.ts} +28 -5
- package/src/member/{index.js → index.ts} +46 -15
- package/src/member/{util.js → util.ts} +17 -16
- package/src/members/{collection.js → collection.ts} +2 -1
- package/src/members/{index.js → index.ts} +94 -26
- package/src/members/{request.js → request.ts} +16 -5
- package/src/members/{util.js → util.ts} +7 -7
- package/src/metrics/{config.js → config.ts} +0 -2
- package/src/metrics/{constants.js → constants.ts} +0 -0
- package/src/metrics/{index.js → index.ts} +27 -8
- package/src/multistream/mediaRequestManager.ts +166 -0
- package/src/multistream/multistreamMedia.ts +92 -0
- package/src/multistream/receiveSlot.ts +141 -0
- package/src/multistream/receiveSlotManager.ts +142 -0
- package/src/multistream/remoteMedia.ts +228 -0
- package/src/multistream/remoteMediaGroup.ts +224 -0
- package/src/multistream/remoteMediaManager.ts +911 -0
- package/src/networkQualityMonitor/{index.js → index.ts} +18 -3
- package/src/personal-meeting-room/{index.js → index.ts} +17 -4
- package/src/personal-meeting-room/{request.js → request.ts} +3 -1
- package/src/personal-meeting-room/{util.js → util.ts} +1 -1
- package/src/reachability/{index.js → index.ts} +28 -17
- package/src/reachability/request.ts +4 -2
- package/src/reactions/reactions.ts +104 -0
- package/src/reactions/reactions.type.ts +36 -0
- package/src/reconnection-manager/{index.js → index.ts} +81 -65
- package/src/roap/index.ts +229 -0
- package/src/roap/{request.js → request.ts} +15 -74
- package/src/roap/turnDiscovery.ts +26 -11
- package/src/statsAnalyzer/{global.js → global.ts} +2 -0
- package/src/statsAnalyzer/{index.js → index.ts} +66 -61
- package/src/statsAnalyzer/{mqaUtil.js → mqaUtil.ts} +6 -1
- package/src/transcription/{index.js → index.ts} +16 -11
- package/test/integration/spec/journey.js +1 -1
- package/test/integration/spec/space-meeting.js +1 -2
- package/test/unit/spec/locus-info/infoUtils.js +17 -1
- package/test/unit/spec/media/index.ts +207 -0
- package/test/unit/spec/media/properties.ts +73 -82
- package/test/unit/spec/meeting/effectsState.js +1 -3
- package/test/unit/spec/meeting/index.js +672 -245
- package/test/unit/spec/meeting/muteState.js +7 -0
- package/test/unit/spec/meeting/request.js +25 -1
- package/test/unit/spec/meeting/utils.js +63 -2
- package/test/unit/spec/meetings/index.js +0 -4
- package/test/unit/spec/members/index.js +164 -2
- package/test/unit/spec/multistream/mediaRequestManager.ts +515 -0
- package/test/unit/spec/multistream/receiveSlot.ts +104 -0
- package/test/unit/spec/multistream/receiveSlotManager.ts +173 -0
- package/test/unit/spec/multistream/remoteMedia.ts +225 -0
- package/test/unit/spec/multistream/remoteMediaGroup.ts +396 -0
- package/test/unit/spec/multistream/remoteMediaManager.ts +1309 -0
- package/test/unit/spec/reconnection-manager/index.js +68 -2
- package/test/unit/spec/roap/index.ts +63 -35
- package/test/unit/spec/stats-analyzer/index.js +19 -22
- package/dist/peer-connection-manager/index.js +0 -794
- package/dist/peer-connection-manager/index.js.map +0 -1
- package/dist/peer-connection-manager/util.js +0 -124
- package/dist/peer-connection-manager/util.js.map +0 -1
- package/dist/roap/collection.js +0 -73
- package/dist/roap/collection.js.map +0 -1
- package/dist/roap/handler.js +0 -337
- package/dist/roap/handler.js.map +0 -1
- package/dist/roap/state.js +0 -164
- package/dist/roap/state.js.map +0 -1
- package/dist/roap/util.js +0 -102
- package/dist/roap/util.js.map +0 -1
- package/src/media/util.js +0 -38
- package/src/peer-connection-manager/index.js +0 -723
- package/src/peer-connection-manager/util.ts +0 -117
- package/src/roap/collection.js +0 -63
- package/src/roap/handler.js +0 -252
- package/src/roap/index.js +0 -380
- package/src/roap/state.js +0 -149
- package/src/roap/util.js +0 -93
- package/test/unit/spec/peerconnection-manager/index.js +0 -188
- package/test/unit/spec/peerconnection-manager/utils.js +0 -48
- package/test/unit/spec/peerconnection-manager/utils.test-fixtures.ts +0 -389
- package/test/unit/spec/roap/util.js +0 -30
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
} from './config';
|
|
19
19
|
|
|
20
20
|
const OSMap = {
|
|
21
|
+
// @ts-ignore
|
|
21
22
|
'Chrome OS': OS_NAME.chrome,
|
|
22
23
|
macOS: OS_NAME.MAC,
|
|
23
24
|
Windows: OS_NAME.WINDOWS,
|
|
@@ -76,6 +77,13 @@ const triggerTimers = ({event, meeting, data}) => {
|
|
|
76
77
|
* @class Metrics
|
|
77
78
|
*/
|
|
78
79
|
class Metrics {
|
|
80
|
+
static instance: Metrics;
|
|
81
|
+
|
|
82
|
+
_events: any;
|
|
83
|
+
keys: any;
|
|
84
|
+
meetingCollection: any;
|
|
85
|
+
webex: any;
|
|
86
|
+
|
|
79
87
|
/**
|
|
80
88
|
* Create Metrics Object
|
|
81
89
|
* @constructor
|
|
@@ -125,7 +133,7 @@ class Metrics {
|
|
|
125
133
|
*
|
|
126
134
|
* @returns {void}
|
|
127
135
|
*/
|
|
128
|
-
initialSetup(meetingCollection, webex) {
|
|
136
|
+
initialSetup(meetingCollection: object, webex: object) {
|
|
129
137
|
this.meetingCollection = meetingCollection;
|
|
130
138
|
this.webex = webex;
|
|
131
139
|
}
|
|
@@ -139,7 +147,7 @@ class Metrics {
|
|
|
139
147
|
* @param {object} options.event
|
|
140
148
|
* @returns {object} null
|
|
141
149
|
*/
|
|
142
|
-
postEvent(options) {
|
|
150
|
+
postEvent(options: { meeting?: any; meetingId?: string; data?: object; event?: any } | any) {
|
|
143
151
|
const {meetingId, data = {}, event} = options;
|
|
144
152
|
let {meeting} = options;
|
|
145
153
|
|
|
@@ -181,7 +189,7 @@ class Metrics {
|
|
|
181
189
|
*/
|
|
182
190
|
|
|
183
191
|
initPayload(eventType, identifiers, options) {
|
|
184
|
-
const payload = {
|
|
192
|
+
const payload: any = {
|
|
185
193
|
eventId: uuid.v4(),
|
|
186
194
|
version: 1,
|
|
187
195
|
origin: {
|
|
@@ -252,7 +260,7 @@ class Metrics {
|
|
|
252
260
|
* @private
|
|
253
261
|
* @memberof Metrics
|
|
254
262
|
*/
|
|
255
|
-
getOsName() {
|
|
263
|
+
private getOsName() {
|
|
256
264
|
return OSMap[getOSName()] ?? OS_NAME.OTHERS;
|
|
257
265
|
}
|
|
258
266
|
|
|
@@ -270,7 +278,18 @@ class Metrics {
|
|
|
270
278
|
* @public
|
|
271
279
|
* @memberof Metrics
|
|
272
280
|
*/
|
|
273
|
-
initMediaPayload(
|
|
281
|
+
public initMediaPayload(
|
|
282
|
+
eventType: string,
|
|
283
|
+
identifiers: {
|
|
284
|
+
correlationId: string;
|
|
285
|
+
locusUrl: string;
|
|
286
|
+
locusId: string;
|
|
287
|
+
},
|
|
288
|
+
options: {
|
|
289
|
+
intervalData: object;
|
|
290
|
+
clientType: string;
|
|
291
|
+
} | any = {}
|
|
292
|
+
) {
|
|
274
293
|
const {audioSetupDelay, videoSetupDelay, joinTimes} = options;
|
|
275
294
|
|
|
276
295
|
const payload = {
|
|
@@ -329,7 +348,7 @@ class Metrics {
|
|
|
329
348
|
* @returns {{showToUser: boolean, category: string, errorDescription: string,
|
|
330
349
|
* errorCode: number, errorData: *, fatal: boolean, name: string}}
|
|
331
350
|
*/
|
|
332
|
-
parseLocusError(err, showToUser) {
|
|
351
|
+
parseLocusError(err: any, showToUser: boolean) {
|
|
333
352
|
let errorCode;
|
|
334
353
|
|
|
335
354
|
if (err && err.statusCode && err.statusCode >= 500) {
|
|
@@ -447,7 +466,7 @@ class Metrics {
|
|
|
447
466
|
|
|
448
467
|
generateErrorPayload(errorCode, shownToUser, name, err) {
|
|
449
468
|
if (error.errors[errorCode]) {
|
|
450
|
-
const errorPayload = {
|
|
469
|
+
const errorPayload: any = {
|
|
451
470
|
shownToUser: shownToUser || false,
|
|
452
471
|
category: error.errors[errorCode][2],
|
|
453
472
|
errorDescription: error.errors[errorCode][0],
|
|
@@ -515,7 +534,7 @@ class Metrics {
|
|
|
515
534
|
*
|
|
516
535
|
* @returns {void}
|
|
517
536
|
*/
|
|
518
|
-
sendBehavioralMetric(metricName, metricFields = {}, metricTags = {}) {
|
|
537
|
+
sendBehavioralMetric(metricName: string, metricFields: object = {}, metricTags: object = {}) {
|
|
519
538
|
this.webex.internal.metrics.submitClientMetrics(metricName, {
|
|
520
539
|
type: this.webex.config.metrics.type,
|
|
521
540
|
fields: metricFields,
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/* eslint-disable require-jsdoc */
|
|
2
|
+
import {MediaConnection as MC} from '@webex/internal-media-core';
|
|
3
|
+
|
|
4
|
+
import LoggerProxy from '../common/logs/logger-proxy';
|
|
5
|
+
|
|
6
|
+
import {ReceiveSlot, ReceiveSlotId} from './receiveSlot';
|
|
7
|
+
|
|
8
|
+
export interface ActiveSpeakerPolicyInfo {
|
|
9
|
+
policy: 'active-speaker';
|
|
10
|
+
priority: number;
|
|
11
|
+
crossPriorityDuplication: boolean;
|
|
12
|
+
crossPolicyDuplication: boolean;
|
|
13
|
+
preferLiveVideo: boolean;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface ReceiverSelectedPolicyInfo {
|
|
17
|
+
policy: 'receiver-selected';
|
|
18
|
+
csi: number;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export type PolicyInfo = ActiveSpeakerPolicyInfo | ReceiverSelectedPolicyInfo;
|
|
22
|
+
|
|
23
|
+
export interface H264CodecInfo {
|
|
24
|
+
codec: 'h264';
|
|
25
|
+
maxFs?: number;
|
|
26
|
+
maxFps?: number;
|
|
27
|
+
maxMbps?: number;
|
|
28
|
+
maxWidth?: number;
|
|
29
|
+
maxHeight?: number;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export type CodecInfo = H264CodecInfo; // we'll add AV1 here in the future when it's available
|
|
33
|
+
|
|
34
|
+
export interface MediaRequest {
|
|
35
|
+
policyInfo: PolicyInfo;
|
|
36
|
+
receiveSlots: Array<ReceiveSlot>;
|
|
37
|
+
codecInfo?: CodecInfo;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export type MediaRequestId = string;
|
|
41
|
+
|
|
42
|
+
const CODEC_DEFAULTS = {
|
|
43
|
+
h264: {
|
|
44
|
+
maxFs: 8192,
|
|
45
|
+
maxFps: 3000,
|
|
46
|
+
maxMbps: 245760,
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
type SendMediaRequestsCallback = (mediaRequests: MC.MediaRequest[]) => void;
|
|
51
|
+
|
|
52
|
+
export class MediaRequestManager {
|
|
53
|
+
private sendMediaRequestsCallback: SendMediaRequestsCallback;
|
|
54
|
+
|
|
55
|
+
private counter;
|
|
56
|
+
|
|
57
|
+
private clientRequests: {[key: MediaRequestId]: MediaRequest};
|
|
58
|
+
|
|
59
|
+
private slotsActiveInLastMediaRequest: {[key: ReceiveSlotId]: ReceiveSlot};
|
|
60
|
+
|
|
61
|
+
constructor(sendMediaRequestsCallback: SendMediaRequestsCallback) {
|
|
62
|
+
this.sendMediaRequestsCallback = sendMediaRequestsCallback;
|
|
63
|
+
this.counter = 0;
|
|
64
|
+
this.clientRequests = {};
|
|
65
|
+
this.slotsActiveInLastMediaRequest = {};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
private resetInactiveReceiveSlots() {
|
|
69
|
+
const activeSlots: {[key: ReceiveSlotId]: ReceiveSlot} = {};
|
|
70
|
+
|
|
71
|
+
// create a map of all currently used slot ids
|
|
72
|
+
Object.values(this.clientRequests).forEach((request) =>
|
|
73
|
+
request.receiveSlots.forEach((slot) => {
|
|
74
|
+
activeSlots[slot.id] = slot;
|
|
75
|
+
})
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
// when we stop using some receive slots and they are not included in the new media request,
|
|
79
|
+
// we will never get a 'no source' notification for them, so we reset their state,
|
|
80
|
+
// so that the client doesn't try to display their video anymore
|
|
81
|
+
for (const [slotId, slot] of Object.entries(this.slotsActiveInLastMediaRequest)) {
|
|
82
|
+
if (!(slotId in activeSlots)) {
|
|
83
|
+
LoggerProxy.logger.info(
|
|
84
|
+
`multistream:mediaRequestManager --> resetting sourceState to "no source" for slot ${slot.id}`
|
|
85
|
+
);
|
|
86
|
+
slot.resetSourceState();
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
this.slotsActiveInLastMediaRequest = activeSlots;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
private sendRequests() {
|
|
94
|
+
const wcmeMediaRequests: MC.MediaRequest[] = [];
|
|
95
|
+
|
|
96
|
+
// todo: check how many streams we're asking for and what resolution and introduce some limits (spark-377701)
|
|
97
|
+
const maxPayloadBitsPerSecond = 10 * 1000 * 1000;
|
|
98
|
+
|
|
99
|
+
// map all the client media requests to wcme media requests
|
|
100
|
+
Object.values(this.clientRequests).forEach((mr) => {
|
|
101
|
+
wcmeMediaRequests.push(
|
|
102
|
+
new MC.MediaRequest(
|
|
103
|
+
mr.policyInfo.policy === 'active-speaker'
|
|
104
|
+
? MC.Policy.ActiveSpeaker
|
|
105
|
+
: MC.Policy.ReceiverSelected,
|
|
106
|
+
mr.policyInfo.policy === 'active-speaker'
|
|
107
|
+
? new MC.ActiveSpeakerInfo(
|
|
108
|
+
mr.policyInfo.priority,
|
|
109
|
+
mr.policyInfo.crossPriorityDuplication,
|
|
110
|
+
mr.policyInfo.crossPolicyDuplication,
|
|
111
|
+
mr.policyInfo.preferLiveVideo
|
|
112
|
+
)
|
|
113
|
+
: new MC.ReceiverSelectedInfo(mr.policyInfo.csi),
|
|
114
|
+
mr.receiveSlots.map((receiveSlot) => receiveSlot.wcmeReceiveSlot),
|
|
115
|
+
maxPayloadBitsPerSecond,
|
|
116
|
+
mr.codecInfo && [
|
|
117
|
+
new MC.CodecInfo(
|
|
118
|
+
0x80,
|
|
119
|
+
new MC.H264Codec(
|
|
120
|
+
mr.codecInfo.maxFs || CODEC_DEFAULTS.h264.maxFs,
|
|
121
|
+
mr.codecInfo.maxFps || CODEC_DEFAULTS.h264.maxFps,
|
|
122
|
+
mr.codecInfo.maxMbps || CODEC_DEFAULTS.h264.maxMbps,
|
|
123
|
+
mr.codecInfo.maxWidth,
|
|
124
|
+
mr.codecInfo.maxHeight
|
|
125
|
+
)
|
|
126
|
+
),
|
|
127
|
+
]
|
|
128
|
+
)
|
|
129
|
+
);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
this.sendMediaRequestsCallback(wcmeMediaRequests);
|
|
133
|
+
|
|
134
|
+
this.resetInactiveReceiveSlots();
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
public addRequest(mediaRequest: MediaRequest, commit = true): MediaRequestId {
|
|
138
|
+
// eslint-disable-next-line no-plusplus
|
|
139
|
+
const newId = `${this.counter++}`;
|
|
140
|
+
|
|
141
|
+
this.clientRequests[newId] = mediaRequest;
|
|
142
|
+
|
|
143
|
+
if (commit) {
|
|
144
|
+
this.commit();
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return newId;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
public cancelRequest(requestId: MediaRequestId, commit = true) {
|
|
151
|
+
delete this.clientRequests[requestId];
|
|
152
|
+
|
|
153
|
+
if (commit) {
|
|
154
|
+
this.commit();
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
public commit() {
|
|
159
|
+
return this.sendRequests();
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
public reset() {
|
|
163
|
+
this.clientRequests = {};
|
|
164
|
+
this.slotsActiveInLastMediaRequest = {};
|
|
165
|
+
}
|
|
166
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/* eslint-disable import/prefer-default-export */
|
|
2
|
+
|
|
3
|
+
import {AUDIO, VIDEO} from '../constants';
|
|
4
|
+
import createMuteState from '../meeting/muteState';
|
|
5
|
+
import Meeting from '../meeting';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Class wrapping all the multistream specific APIs that need to be exposed at meeting level.
|
|
9
|
+
*
|
|
10
|
+
*/
|
|
11
|
+
export class MultistreamMedia {
|
|
12
|
+
private meeting: Meeting;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Constructor
|
|
16
|
+
* @param {Meeting} meeting
|
|
17
|
+
*/
|
|
18
|
+
constructor(meeting: Meeting) {
|
|
19
|
+
this.meeting = meeting;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* throws if we don't have a media connection created
|
|
24
|
+
*/
|
|
25
|
+
private checkMediaConnection() {
|
|
26
|
+
if (this.meeting?.mediaProperties?.webrtcMediaConnection) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
throw new Error('Webrtc media connection is missing');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Publishes a local track in the meeting
|
|
34
|
+
*
|
|
35
|
+
* @param {MediaStreamTrack} track
|
|
36
|
+
* @returns {Promise}
|
|
37
|
+
*/
|
|
38
|
+
publishTrack(track: MediaStreamTrack): Promise<void> {
|
|
39
|
+
this.checkMediaConnection();
|
|
40
|
+
|
|
41
|
+
/* todo: for now we don't support screen share (waiting for server to support bundling, see SPARK-377812)
|
|
42
|
+
for sharing:
|
|
43
|
+
we have to call meeting.stopFloorRequest() before unpublishing a track
|
|
44
|
+
we have to call meeting.share() after publishing a track
|
|
45
|
+
*/
|
|
46
|
+
|
|
47
|
+
// todo: depending on how muting is done with Local tracks, this code here might need to change...
|
|
48
|
+
|
|
49
|
+
if (track.kind === 'audio') {
|
|
50
|
+
this.meeting.setLocalAudioTrack(track);
|
|
51
|
+
this.meeting.mediaProperties.mediaDirection.sendAudio = true;
|
|
52
|
+
|
|
53
|
+
// audio state could be undefined if you have not sent audio before
|
|
54
|
+
this.meeting.audio =
|
|
55
|
+
this.meeting.audio ||
|
|
56
|
+
createMuteState(AUDIO, this.meeting, this.meeting.mediaProperties.mediaDirection);
|
|
57
|
+
} else if (track.kind === 'video') {
|
|
58
|
+
this.meeting.setLocalVideoTrack(track);
|
|
59
|
+
this.meeting.mediaProperties.mediaDirection.sendVideo = true;
|
|
60
|
+
|
|
61
|
+
// video state could be undefined if you have not sent video before
|
|
62
|
+
this.meeting.video =
|
|
63
|
+
this.meeting.video ||
|
|
64
|
+
createMuteState(VIDEO, this.meeting, this.meeting.mediaProperties.mediaDirection);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return this.meeting.mediaProperties.webrtcMediaConnection.publishTrack(track);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Unpublishes a local track in the meeting
|
|
72
|
+
*
|
|
73
|
+
* @param {MediaStreamTrack} track
|
|
74
|
+
* @returns {Promise}
|
|
75
|
+
*/
|
|
76
|
+
unpublishTrack(track: MediaStreamTrack): Promise<void> {
|
|
77
|
+
// todo: see todos in publishTrack() - they all apply here too:
|
|
78
|
+
// screen sharing - SPARK-377812
|
|
79
|
+
// muting etc
|
|
80
|
+
|
|
81
|
+
if (track.kind === 'audio') {
|
|
82
|
+
this.meeting.setLocalVideoTrack(null);
|
|
83
|
+
this.meeting.mediaProperties.mediaDirection.sendAudio = false;
|
|
84
|
+
} else if (track.kind === 'video') {
|
|
85
|
+
this.meeting.setLocalAudioTrack(null);
|
|
86
|
+
this.meeting.mediaProperties.mediaDirection.sendVideo = false;
|
|
87
|
+
}
|
|
88
|
+
this.checkMediaConnection();
|
|
89
|
+
|
|
90
|
+
return this.meeting.mediaProperties.webrtcMediaConnection.unpublishTrack(track);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import {MediaConnection as MC} from '@webex/internal-media-core';
|
|
2
|
+
|
|
3
|
+
import LoggerProxy from '../common/logs/logger-proxy';
|
|
4
|
+
import EventsScope from '../common/events/events-scope';
|
|
5
|
+
|
|
6
|
+
export const ReceiveSlotEvents = {
|
|
7
|
+
SourceUpdate: 'sourceUpdate',
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export type SourceState = MC.SourceState;
|
|
11
|
+
export type CSI = number;
|
|
12
|
+
export type MemberId = string;
|
|
13
|
+
export type ReceiveSlotId = string;
|
|
14
|
+
|
|
15
|
+
let receiveSlotCounter = 0;
|
|
16
|
+
|
|
17
|
+
export type FindMemberIdCallback = (csi: CSI) => MemberId | undefined;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Class representing a receive slot. A single receive slot is able to receive a single track
|
|
21
|
+
* for example some participant's main video or audio
|
|
22
|
+
*/
|
|
23
|
+
export class ReceiveSlot extends EventsScope {
|
|
24
|
+
private readonly mcReceiveSlot: MC.ReceiveSlot;
|
|
25
|
+
|
|
26
|
+
private readonly findMemberIdCallback: FindMemberIdCallback;
|
|
27
|
+
|
|
28
|
+
public readonly id: ReceiveSlotId;
|
|
29
|
+
|
|
30
|
+
public readonly mediaType: MC.MediaType;
|
|
31
|
+
|
|
32
|
+
#memberId?: MemberId;
|
|
33
|
+
|
|
34
|
+
#csi?: CSI;
|
|
35
|
+
|
|
36
|
+
#sourceState: MC.SourceState;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* constructor - don't use it directly, you should always use meeting.receiveSlotManager.allocateSlot()
|
|
40
|
+
* to create any receive slots
|
|
41
|
+
*
|
|
42
|
+
* @param {MC.MediaType} mediaType
|
|
43
|
+
* @param {MC.ReceiveSlot} mcReceiveSlot
|
|
44
|
+
* @param {FindMemberIdCallback} findMemberIdCallback callback for finding memberId for given CSI
|
|
45
|
+
*/
|
|
46
|
+
constructor(
|
|
47
|
+
mediaType: MC.MediaType,
|
|
48
|
+
mcReceiveSlot: MC.ReceiveSlot,
|
|
49
|
+
findMemberIdCallback: FindMemberIdCallback
|
|
50
|
+
) {
|
|
51
|
+
super();
|
|
52
|
+
|
|
53
|
+
receiveSlotCounter += 1;
|
|
54
|
+
|
|
55
|
+
this.findMemberIdCallback = findMemberIdCallback;
|
|
56
|
+
this.mediaType = mediaType;
|
|
57
|
+
this.mcReceiveSlot = mcReceiveSlot;
|
|
58
|
+
this.#sourceState = 'no source';
|
|
59
|
+
this.id = `r${receiveSlotCounter}`;
|
|
60
|
+
|
|
61
|
+
this.setupEventListeners();
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Getter for memberId
|
|
66
|
+
*/
|
|
67
|
+
public get memberId() {
|
|
68
|
+
return this.#memberId;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Getter for csi
|
|
73
|
+
*/
|
|
74
|
+
public get csi() {
|
|
75
|
+
return this.#csi;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Getter for sourceState
|
|
80
|
+
*/
|
|
81
|
+
public get sourceState() {
|
|
82
|
+
return this.#sourceState;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* registers event handlers with the underlying ReceiveSlot
|
|
87
|
+
*/
|
|
88
|
+
setupEventListeners() {
|
|
89
|
+
const scope = {
|
|
90
|
+
file: 'meeting/receiveSlot',
|
|
91
|
+
function: 'setupEventListeners',
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
this.mcReceiveSlot.on(
|
|
95
|
+
MC.ReceiveSlotEvents.SourceUpdate,
|
|
96
|
+
(state: MC.SourceState, csi?: number) => {
|
|
97
|
+
LoggerProxy.logger.log(
|
|
98
|
+
`ReceiveSlot#setupEventListeners --> got source update on receive slot ${this.id}, mediaType=${this.mediaType}, csi=${csi}, state=${state}`
|
|
99
|
+
);
|
|
100
|
+
this.#memberId = csi ? this.findMemberIdCallback(csi) : undefined;
|
|
101
|
+
this.#csi = csi;
|
|
102
|
+
this.#sourceState = state;
|
|
103
|
+
|
|
104
|
+
this.emit(scope, ReceiveSlotEvents.SourceUpdate, {
|
|
105
|
+
state: this.#sourceState,
|
|
106
|
+
csi: this.#csi,
|
|
107
|
+
memberId: this.#memberId,
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* The MediaStream object associated with this slot.
|
|
115
|
+
*
|
|
116
|
+
* @returns {MediaStream} The MediaStreamTrack.
|
|
117
|
+
*/
|
|
118
|
+
get stream(): MediaStream {
|
|
119
|
+
return this.mcReceiveSlot.stream;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* The underlying WCME receive slot
|
|
124
|
+
*/
|
|
125
|
+
get wcmeReceiveSlot(): MC.ReceiveSlot {
|
|
126
|
+
return this.mcReceiveSlot;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Resets the source state to the default 'no source' value.
|
|
131
|
+
* This function should be called on receive slots that are
|
|
132
|
+
* no longer part of a media request. It's needed because WCME
|
|
133
|
+
* does not send any more events on such slots, so the sourceState
|
|
134
|
+
* value would not represent the truth anymore.
|
|
135
|
+
*/
|
|
136
|
+
public resetSourceState() {
|
|
137
|
+
this.#sourceState = 'no source';
|
|
138
|
+
this.#csi = undefined;
|
|
139
|
+
this.#memberId = undefined;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/* eslint-disable import/prefer-default-export */
|
|
2
|
+
import {MediaConnection as MC} from '@webex/internal-media-core';
|
|
3
|
+
|
|
4
|
+
import LoggerProxy from '../common/logs/logger-proxy';
|
|
5
|
+
import Meeting from '../meeting';
|
|
6
|
+
|
|
7
|
+
import {CSI, ReceiveSlot} from './receiveSlot';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Manages all receive slots used by a meeting. WMCE receive slots cannot be ever deleted,
|
|
11
|
+
* so this manager has a pool in order to re-use the slots that were released earlier.
|
|
12
|
+
*/
|
|
13
|
+
export class ReceiveSlotManager {
|
|
14
|
+
private allocatedSlots: {[key in MC.MediaType]: ReceiveSlot[]};
|
|
15
|
+
|
|
16
|
+
private freeSlots: {[key in MC.MediaType]: ReceiveSlot[]};
|
|
17
|
+
|
|
18
|
+
private meeting: Meeting;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Constructor
|
|
22
|
+
* @param {Meeting} meeting
|
|
23
|
+
*/
|
|
24
|
+
constructor(meeting) {
|
|
25
|
+
this.allocatedSlots = {
|
|
26
|
+
[MC.MediaType.AudioMain]: [],
|
|
27
|
+
[MC.MediaType.VideoMain]: [],
|
|
28
|
+
[MC.MediaType.AudioSlides]: [],
|
|
29
|
+
[MC.MediaType.VideoSlides]: [],
|
|
30
|
+
};
|
|
31
|
+
this.freeSlots = {
|
|
32
|
+
[MC.MediaType.AudioMain]: [],
|
|
33
|
+
[MC.MediaType.VideoMain]: [],
|
|
34
|
+
[MC.MediaType.AudioSlides]: [],
|
|
35
|
+
[MC.MediaType.VideoSlides]: [],
|
|
36
|
+
};
|
|
37
|
+
this.meeting = meeting;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Creates a new receive slot or returns one from the existing pool of free slots
|
|
42
|
+
*
|
|
43
|
+
* @param {MC.MediaType} mediaType
|
|
44
|
+
* @returns {Promise<ReceiveSlot>}
|
|
45
|
+
*/
|
|
46
|
+
async allocateSlot(mediaType: MC.MediaType): Promise<ReceiveSlot> {
|
|
47
|
+
if (!this.meeting?.mediaProperties?.webrtcMediaConnection) {
|
|
48
|
+
return Promise.reject(new Error('Webrtc media connection is missing'));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// try to use one of the free ones
|
|
52
|
+
const availableSlot = this.freeSlots[mediaType].pop();
|
|
53
|
+
|
|
54
|
+
if (availableSlot) {
|
|
55
|
+
this.allocatedSlots[mediaType].push(availableSlot);
|
|
56
|
+
|
|
57
|
+
LoggerProxy.logger.log(`receive slot re-used: ${availableSlot.id}`);
|
|
58
|
+
|
|
59
|
+
return availableSlot;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// we have to create a new one
|
|
63
|
+
const wcmeReceiveSlot =
|
|
64
|
+
await this.meeting.mediaProperties.webrtcMediaConnection.createReceiveSlot(mediaType);
|
|
65
|
+
|
|
66
|
+
const receiveSlot = new ReceiveSlot(
|
|
67
|
+
mediaType,
|
|
68
|
+
wcmeReceiveSlot,
|
|
69
|
+
(csi: CSI) => this.meeting.members.findMemberByCsi(csi)?.id
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
this.allocatedSlots[mediaType].push(receiveSlot);
|
|
73
|
+
LoggerProxy.logger.log(`new receive slot allocated: ${receiveSlot.id}`);
|
|
74
|
+
|
|
75
|
+
return receiveSlot;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Releases the slot back to the pool so it can be re-used by others in the future
|
|
80
|
+
* @param {ReceiveSlot} slot
|
|
81
|
+
*/
|
|
82
|
+
releaseSlot(slot: ReceiveSlot) {
|
|
83
|
+
const idx = this.allocatedSlots[slot.mediaType].findIndex(
|
|
84
|
+
(allocatedSlot) => allocatedSlot === slot
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
if (idx >= 0) {
|
|
88
|
+
this.allocatedSlots[slot.mediaType].splice(idx, 1);
|
|
89
|
+
this.freeSlots[slot.mediaType].push(slot);
|
|
90
|
+
LoggerProxy.logger.log(`receive slot released: ${slot.id}`);
|
|
91
|
+
} else {
|
|
92
|
+
LoggerProxy.logger.warn(
|
|
93
|
+
'ReceiveSlotManager#releaseSlot --> trying to release a slot that is not managed by this ReceiveSlotManager'
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Resets the slot manager - this method should be called when the media connection is torn down
|
|
100
|
+
*/
|
|
101
|
+
reset() {
|
|
102
|
+
this.allocatedSlots = {
|
|
103
|
+
[MC.MediaType.AudioMain]: [],
|
|
104
|
+
[MC.MediaType.VideoMain]: [],
|
|
105
|
+
[MC.MediaType.AudioSlides]: [],
|
|
106
|
+
[MC.MediaType.VideoSlides]: [],
|
|
107
|
+
};
|
|
108
|
+
this.freeSlots = {
|
|
109
|
+
[MC.MediaType.AudioMain]: [],
|
|
110
|
+
[MC.MediaType.VideoMain]: [],
|
|
111
|
+
[MC.MediaType.AudioSlides]: [],
|
|
112
|
+
[MC.MediaType.VideoSlides]: [],
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Returns statistics about the managed slots
|
|
118
|
+
*
|
|
119
|
+
* @returns {Object}
|
|
120
|
+
*/
|
|
121
|
+
getStats() {
|
|
122
|
+
const numAllocatedSlots = {};
|
|
123
|
+
const numFreeSlots = {};
|
|
124
|
+
|
|
125
|
+
Object.keys(this.allocatedSlots).forEach((key) => {
|
|
126
|
+
if (this.allocatedSlots[key].length > 0) {
|
|
127
|
+
numAllocatedSlots[key] = this.allocatedSlots[key].length;
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
Object.keys(this.freeSlots).forEach((key) => {
|
|
132
|
+
if (this.freeSlots[key].length > 0) {
|
|
133
|
+
numFreeSlots[key] = this.freeSlots[key].length;
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
return {
|
|
138
|
+
numAllocatedSlots,
|
|
139
|
+
numFreeSlots,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
}
|