@webex/plugin-meetings 3.0.0-beta.16 → 3.0.0-beta.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/breakouts/breakout.js +116 -0
- package/dist/breakouts/breakout.js.map +1 -0
- package/dist/breakouts/collection.js +23 -0
- package/dist/breakouts/collection.js.map +1 -0
- package/dist/breakouts/index.js +226 -0
- package/dist/breakouts/index.js.map +1 -0
- package/dist/config.js +4 -1
- package/dist/config.js.map +1 -1
- package/dist/constants.js +43 -6
- package/dist/constants.js.map +1 -1
- package/dist/locus-info/controlsUtils.js +2 -1
- package/dist/locus-info/controlsUtils.js.map +1 -1
- package/dist/locus-info/index.js +48 -0
- package/dist/locus-info/index.js.map +1 -1
- package/dist/locus-info/parser.js +1 -0
- package/dist/locus-info/parser.js.map +1 -1
- package/dist/locus-info/selfUtils.js +19 -11
- package/dist/locus-info/selfUtils.js.map +1 -1
- package/dist/media/index.js +3 -3
- package/dist/media/index.js.map +1 -1
- package/dist/media/properties.js +4 -4
- package/dist/media/properties.js.map +1 -1
- package/dist/meeting/in-meeting-actions.js +5 -1
- package/dist/meeting/in-meeting-actions.js.map +1 -1
- package/dist/meeting/index.js +652 -459
- package/dist/meeting/index.js.map +1 -1
- package/dist/meeting/request.js +25 -44
- package/dist/meeting/request.js.map +1 -1
- package/dist/meeting/request.type.js.map +1 -1
- package/dist/meeting/util.js +22 -57
- package/dist/meeting/util.js.map +1 -1
- package/dist/meeting-info/meeting-info-v2.js +2 -0
- package/dist/meeting-info/meeting-info-v2.js.map +1 -1
- package/dist/meetings/index.js +28 -18
- package/dist/meetings/index.js.map +1 -1
- package/dist/meetings/request.js +14 -12
- package/dist/meetings/request.js.map +1 -1
- package/dist/member/index.js +9 -0
- package/dist/member/index.js.map +1 -1
- package/dist/member/util.js +14 -1
- package/dist/member/util.js.map +1 -1
- package/dist/members/index.js +8 -6
- package/dist/members/index.js.map +1 -1
- package/dist/members/request.js +3 -1
- package/dist/members/request.js.map +1 -1
- package/dist/multistream/mediaRequestManager.js +46 -6
- package/dist/multistream/mediaRequestManager.js.map +1 -1
- package/dist/multistream/multistreamMedia.js +4 -0
- package/dist/multistream/multistreamMedia.js.map +1 -1
- package/dist/multistream/receiveSlot.js +3 -3
- package/dist/multistream/receiveSlot.js.map +1 -1
- package/dist/multistream/receiveSlotManager.js +8 -6
- package/dist/multistream/receiveSlotManager.js.map +1 -1
- package/dist/multistream/remoteMedia.js.map +1 -1
- package/dist/multistream/remoteMediaGroup.js.map +1 -1
- package/dist/multistream/remoteMediaManager.js +168 -63
- package/dist/multistream/remoteMediaManager.js.map +1 -1
- package/dist/reachability/index.js +63 -51
- package/dist/reachability/index.js.map +1 -1
- package/dist/reactions/constants.js +13 -0
- package/dist/reactions/constants.js.map +1 -0
- package/dist/reactions/reactions.type.js.map +1 -1
- package/dist/reconnection-manager/index.js +25 -12
- package/dist/reconnection-manager/index.js.map +1 -1
- package/dist/recording-controller/enums.js +17 -0
- package/dist/recording-controller/enums.js.map +1 -0
- package/dist/recording-controller/index.js +343 -0
- package/dist/recording-controller/index.js.map +1 -0
- package/dist/recording-controller/util.js +63 -0
- package/dist/recording-controller/util.js.map +1 -0
- package/dist/roap/request.js +88 -68
- package/dist/roap/request.js.map +1 -1
- package/dist/roap/turnDiscovery.js +72 -47
- package/dist/roap/turnDiscovery.js.map +1 -1
- package/dist/statsAnalyzer/index.js +3 -3
- package/dist/statsAnalyzer/index.js.map +1 -1
- package/dist/statsAnalyzer/mqaUtil.js +18 -6
- package/dist/statsAnalyzer/mqaUtil.js.map +1 -1
- package/package.json +24 -19
- package/src/breakouts/README.md +190 -0
- package/src/breakouts/breakout.ts +110 -0
- package/src/breakouts/collection.ts +19 -0
- package/src/breakouts/index.ts +225 -0
- package/src/config.ts +4 -1
- package/src/constants.ts +39 -1
- package/src/locus-info/controlsUtils.ts +2 -0
- package/src/locus-info/index.ts +59 -1
- package/src/locus-info/parser.ts +1 -0
- package/src/locus-info/selfUtils.ts +8 -0
- package/src/media/index.ts +1 -2
- package/src/media/properties.ts +6 -9
- package/src/meeting/in-meeting-actions.ts +8 -0
- package/src/meeting/index.ts +360 -111
- package/src/meeting/request.ts +9 -31
- package/src/meeting/request.type.ts +2 -0
- package/src/meeting/util.ts +25 -60
- package/src/meeting-info/meeting-info-v2.ts +2 -0
- package/src/meetings/index.ts +10 -5
- package/src/meetings/request.ts +1 -1
- package/src/member/index.ts +9 -0
- package/src/member/util.ts +14 -1
- package/src/members/index.ts +1 -0
- package/src/members/request.ts +1 -0
- package/src/multistream/mediaRequestManager.ts +79 -15
- package/src/multistream/multistreamMedia.ts +4 -0
- package/src/multistream/receiveSlot.ts +17 -12
- package/src/multistream/receiveSlotManager.ts +22 -21
- package/src/multistream/remoteMedia.ts +1 -1
- package/src/multistream/remoteMediaGroup.ts +2 -2
- package/src/multistream/remoteMediaManager.ts +150 -37
- package/src/reachability/index.ts +16 -13
- package/src/reactions/constants.ts +4 -0
- package/src/reactions/reactions.type.ts +25 -0
- package/src/reconnection-manager/index.ts +18 -9
- package/src/recording-controller/enums.ts +8 -0
- package/src/recording-controller/index.ts +315 -0
- package/src/recording-controller/util.ts +58 -0
- package/src/roap/request.ts +78 -73
- package/src/roap/turnDiscovery.ts +8 -6
- package/src/statsAnalyzer/index.ts +4 -4
- package/src/statsAnalyzer/mqaUtil.ts +6 -0
- package/test/unit/spec/breakouts/breakout.ts +119 -0
- package/test/unit/spec/breakouts/collection.ts +15 -0
- package/test/unit/spec/breakouts/index.ts +293 -0
- package/test/unit/spec/locus-info/controlsUtils.js +20 -0
- package/test/unit/spec/locus-info/index.js +103 -0
- package/test/unit/spec/locus-info/selfConstant.js +25 -0
- package/test/unit/spec/locus-info/selfUtils.js +84 -0
- package/test/unit/spec/media/index.ts +1 -1
- package/test/unit/spec/media/properties.ts +9 -9
- package/test/unit/spec/meeting/effectsState.js +5 -1
- package/test/unit/spec/meeting/in-meeting-actions.ts +5 -1
- package/test/unit/spec/meeting/index.js +241 -50
- package/test/unit/spec/meeting/request.js +17 -0
- package/test/unit/spec/meeting/utils.js +28 -122
- package/test/unit/spec/meetings/index.js +1 -0
- package/test/unit/spec/member/util.js +26 -1
- package/test/unit/spec/multistream/mediaRequestManager.ts +312 -50
- package/test/unit/spec/multistream/receiveSlot.ts +6 -6
- package/test/unit/spec/multistream/receiveSlotManager.ts +13 -13
- package/test/unit/spec/multistream/remoteMedia.ts +2 -2
- package/test/unit/spec/multistream/remoteMediaGroup.ts +5 -5
- package/test/unit/spec/multistream/remoteMediaManager.ts +354 -65
- package/test/unit/spec/reachability/index.ts +58 -24
- package/test/unit/spec/reconnection-manager/index.js +42 -13
- package/test/unit/spec/recording-controller/index.js +231 -0
- package/test/unit/spec/recording-controller/util.js +102 -0
- package/test/unit/spec/roap/index.ts +2 -1
- package/test/unit/spec/roap/request.ts +114 -0
- package/test/unit/spec/roap/turnDiscovery.ts +45 -29
- package/test/unit/spec/stats-analyzer/index.js +2 -2
- package/test/utils/webex-test-users.js +1 -0
- package/tsconfig.json +6 -0
- package/dist/media/internal-media-core-wrapper.js +0 -18
- package/dist/media/internal-media-core-wrapper.js.map +0 -1
- package/src/media/internal-media-core-wrapper.ts +0 -9
package/src/meeting/request.ts
CHANGED
|
@@ -9,6 +9,7 @@ import LoggerProxy from '../common/logs/logger-proxy';
|
|
|
9
9
|
import {
|
|
10
10
|
ALERT,
|
|
11
11
|
ALTERNATE_REDIRECT_TRUE,
|
|
12
|
+
BREAKOUTS,
|
|
12
13
|
CALL,
|
|
13
14
|
CONTROLS,
|
|
14
15
|
DECLINE,
|
|
@@ -55,6 +56,7 @@ export default class MeetingRequest extends StatelessWebexPlugin {
|
|
|
55
56
|
* @param {boolean} options.pin
|
|
56
57
|
* @param {boolean} options.moveToResource
|
|
57
58
|
* @param {Object} options.roapMessage
|
|
59
|
+
* @param {boolean} options.breakoutsSupported
|
|
58
60
|
* @returns {Promise}
|
|
59
61
|
*/
|
|
60
62
|
async joinMeeting(options: {
|
|
@@ -73,6 +75,7 @@ export default class MeetingRequest extends StatelessWebexPlugin {
|
|
|
73
75
|
meetingNumber: any;
|
|
74
76
|
permissionToken: any;
|
|
75
77
|
preferTranscoding: any;
|
|
78
|
+
breakoutsSupported: boolean;
|
|
76
79
|
}) {
|
|
77
80
|
const {
|
|
78
81
|
asResourceOccupant,
|
|
@@ -89,6 +92,7 @@ export default class MeetingRequest extends StatelessWebexPlugin {
|
|
|
89
92
|
moveToResource,
|
|
90
93
|
roapMessage,
|
|
91
94
|
preferTranscoding,
|
|
95
|
+
breakoutsSupported,
|
|
92
96
|
} = options;
|
|
93
97
|
|
|
94
98
|
LoggerProxy.logger.info('Meeting:request#joinMeeting --> Joining a meeting', correlationId);
|
|
@@ -114,6 +118,10 @@ export default class MeetingRequest extends StatelessWebexPlugin {
|
|
|
114
118
|
},
|
|
115
119
|
};
|
|
116
120
|
|
|
121
|
+
if (breakoutsSupported) {
|
|
122
|
+
body.deviceCapabilities = [BREAKOUTS.BREAKOUTS_SUPPORTED];
|
|
123
|
+
}
|
|
124
|
+
|
|
117
125
|
// @ts-ignore
|
|
118
126
|
if (this.webex.meetings.clientRegion) {
|
|
119
127
|
// @ts-ignore
|
|
@@ -497,37 +505,6 @@ export default class MeetingRequest extends StatelessWebexPlugin {
|
|
|
497
505
|
});
|
|
498
506
|
}
|
|
499
507
|
|
|
500
|
-
/**
|
|
501
|
-
* Make a network request to acknowledge a meeting
|
|
502
|
-
* @param {Object} options
|
|
503
|
-
* @param {String} options.locusUrl
|
|
504
|
-
* @param {String} options.deviceUrl
|
|
505
|
-
* @param {String} options.id
|
|
506
|
-
* @returns {Promise}
|
|
507
|
-
*/
|
|
508
|
-
recordMeeting(options: {
|
|
509
|
-
locusUrl: string;
|
|
510
|
-
deviceUrl: string;
|
|
511
|
-
id: string;
|
|
512
|
-
recording: any;
|
|
513
|
-
paused: any;
|
|
514
|
-
}) {
|
|
515
|
-
const uri = `${options.locusUrl}/${CONTROLS}`;
|
|
516
|
-
const body = {
|
|
517
|
-
record: {
|
|
518
|
-
recording: options.recording,
|
|
519
|
-
paused: options.paused,
|
|
520
|
-
},
|
|
521
|
-
};
|
|
522
|
-
|
|
523
|
-
// @ts-ignore
|
|
524
|
-
return this.request({
|
|
525
|
-
method: HTTP_VERBS.PATCH,
|
|
526
|
-
uri,
|
|
527
|
-
body,
|
|
528
|
-
});
|
|
529
|
-
}
|
|
530
|
-
|
|
531
508
|
lockMeeting(options) {
|
|
532
509
|
const uri = `${options.locusUrl}/${CONTROLS}`;
|
|
533
510
|
const body = {
|
|
@@ -842,6 +819,7 @@ export default class MeetingRequest extends StatelessWebexPlugin {
|
|
|
842
819
|
toggleReactions({enable, locusUrl, requestingParticipantId}: ToggleReactionsOptions) {
|
|
843
820
|
const uri = `${locusUrl}/${CONTROLS}`;
|
|
844
821
|
|
|
822
|
+
// @ts-ignore
|
|
845
823
|
return this.request({
|
|
846
824
|
method: HTTP_VERBS.PUT,
|
|
847
825
|
uri,
|
package/src/meeting/util.ts
CHANGED
|
@@ -105,6 +105,7 @@ MeetingUtil.joinMeeting = (meeting, options) => {
|
|
|
105
105
|
moveToResource: options.moveToResource,
|
|
106
106
|
preferTranscoding: !meeting.isMultistream,
|
|
107
107
|
asResourceOccupant: options.asResourceOccupant,
|
|
108
|
+
breakoutsSupported: options.breakoutsSupported,
|
|
108
109
|
})
|
|
109
110
|
.then((res) => {
|
|
110
111
|
Metrics.postEvent({
|
|
@@ -123,6 +124,8 @@ MeetingUtil.joinMeeting = (meeting, options) => {
|
|
|
123
124
|
};
|
|
124
125
|
|
|
125
126
|
MeetingUtil.cleanUp = (meeting) => {
|
|
127
|
+
meeting.breakouts.cleanUp();
|
|
128
|
+
|
|
126
129
|
// make sure we send last metrics before we close the peerconnection
|
|
127
130
|
const stopStatsAnalyzer = meeting.statsAnalyzer
|
|
128
131
|
? meeting.statsAnalyzer.stopAnalyzer()
|
|
@@ -359,66 +362,6 @@ MeetingUtil.canUserUnlock = (displayHints) =>
|
|
|
359
362
|
displayHints.includes(DISPLAY_HINTS.LOCK_CONTROL_UNLOCK) &&
|
|
360
363
|
displayHints.includes(DISPLAY_HINTS.LOCK_STATUS_LOCKED);
|
|
361
364
|
|
|
362
|
-
MeetingUtil.canUserRecord = (displayHints) =>
|
|
363
|
-
displayHints.includes(DISPLAY_HINTS.RECORDING_CONTROL_START);
|
|
364
|
-
|
|
365
|
-
MeetingUtil.canUserPause = (displayHints) =>
|
|
366
|
-
displayHints.includes(DISPLAY_HINTS.RECORDING_CONTROL_PAUSE);
|
|
367
|
-
|
|
368
|
-
MeetingUtil.canUserResume = (displayHints) =>
|
|
369
|
-
displayHints.includes(DISPLAY_HINTS.RECORDING_CONTROL_RESUME);
|
|
370
|
-
|
|
371
|
-
MeetingUtil.canUserStop = (displayHints) =>
|
|
372
|
-
displayHints.includes(DISPLAY_HINTS.RECORDING_CONTROL_STOP);
|
|
373
|
-
|
|
374
|
-
MeetingUtil.startRecording = (request, locusUrl, locusInfo) => {
|
|
375
|
-
const displayHints = MeetingUtil.getUserDisplayHintsFromLocusInfo(locusInfo);
|
|
376
|
-
|
|
377
|
-
if (MeetingUtil.canUserRecord(displayHints)) {
|
|
378
|
-
return request.recordMeeting({locusUrl, recording: true, paused: false});
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
return Promise.reject(
|
|
382
|
-
new PermissionError('Start recording not allowed, due to moderator property.')
|
|
383
|
-
);
|
|
384
|
-
};
|
|
385
|
-
|
|
386
|
-
MeetingUtil.pauseRecording = (request, locusUrl, locusInfo) => {
|
|
387
|
-
const displayHints = MeetingUtil.getUserDisplayHintsFromLocusInfo(locusInfo);
|
|
388
|
-
|
|
389
|
-
if (MeetingUtil.canUserPause(displayHints)) {
|
|
390
|
-
return request.recordMeeting({locusUrl, recording: true, paused: true});
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
return Promise.reject(
|
|
394
|
-
new PermissionError('Pause recording not allowed, due to moderator property.')
|
|
395
|
-
);
|
|
396
|
-
};
|
|
397
|
-
|
|
398
|
-
MeetingUtil.resumeRecording = (request, locusUrl, locusInfo) => {
|
|
399
|
-
const displayHints = MeetingUtil.getUserDisplayHintsFromLocusInfo(locusInfo);
|
|
400
|
-
|
|
401
|
-
if (MeetingUtil.canUserResume(displayHints)) {
|
|
402
|
-
return request.recordMeeting({locusUrl, recording: true, paused: false});
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
return Promise.reject(
|
|
406
|
-
new PermissionError('Resume recording not allowed, due to moderator property.')
|
|
407
|
-
);
|
|
408
|
-
};
|
|
409
|
-
|
|
410
|
-
MeetingUtil.stopRecording = (request, locusUrl, locusInfo) => {
|
|
411
|
-
const displayHints = MeetingUtil.getUserDisplayHintsFromLocusInfo(locusInfo);
|
|
412
|
-
|
|
413
|
-
if (MeetingUtil.canUserStop(displayHints)) {
|
|
414
|
-
return request.recordMeeting({locusUrl, recording: false, paused: false});
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
return Promise.reject(
|
|
418
|
-
new PermissionError('Stop recording not allowed, due to moderator property.')
|
|
419
|
-
);
|
|
420
|
-
};
|
|
421
|
-
|
|
422
365
|
MeetingUtil.canUserRaiseHand = (displayHints) => displayHints.includes(DISPLAY_HINTS.RAISE_HAND);
|
|
423
366
|
|
|
424
367
|
MeetingUtil.canUserLowerAllHands = (displayHints) =>
|
|
@@ -535,4 +478,26 @@ MeetingUtil.canSelectSpokenLanguages = (displayHints) =>
|
|
|
535
478
|
MeetingUtil.waitingForOthersToJoin = (displayHints) =>
|
|
536
479
|
displayHints.includes(DISPLAY_HINTS.WAITING_FOR_OTHERS);
|
|
537
480
|
|
|
481
|
+
MeetingUtil.canEnableReactions = (originalValue, displayHints) => {
|
|
482
|
+
if (displayHints.includes(DISPLAY_HINTS.ENABLE_REACTIONS)) {
|
|
483
|
+
return true;
|
|
484
|
+
}
|
|
485
|
+
if (displayHints.includes(DISPLAY_HINTS.DISABLE_REACTIONS)) {
|
|
486
|
+
return false;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
return originalValue;
|
|
490
|
+
};
|
|
491
|
+
|
|
492
|
+
MeetingUtil.canSendReactions = (originalValue, displayHints) => {
|
|
493
|
+
if (displayHints.includes(DISPLAY_HINTS.REACTIONS_ACTIVE)) {
|
|
494
|
+
return true;
|
|
495
|
+
}
|
|
496
|
+
if (displayHints.includes(DISPLAY_HINTS.REACTIONS_INACTIVE)) {
|
|
497
|
+
return false;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
return originalValue;
|
|
501
|
+
};
|
|
502
|
+
|
|
538
503
|
export default MeetingUtil;
|
|
@@ -19,6 +19,7 @@ export class MeetingInfoV2PasswordError extends Error {
|
|
|
19
19
|
meetingInfo: any;
|
|
20
20
|
sdkMessage: any;
|
|
21
21
|
wbxAppApiCode: any;
|
|
22
|
+
body: any;
|
|
22
23
|
|
|
23
24
|
/**
|
|
24
25
|
*
|
|
@@ -70,6 +71,7 @@ export class MeetingInfoV2CaptchaError extends Error {
|
|
|
70
71
|
isPasswordRequired: any;
|
|
71
72
|
sdkMessage: any;
|
|
72
73
|
wbxAppApiCode: any;
|
|
74
|
+
body: any;
|
|
73
75
|
/**
|
|
74
76
|
*
|
|
75
77
|
* @constructor
|
package/src/meetings/index.ts
CHANGED
|
@@ -4,7 +4,7 @@ import '@webex/internal-plugin-mercury';
|
|
|
4
4
|
import '@webex/internal-plugin-conversation';
|
|
5
5
|
// @ts-ignore
|
|
6
6
|
import {WebexPlugin} from '@webex/webex-core';
|
|
7
|
-
import {
|
|
7
|
+
import {setLogger} from '@webex/internal-media-core';
|
|
8
8
|
|
|
9
9
|
import 'webrtc-adapter';
|
|
10
10
|
|
|
@@ -249,6 +249,7 @@ export default class Meetings extends WebexPlugin {
|
|
|
249
249
|
// @ts-ignore
|
|
250
250
|
this.meetingCollection.getByKey(
|
|
251
251
|
CORRELATION_ID,
|
|
252
|
+
// @ts-ignore
|
|
252
253
|
MeetingsUtil.checkForCorrelationId(this.webex.internal.device.url, data.locus)
|
|
253
254
|
) ||
|
|
254
255
|
this.meetingCollection.getByKey(
|
|
@@ -460,7 +461,7 @@ export default class Meetings extends WebexPlugin {
|
|
|
460
461
|
LoggerProxy.set(this.webex.logger);
|
|
461
462
|
|
|
462
463
|
mediaLogger = new MediaLogger();
|
|
463
|
-
|
|
464
|
+
setLogger(mediaLogger);
|
|
464
465
|
|
|
465
466
|
/**
|
|
466
467
|
* The MeetingInfo object to interact with server
|
|
@@ -471,11 +472,14 @@ export default class Meetings extends WebexPlugin {
|
|
|
471
472
|
*/
|
|
472
473
|
// @ts-ignore
|
|
473
474
|
this.meetingInfo = this.config.experimental.enableUnifiedMeetings
|
|
474
|
-
?
|
|
475
|
-
|
|
475
|
+
? // @ts-ignore
|
|
476
|
+
new MeetingInfoV2(this.webex)
|
|
477
|
+
: // @ts-ignore
|
|
478
|
+
new MeetingInfo(this.webex);
|
|
476
479
|
// @ts-ignore
|
|
477
480
|
this.personalMeetingRoom = new PersonalMeetingRoom(
|
|
478
481
|
{meetingInfo: this.meetingInfo},
|
|
482
|
+
// @ts-ignore
|
|
479
483
|
{parent: this.webex}
|
|
480
484
|
);
|
|
481
485
|
|
|
@@ -585,6 +589,7 @@ export default class Meetings extends WebexPlugin {
|
|
|
585
589
|
// @ts-ignore
|
|
586
590
|
.then(() =>
|
|
587
591
|
LoggerProxy.logger.info(
|
|
592
|
+
// @ts-ignore
|
|
588
593
|
`Meetings:index#register --> INFO, Device registered ${this.webex.internal.device.url}`
|
|
589
594
|
)
|
|
590
595
|
)
|
|
@@ -638,8 +643,8 @@ export default class Meetings extends WebexPlugin {
|
|
|
638
643
|
|
|
639
644
|
this.stopListeningForEvents();
|
|
640
645
|
|
|
641
|
-
// @ts-ignore
|
|
642
646
|
return (
|
|
647
|
+
// @ts-ignore
|
|
643
648
|
this.webex.internal.mercury
|
|
644
649
|
.disconnect()
|
|
645
650
|
// @ts-ignore
|
package/src/meetings/request.ts
CHANGED
|
@@ -53,9 +53,9 @@ export default class MeetingRequest extends StatelessWebexPlugin {
|
|
|
53
53
|
*/
|
|
54
54
|
determineRedirections(responseBody: any) {
|
|
55
55
|
if (responseBody.remoteLocusClusterUrls && responseBody.remoteLocusClusterUrls.length) {
|
|
56
|
-
// @ts-ignore
|
|
57
56
|
return Promise.all(
|
|
58
57
|
responseBody.remoteLocusClusterUrls.map((url) =>
|
|
58
|
+
// @ts-ignore
|
|
59
59
|
this.request({
|
|
60
60
|
method: HTTP_VERBS.GET,
|
|
61
61
|
url,
|
package/src/member/index.ts
CHANGED
|
@@ -31,6 +31,7 @@ export default class Member {
|
|
|
31
31
|
name: any;
|
|
32
32
|
participant: any;
|
|
33
33
|
status: any;
|
|
34
|
+
supportsBreakouts: boolean;
|
|
34
35
|
type: any;
|
|
35
36
|
namespace = MEETINGS;
|
|
36
37
|
|
|
@@ -100,6 +101,13 @@ export default class Member {
|
|
|
100
101
|
* @memberof Member
|
|
101
102
|
*/
|
|
102
103
|
this.isHandRaised = null;
|
|
104
|
+
/**
|
|
105
|
+
* @instance
|
|
106
|
+
* @type {Boolean}
|
|
107
|
+
* @public
|
|
108
|
+
* @memberof Member
|
|
109
|
+
*/
|
|
110
|
+
this.supportsBreakouts = null;
|
|
103
111
|
/**
|
|
104
112
|
* @instance
|
|
105
113
|
* @type {Boolean}
|
|
@@ -242,6 +250,7 @@ export default class Member {
|
|
|
242
250
|
this.isAudioMuted = MemberUtil.isAudioMuted(participant);
|
|
243
251
|
this.isVideoMuted = MemberUtil.isVideoMuted(participant);
|
|
244
252
|
this.isHandRaised = MemberUtil.isHandRaised(participant);
|
|
253
|
+
this.supportsBreakouts = MemberUtil.isBreakoutsSupported(participant);
|
|
245
254
|
this.isGuest = MemberUtil.isGuest(participant);
|
|
246
255
|
this.isUser = MemberUtil.isUser(participant);
|
|
247
256
|
this.isDevice = MemberUtil.isDevice(participant);
|
package/src/member/util.ts
CHANGED
|
@@ -72,7 +72,7 @@ MemberUtil.isAssociatedSame = (participant: any, id: string) =>
|
|
|
72
72
|
* @param {String} status
|
|
73
73
|
* @returns {Boolean}
|
|
74
74
|
*/
|
|
75
|
-
MemberUtil.isNotAdmitted = (participant: any, isGuest: boolean, status: string) =>
|
|
75
|
+
MemberUtil.isNotAdmitted = (participant: any, isGuest: boolean, status: string): boolean =>
|
|
76
76
|
participant &&
|
|
77
77
|
participant.guest &&
|
|
78
78
|
((participant.devices &&
|
|
@@ -82,6 +82,7 @@ MemberUtil.isNotAdmitted = (participant: any, isGuest: boolean, status: string)
|
|
|
82
82
|
// @ts-ignore
|
|
83
83
|
isGuest &&
|
|
84
84
|
status === _IN_LOBBY_) ||
|
|
85
|
+
// @ts-ignore
|
|
85
86
|
!status === _IN_MEETING_);
|
|
86
87
|
|
|
87
88
|
/**
|
|
@@ -130,6 +131,18 @@ MemberUtil.isHandRaised = (participant: any) => {
|
|
|
130
131
|
return participant.controls?.hand?.raised || false;
|
|
131
132
|
};
|
|
132
133
|
|
|
134
|
+
/**
|
|
135
|
+
* @param {Object} participant the locus participant
|
|
136
|
+
* @returns {Boolean}
|
|
137
|
+
*/
|
|
138
|
+
MemberUtil.isBreakoutsSupported = (participant) => {
|
|
139
|
+
if (!participant) {
|
|
140
|
+
throw new ParameterError('Breakout support could not be processed, participant is undefined.');
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return !participant.doesNotSupportBreakouts;
|
|
144
|
+
};
|
|
145
|
+
|
|
133
146
|
/**
|
|
134
147
|
* utility method for audio/video muted status
|
|
135
148
|
* @param {String} status
|
package/src/members/index.ts
CHANGED
|
@@ -925,6 +925,7 @@ export default class Members extends StatelessWebexPlugin {
|
|
|
925
925
|
*/
|
|
926
926
|
findMemberByCsi(csi) {
|
|
927
927
|
return Object.values(this.membersCollection.getAll()).find((member) =>
|
|
928
|
+
// @ts-ignore
|
|
928
929
|
member.participant?.devices?.find((device) =>
|
|
929
930
|
device.csis?.find((memberCsi) => memberCsi === csi)
|
|
930
931
|
)
|
package/src/members/request.ts
CHANGED
|
@@ -1,9 +1,18 @@
|
|
|
1
1
|
/* eslint-disable require-jsdoc */
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
MediaRequest as WcmeMediaRequest,
|
|
4
|
+
Policy,
|
|
5
|
+
ActiveSpeakerInfo,
|
|
6
|
+
ReceiverSelectedInfo,
|
|
7
|
+
CodecInfo as WcmeCodecInfo,
|
|
8
|
+
H264Codec,
|
|
9
|
+
} from '@webex/internal-media-core';
|
|
10
|
+
import {cloneDeep} from 'lodash';
|
|
3
11
|
|
|
4
12
|
import LoggerProxy from '../common/logs/logger-proxy';
|
|
5
13
|
|
|
6
14
|
import {ReceiveSlot, ReceiveSlotId} from './receiveSlot';
|
|
15
|
+
import {getMaxFs} from './remoteMedia';
|
|
7
16
|
|
|
8
17
|
export interface ActiveSpeakerPolicyInfo {
|
|
9
18
|
policy: 'active-speaker';
|
|
@@ -47,22 +56,32 @@ const CODEC_DEFAULTS = {
|
|
|
47
56
|
},
|
|
48
57
|
};
|
|
49
58
|
|
|
50
|
-
type
|
|
59
|
+
type DegradationPreferences = {
|
|
60
|
+
maxMacroblocksLimit: number;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
type SendMediaRequestsCallback = (mediaRequests: WcmeMediaRequest[]) => void;
|
|
51
64
|
|
|
52
65
|
export class MediaRequestManager {
|
|
53
66
|
private sendMediaRequestsCallback: SendMediaRequestsCallback;
|
|
54
67
|
|
|
55
|
-
private counter;
|
|
68
|
+
private counter: number;
|
|
56
69
|
|
|
57
70
|
private clientRequests: {[key: MediaRequestId]: MediaRequest};
|
|
58
71
|
|
|
59
72
|
private slotsActiveInLastMediaRequest: {[key: ReceiveSlotId]: ReceiveSlot};
|
|
60
73
|
|
|
61
|
-
|
|
74
|
+
private degradationPreferences: DegradationPreferences;
|
|
75
|
+
|
|
76
|
+
constructor(
|
|
77
|
+
degradationPreferences: DegradationPreferences,
|
|
78
|
+
sendMediaRequestsCallback: SendMediaRequestsCallback
|
|
79
|
+
) {
|
|
62
80
|
this.sendMediaRequestsCallback = sendMediaRequestsCallback;
|
|
63
81
|
this.counter = 0;
|
|
64
82
|
this.clientRequests = {};
|
|
65
83
|
this.slotsActiveInLastMediaRequest = {};
|
|
84
|
+
this.degradationPreferences = degradationPreferences;
|
|
66
85
|
}
|
|
67
86
|
|
|
68
87
|
private resetInactiveReceiveSlots() {
|
|
@@ -90,34 +109,79 @@ export class MediaRequestManager {
|
|
|
90
109
|
this.slotsActiveInLastMediaRequest = activeSlots;
|
|
91
110
|
}
|
|
92
111
|
|
|
112
|
+
public setDegradationPreferences(degradationPreferences: DegradationPreferences) {
|
|
113
|
+
this.degradationPreferences = degradationPreferences;
|
|
114
|
+
this.sendRequests(); // re-send requests after preferences are set
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
private getDegradedClientRequests() {
|
|
118
|
+
const clientRequests = cloneDeep(this.clientRequests);
|
|
119
|
+
const maxFsLimits = [
|
|
120
|
+
getMaxFs('best'),
|
|
121
|
+
getMaxFs('large'),
|
|
122
|
+
getMaxFs('medium'),
|
|
123
|
+
getMaxFs('small'),
|
|
124
|
+
getMaxFs('very small'),
|
|
125
|
+
getMaxFs('thumbnail'),
|
|
126
|
+
];
|
|
127
|
+
|
|
128
|
+
// reduce max-fs until total macroblocks is below limit
|
|
129
|
+
for (let i = 0; i < maxFsLimits.length; i += 1) {
|
|
130
|
+
let totalMacroblocksRequested = 0;
|
|
131
|
+
Object.values(clientRequests).forEach((mr) => {
|
|
132
|
+
if (mr.codecInfo) {
|
|
133
|
+
mr.codecInfo.maxFs = Math.min(
|
|
134
|
+
mr.codecInfo.maxFs || CODEC_DEFAULTS.h264.maxFs,
|
|
135
|
+
maxFsLimits[i]
|
|
136
|
+
);
|
|
137
|
+
totalMacroblocksRequested += mr.codecInfo.maxFs * mr.receiveSlots.length;
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
if (totalMacroblocksRequested <= this.degradationPreferences.maxMacroblocksLimit) {
|
|
141
|
+
if (i !== 0) {
|
|
142
|
+
LoggerProxy.logger.warn(
|
|
143
|
+
`multistream:mediaRequestManager --> too many requests with high max-fs, frame size will be limited to ${maxFsLimits[i]}`
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
break;
|
|
147
|
+
} else if (i === maxFsLimits.length - 1) {
|
|
148
|
+
LoggerProxy.logger.warn(
|
|
149
|
+
`multistream:mediaRequestManager --> even with frame size limited to ${maxFsLimits[i]} you are still requesting too many streams, consider reducing the number of requests`
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return clientRequests;
|
|
155
|
+
}
|
|
156
|
+
|
|
93
157
|
private sendRequests() {
|
|
94
|
-
const wcmeMediaRequests:
|
|
158
|
+
const wcmeMediaRequests: WcmeMediaRequest[] = [];
|
|
95
159
|
|
|
96
|
-
|
|
160
|
+
const clientRequests = this.getDegradedClientRequests();
|
|
97
161
|
const maxPayloadBitsPerSecond = 10 * 1000 * 1000;
|
|
98
162
|
|
|
99
163
|
// map all the client media requests to wcme media requests
|
|
100
|
-
Object.values(
|
|
164
|
+
Object.values(clientRequests).forEach((mr) => {
|
|
101
165
|
wcmeMediaRequests.push(
|
|
102
|
-
new
|
|
166
|
+
new WcmeMediaRequest(
|
|
103
167
|
mr.policyInfo.policy === 'active-speaker'
|
|
104
|
-
?
|
|
105
|
-
:
|
|
168
|
+
? Policy.ActiveSpeaker
|
|
169
|
+
: Policy.ReceiverSelected,
|
|
106
170
|
mr.policyInfo.policy === 'active-speaker'
|
|
107
|
-
? new
|
|
171
|
+
? new ActiveSpeakerInfo(
|
|
108
172
|
mr.policyInfo.priority,
|
|
109
173
|
mr.policyInfo.crossPriorityDuplication,
|
|
110
174
|
mr.policyInfo.crossPolicyDuplication,
|
|
111
175
|
mr.policyInfo.preferLiveVideo
|
|
112
176
|
)
|
|
113
|
-
: new
|
|
177
|
+
: new ReceiverSelectedInfo(mr.policyInfo.csi),
|
|
114
178
|
mr.receiveSlots.map((receiveSlot) => receiveSlot.wcmeReceiveSlot),
|
|
115
179
|
maxPayloadBitsPerSecond,
|
|
116
180
|
mr.codecInfo && [
|
|
117
|
-
new
|
|
181
|
+
new WcmeCodecInfo(
|
|
118
182
|
0x80,
|
|
119
|
-
new
|
|
120
|
-
mr.codecInfo.maxFs
|
|
183
|
+
new H264Codec(
|
|
184
|
+
mr.codecInfo.maxFs,
|
|
121
185
|
mr.codecInfo.maxFps || CODEC_DEFAULTS.h264.maxFps,
|
|
122
186
|
mr.codecInfo.maxMbps || CODEC_DEFAULTS.h264.maxMbps,
|
|
123
187
|
mr.codecInfo.maxWidth,
|
|
@@ -48,6 +48,7 @@ export class MultistreamMedia {
|
|
|
48
48
|
// todo: depending on how muting is done with Local tracks, this code here might need to change...
|
|
49
49
|
|
|
50
50
|
if (track.kind === 'audio') {
|
|
51
|
+
// @ts-ignore
|
|
51
52
|
this.meeting.setLocalAudioTrack(track);
|
|
52
53
|
this.meeting.mediaProperties.mediaDirection.sendAudio = true;
|
|
53
54
|
|
|
@@ -56,6 +57,7 @@ export class MultistreamMedia {
|
|
|
56
57
|
this.meeting.audio ||
|
|
57
58
|
createMuteState(AUDIO, this.meeting, this.meeting.mediaProperties.mediaDirection);
|
|
58
59
|
} else if (track.kind === 'video') {
|
|
60
|
+
// @ts-ignore
|
|
59
61
|
this.meeting.setLocalVideoTrack(track);
|
|
60
62
|
this.meeting.mediaProperties.mediaDirection.sendVideo = true;
|
|
61
63
|
|
|
@@ -80,9 +82,11 @@ export class MultistreamMedia {
|
|
|
80
82
|
// muting etc
|
|
81
83
|
|
|
82
84
|
if (track.kind === 'audio') {
|
|
85
|
+
// @ts-ignore
|
|
83
86
|
this.meeting.setLocalVideoTrack(null);
|
|
84
87
|
this.meeting.mediaProperties.mediaDirection.sendAudio = false;
|
|
85
88
|
} else if (track.kind === 'video') {
|
|
89
|
+
// @ts-ignore
|
|
86
90
|
this.meeting.setLocalAudioTrack(null);
|
|
87
91
|
this.meeting.mediaProperties.mediaDirection.sendVideo = false;
|
|
88
92
|
}
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
/* eslint-disable valid-jsdoc */
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
MediaType,
|
|
4
|
+
ReceiveSlot as WcmeReceiveSlot,
|
|
5
|
+
ReceiveSlotEvents as WcmeReceiveSlotEvents,
|
|
6
|
+
SourceState,
|
|
7
|
+
} from '@webex/internal-media-core';
|
|
3
8
|
|
|
4
9
|
import LoggerProxy from '../common/logs/logger-proxy';
|
|
5
10
|
import EventsScope from '../common/events/events-scope';
|
|
@@ -8,7 +13,7 @@ export const ReceiveSlotEvents = {
|
|
|
8
13
|
SourceUpdate: 'sourceUpdate',
|
|
9
14
|
};
|
|
10
15
|
|
|
11
|
-
export type SourceState
|
|
16
|
+
export type {SourceState} from '@webex/internal-media-core';
|
|
12
17
|
export type CSI = number;
|
|
13
18
|
export type MemberId = string;
|
|
14
19
|
export type ReceiveSlotId = string;
|
|
@@ -22,31 +27,31 @@ export type FindMemberIdCallback = (csi: CSI) => MemberId | undefined;
|
|
|
22
27
|
* for example some participant's main video or audio
|
|
23
28
|
*/
|
|
24
29
|
export class ReceiveSlot extends EventsScope {
|
|
25
|
-
private readonly mcReceiveSlot:
|
|
30
|
+
private readonly mcReceiveSlot: WcmeReceiveSlot;
|
|
26
31
|
|
|
27
32
|
private readonly findMemberIdCallback: FindMemberIdCallback;
|
|
28
33
|
|
|
29
34
|
public readonly id: ReceiveSlotId;
|
|
30
35
|
|
|
31
|
-
public readonly mediaType:
|
|
36
|
+
public readonly mediaType: MediaType;
|
|
32
37
|
|
|
33
38
|
#memberId?: MemberId;
|
|
34
39
|
|
|
35
40
|
#csi?: CSI;
|
|
36
41
|
|
|
37
|
-
#sourceState:
|
|
42
|
+
#sourceState: SourceState;
|
|
38
43
|
|
|
39
44
|
/**
|
|
40
45
|
* constructor - don't use it directly, you should always use meeting.receiveSlotManager.allocateSlot()
|
|
41
46
|
* to create any receive slots
|
|
42
47
|
*
|
|
43
|
-
* @param {
|
|
44
|
-
* @param {
|
|
48
|
+
* @param {MediaType} mediaType
|
|
49
|
+
* @param {ReceiveSlot} mcReceiveSlot
|
|
45
50
|
* @param {FindMemberIdCallback} findMemberIdCallback callback for finding memberId for given CSI
|
|
46
51
|
*/
|
|
47
52
|
constructor(
|
|
48
|
-
mediaType:
|
|
49
|
-
mcReceiveSlot:
|
|
53
|
+
mediaType: MediaType,
|
|
54
|
+
mcReceiveSlot: WcmeReceiveSlot,
|
|
50
55
|
findMemberIdCallback: FindMemberIdCallback
|
|
51
56
|
) {
|
|
52
57
|
super();
|
|
@@ -93,8 +98,8 @@ export class ReceiveSlot extends EventsScope {
|
|
|
93
98
|
};
|
|
94
99
|
|
|
95
100
|
this.mcReceiveSlot.on(
|
|
96
|
-
|
|
97
|
-
(state:
|
|
101
|
+
WcmeReceiveSlotEvents.SourceUpdate,
|
|
102
|
+
(state: SourceState, csi?: number) => {
|
|
98
103
|
LoggerProxy.logger.log(
|
|
99
104
|
`ReceiveSlot#setupEventListeners --> got source update on receive slot ${this.id}, mediaType=${this.mediaType}, csi=${csi}, state=${state}`
|
|
100
105
|
);
|
|
@@ -123,7 +128,7 @@ export class ReceiveSlot extends EventsScope {
|
|
|
123
128
|
/**
|
|
124
129
|
* The underlying WCME receive slot
|
|
125
130
|
*/
|
|
126
|
-
get wcmeReceiveSlot():
|
|
131
|
+
get wcmeReceiveSlot(): WcmeReceiveSlot {
|
|
127
132
|
return this.mcReceiveSlot;
|
|
128
133
|
}
|
|
129
134
|
|