@webex/plugin-meetings 3.0.0-beta.17 → 3.0.0-beta.19
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 +38 -5
- 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/index.js +719 -490
- 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 +4 -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 +35 -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/index.ts +398 -129
- package/src/meeting/request.ts +9 -31
- package/src/meeting/request.type.ts +2 -0
- package/src/meeting/util.ts +3 -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/index.js +419 -50
- package/test/unit/spec/meeting/request.js +17 -0
- package/test/unit/spec/meeting/utils.js +20 -129
- 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/index.ts
CHANGED
|
@@ -2,7 +2,15 @@ import uuid from 'uuid';
|
|
|
2
2
|
import {cloneDeep, isEqual, pick, isString, defer} from 'lodash';
|
|
3
3
|
// @ts-ignore - Fix this
|
|
4
4
|
import {StatelessWebexPlugin} from '@webex/webex-core';
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
ConnectionState,
|
|
7
|
+
Errors,
|
|
8
|
+
ErrorType,
|
|
9
|
+
Event,
|
|
10
|
+
Media as WebRTCMedia,
|
|
11
|
+
MediaType,
|
|
12
|
+
RemoteTrackType,
|
|
13
|
+
} from '@webex/internal-media-core';
|
|
6
14
|
|
|
7
15
|
import {
|
|
8
16
|
MeetingNotActiveError,
|
|
@@ -28,8 +36,10 @@ import ReconnectionManager from '../reconnection-manager';
|
|
|
28
36
|
import MeetingRequest from './request';
|
|
29
37
|
import Members from '../members/index';
|
|
30
38
|
import MeetingUtil from './util';
|
|
39
|
+
import RecordingUtil from '../recording-controller/util';
|
|
31
40
|
import MediaUtil from '../media/util';
|
|
32
41
|
import Transcription from '../transcription';
|
|
42
|
+
import {Reactions, SkinTones} from '../reactions/reactions';
|
|
33
43
|
import PasswordError from '../common/errors/password-error';
|
|
34
44
|
import CaptchaError from '../common/errors/captcha-error';
|
|
35
45
|
import ReconnectionError from '../common/errors/reconnection';
|
|
@@ -44,6 +54,7 @@ import {
|
|
|
44
54
|
EVENT_TRIGGERS,
|
|
45
55
|
EVENT_TYPES,
|
|
46
56
|
EVENTS,
|
|
57
|
+
BREAKOUTS,
|
|
47
58
|
FLOOR_ACTION,
|
|
48
59
|
FULL_STATE,
|
|
49
60
|
LAYOUT_TYPES,
|
|
@@ -84,10 +95,18 @@ import {
|
|
|
84
95
|
Event as RemoteMediaManagerEvent,
|
|
85
96
|
} from '../multistream/remoteMediaManager';
|
|
86
97
|
import {MultistreamMedia} from '../multistream/multistreamMedia';
|
|
87
|
-
import {
|
|
88
|
-
|
|
98
|
+
import {
|
|
99
|
+
Reaction,
|
|
100
|
+
ReactionType,
|
|
101
|
+
SkinToneType,
|
|
102
|
+
ProcessedReaction,
|
|
103
|
+
RelayEvent,
|
|
104
|
+
} from '../reactions/reactions.type';
|
|
105
|
+
import Breakouts from '../breakouts';
|
|
89
106
|
|
|
90
107
|
import InMeetingActions from './in-meeting-actions';
|
|
108
|
+
import {REACTION_RELAY_TYPES} from '../reactions/constants';
|
|
109
|
+
import RecordingController from '../recording-controller';
|
|
91
110
|
|
|
92
111
|
const {isBrowser} = BrowserDetection();
|
|
93
112
|
|
|
@@ -111,6 +130,7 @@ export const MEDIA_UPDATE_TYPE = {
|
|
|
111
130
|
AUDIO: 'AUDIO',
|
|
112
131
|
VIDEO: 'VIDEO',
|
|
113
132
|
SHARE: 'SHARE',
|
|
133
|
+
LAMBDA: 'LAMBDA',
|
|
114
134
|
};
|
|
115
135
|
|
|
116
136
|
/**
|
|
@@ -403,6 +423,7 @@ export const MEDIA_UPDATE_TYPE = {
|
|
|
403
423
|
export default class Meeting extends StatelessWebexPlugin {
|
|
404
424
|
attrs: any;
|
|
405
425
|
audio: any;
|
|
426
|
+
breakouts: any;
|
|
406
427
|
conversationUrl: string;
|
|
407
428
|
correlationId: string;
|
|
408
429
|
destination: string;
|
|
@@ -454,6 +475,8 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
454
475
|
mediaRequestManagers: {
|
|
455
476
|
audio: MediaRequestManager;
|
|
456
477
|
video: MediaRequestManager;
|
|
478
|
+
screenShareAudio: MediaRequestManager;
|
|
479
|
+
screenShareVideo: MediaRequestManager;
|
|
457
480
|
};
|
|
458
481
|
|
|
459
482
|
meetingInfoFailureReason: string;
|
|
@@ -463,6 +486,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
463
486
|
queuedMediaUpdates: any[];
|
|
464
487
|
recording: any;
|
|
465
488
|
remoteMediaManager: RemoteMediaManager | null;
|
|
489
|
+
recordingController: RecordingController;
|
|
466
490
|
requiredCaptcha: any;
|
|
467
491
|
receiveSlotManager: ReceiveSlotManager;
|
|
468
492
|
shareStatus: string;
|
|
@@ -573,16 +597,25 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
573
597
|
*/
|
|
574
598
|
// TODO: needs to be defined as a class
|
|
575
599
|
this.meetingInfo = {};
|
|
600
|
+
/**
|
|
601
|
+
* @instance
|
|
602
|
+
* @type {Breakouts}
|
|
603
|
+
* @public
|
|
604
|
+
* @memberof Meeting
|
|
605
|
+
*/
|
|
606
|
+
// @ts-ignore
|
|
607
|
+
this.breakouts = new Breakouts({}, {parent: this.webex});
|
|
576
608
|
/**
|
|
577
609
|
* helper class for managing receive slots (for multistream media connections)
|
|
578
610
|
*/
|
|
579
611
|
this.receiveSlotManager = new ReceiveSlotManager(this);
|
|
580
612
|
/**
|
|
581
|
-
*
|
|
582
|
-
* All media requests sent out for
|
|
613
|
+
* Object containing helper classes for managing media requests for audio/video/screenshare (for multistream media connections)
|
|
614
|
+
* All multistream media requests sent out for this meeting have to go through them.
|
|
583
615
|
*/
|
|
584
616
|
this.mediaRequestManagers = {
|
|
585
|
-
|
|
617
|
+
// @ts-ignore - config coming from registerPlugin
|
|
618
|
+
audio: new MediaRequestManager(this.config.degradationPreferences, (mediaRequests) => {
|
|
586
619
|
if (!this.mediaProperties.webrtcMediaConnection) {
|
|
587
620
|
LoggerProxy.logger.warn(
|
|
588
621
|
'Meeting:index#mediaRequestManager --> trying to send audio media request before media connection was created'
|
|
@@ -590,12 +623,10 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
590
623
|
|
|
591
624
|
return;
|
|
592
625
|
}
|
|
593
|
-
this.mediaProperties.webrtcMediaConnection.requestMedia(
|
|
594
|
-
MC.MediaType.AudioMain,
|
|
595
|
-
mediaRequests
|
|
596
|
-
);
|
|
626
|
+
this.mediaProperties.webrtcMediaConnection.requestMedia(MediaType.AudioMain, mediaRequests);
|
|
597
627
|
}),
|
|
598
|
-
|
|
628
|
+
// @ts-ignore - config coming from registerPlugin
|
|
629
|
+
video: new MediaRequestManager(this.config.degradationPreferences, (mediaRequests) => {
|
|
599
630
|
if (!this.mediaProperties.webrtcMediaConnection) {
|
|
600
631
|
LoggerProxy.logger.warn(
|
|
601
632
|
'Meeting:index#mediaRequestManager --> trying to send video media request before media connection was created'
|
|
@@ -603,11 +634,42 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
603
634
|
|
|
604
635
|
return;
|
|
605
636
|
}
|
|
606
|
-
this.mediaProperties.webrtcMediaConnection.requestMedia(
|
|
607
|
-
MC.MediaType.VideoMain,
|
|
608
|
-
mediaRequests
|
|
609
|
-
);
|
|
637
|
+
this.mediaProperties.webrtcMediaConnection.requestMedia(MediaType.VideoMain, mediaRequests);
|
|
610
638
|
}),
|
|
639
|
+
screenShareAudio: new MediaRequestManager(
|
|
640
|
+
// @ts-ignore - config coming from registerPlugin
|
|
641
|
+
this.config.degradationPreferences,
|
|
642
|
+
(mediaRequests) => {
|
|
643
|
+
if (!this.mediaProperties.webrtcMediaConnection) {
|
|
644
|
+
LoggerProxy.logger.warn(
|
|
645
|
+
'Meeting:index#mediaRequestManager --> trying to send screenshare audio media request before media connection was created'
|
|
646
|
+
);
|
|
647
|
+
|
|
648
|
+
return;
|
|
649
|
+
}
|
|
650
|
+
this.mediaProperties.webrtcMediaConnection.requestMedia(
|
|
651
|
+
MediaType.AudioSlides,
|
|
652
|
+
mediaRequests
|
|
653
|
+
);
|
|
654
|
+
}
|
|
655
|
+
),
|
|
656
|
+
screenShareVideo: new MediaRequestManager(
|
|
657
|
+
// @ts-ignore - config coming from registerPlugin
|
|
658
|
+
this.config.degradationPreferences,
|
|
659
|
+
(mediaRequests) => {
|
|
660
|
+
if (!this.mediaProperties.webrtcMediaConnection) {
|
|
661
|
+
LoggerProxy.logger.warn(
|
|
662
|
+
'Meeting:index#mediaRequestManager --> trying to send screenshare video media request before media connection was created'
|
|
663
|
+
);
|
|
664
|
+
|
|
665
|
+
return;
|
|
666
|
+
}
|
|
667
|
+
this.mediaProperties.webrtcMediaConnection.requestMedia(
|
|
668
|
+
MediaType.VideoSlides,
|
|
669
|
+
mediaRequests
|
|
670
|
+
);
|
|
671
|
+
}
|
|
672
|
+
),
|
|
611
673
|
};
|
|
612
674
|
/**
|
|
613
675
|
* @instance
|
|
@@ -620,8 +682,8 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
620
682
|
locusUrl: attrs.locus && attrs.locus.url,
|
|
621
683
|
receiveSlotManager: this.receiveSlotManager,
|
|
622
684
|
mediaRequestManagers: this.mediaRequestManagers,
|
|
623
|
-
// @ts-ignore - Fix type
|
|
624
685
|
},
|
|
686
|
+
// @ts-ignore - Fix type
|
|
625
687
|
{parent: this.webex}
|
|
626
688
|
);
|
|
627
689
|
/**
|
|
@@ -924,6 +986,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
924
986
|
*/
|
|
925
987
|
// @ts-ignore - Fix type
|
|
926
988
|
this.locusInfo = new LocusInfo(this.updateMeetingObject.bind(this), this.webex, this.id);
|
|
989
|
+
|
|
927
990
|
// We had to add listeners first before setting up the locus instance
|
|
928
991
|
/**
|
|
929
992
|
* @instance
|
|
@@ -1023,6 +1086,20 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
1023
1086
|
*/
|
|
1024
1087
|
this.keepAliveTimerId = null;
|
|
1025
1088
|
|
|
1089
|
+
/**
|
|
1090
|
+
* The class that helps to control recording functions: start, stop, pause, resume, etc
|
|
1091
|
+
* @instance
|
|
1092
|
+
* @type {RecordingController}
|
|
1093
|
+
* @public
|
|
1094
|
+
* @memberof Meeting
|
|
1095
|
+
*/
|
|
1096
|
+
this.recordingController = new RecordingController(this.meetingRequest, {
|
|
1097
|
+
serviceUrl: this.locusInfo?.links?.services?.record?.url,
|
|
1098
|
+
sessionId: this.locusInfo?.fullState?.sessionId,
|
|
1099
|
+
locusUrl: this.locusInfo?.url,
|
|
1100
|
+
displayHints: [],
|
|
1101
|
+
});
|
|
1102
|
+
|
|
1026
1103
|
this.setUpLocusInfoListeners();
|
|
1027
1104
|
this.locusInfo.init(attrs.locus ? attrs.locus : {});
|
|
1028
1105
|
this.hasJoinedOnce = false;
|
|
@@ -1108,8 +1185,8 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
1108
1185
|
return Promise.resolve();
|
|
1109
1186
|
} catch (err) {
|
|
1110
1187
|
if (err instanceof MeetingInfoV2PasswordError) {
|
|
1111
|
-
// @ts-ignore
|
|
1112
1188
|
LoggerProxy.logger.info(
|
|
1189
|
+
// @ts-ignore
|
|
1113
1190
|
`Meeting:index#fetchMeetingInfo --> Info Unable to fetch meeting info for ${this.destination} - password required (code=${err?.body?.code}).`
|
|
1114
1191
|
);
|
|
1115
1192
|
|
|
@@ -1128,8 +1205,8 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
1128
1205
|
|
|
1129
1206
|
throw new PasswordError();
|
|
1130
1207
|
} else if (err instanceof MeetingInfoV2CaptchaError) {
|
|
1131
|
-
// @ts-ignore
|
|
1132
1208
|
LoggerProxy.logger.info(
|
|
1209
|
+
// @ts-ignore
|
|
1133
1210
|
`Meeting:index#fetchMeetingInfo --> Info Unable to fetch meeting info for ${this.destination} - captcha required (code=${err?.body?.code}).`
|
|
1134
1211
|
);
|
|
1135
1212
|
|
|
@@ -1201,22 +1278,25 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
1201
1278
|
// we have to pass the wbxappapi hostname as the siteFullName parameter
|
|
1202
1279
|
const {hostname} = new URL(this.requiredCaptcha.refreshURL);
|
|
1203
1280
|
|
|
1204
|
-
return
|
|
1205
|
-
.
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1281
|
+
return (
|
|
1282
|
+
this.meetingRequest
|
|
1283
|
+
// @ts-ignore
|
|
1284
|
+
.refreshCaptcha({
|
|
1285
|
+
captchaRefreshUrl: `${this.requiredCaptcha.refreshURL}&siteFullName=${hostname}`,
|
|
1286
|
+
captchaId: this.requiredCaptcha.captchaId,
|
|
1287
|
+
})
|
|
1288
|
+
.then((response) => {
|
|
1289
|
+
this.requiredCaptcha.captchaId = response.body.captchaID;
|
|
1290
|
+
this.requiredCaptcha.verificationImageURL = response.body.verificationImageURL;
|
|
1291
|
+
this.requiredCaptcha.verificationAudioURL = response.body.verificationAudioURL;
|
|
1292
|
+
})
|
|
1293
|
+
.catch((error) => {
|
|
1294
|
+
LoggerProxy.logger.error(
|
|
1295
|
+
`Meeting:index#refreshCaptcha --> Error Unable to refresh captcha for ${this.destination} - ${error}`
|
|
1296
|
+
);
|
|
1297
|
+
throw error;
|
|
1298
|
+
})
|
|
1299
|
+
);
|
|
1220
1300
|
}
|
|
1221
1301
|
|
|
1222
1302
|
/**
|
|
@@ -1229,6 +1309,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
1229
1309
|
// meeting update listeners
|
|
1230
1310
|
this.setUpLocusInfoSelfListener();
|
|
1231
1311
|
this.setUpLocusInfoMeetingListener();
|
|
1312
|
+
this.setUpLocusServicesListener();
|
|
1232
1313
|
// members update listeners
|
|
1233
1314
|
this.setUpLocusFullStateListener();
|
|
1234
1315
|
this.setUpLocusUrlListener();
|
|
@@ -1241,6 +1322,49 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
1241
1322
|
this.setUpLocusInfoMeetingInfoListener();
|
|
1242
1323
|
this.setUpLocusInfoAssignHostListener();
|
|
1243
1324
|
this.setUpLocusInfoMediaInactiveListener();
|
|
1325
|
+
this.setUpBreakoutsListener();
|
|
1326
|
+
}
|
|
1327
|
+
|
|
1328
|
+
/**
|
|
1329
|
+
* Set up the listeners for breakouts
|
|
1330
|
+
* @returns {undefined}
|
|
1331
|
+
* @private
|
|
1332
|
+
* @memberof Meeting
|
|
1333
|
+
*/
|
|
1334
|
+
setUpBreakoutsListener() {
|
|
1335
|
+
this.breakouts.on(BREAKOUTS.EVENTS.BREAKOUTS_CLOSING, () => {
|
|
1336
|
+
Trigger.trigger(
|
|
1337
|
+
this,
|
|
1338
|
+
{
|
|
1339
|
+
file: 'meeting/index',
|
|
1340
|
+
function: 'setUpBreakoutsListener',
|
|
1341
|
+
},
|
|
1342
|
+
EVENT_TRIGGERS.MEETING_BREAKOUTS_CLOSING
|
|
1343
|
+
);
|
|
1344
|
+
});
|
|
1345
|
+
|
|
1346
|
+
this.breakouts.on(BREAKOUTS.EVENTS.MESSAGE, (messageEvent) => {
|
|
1347
|
+
Trigger.trigger(
|
|
1348
|
+
this,
|
|
1349
|
+
{
|
|
1350
|
+
file: 'meeting/index',
|
|
1351
|
+
function: 'setUpBreakoutsListener',
|
|
1352
|
+
},
|
|
1353
|
+
EVENT_TRIGGERS.MEETING_BREAKOUTS_MESSAGE,
|
|
1354
|
+
messageEvent
|
|
1355
|
+
);
|
|
1356
|
+
});
|
|
1357
|
+
|
|
1358
|
+
this.breakouts.on(BREAKOUTS.EVENTS.MEMBERS_UPDATE, () => {
|
|
1359
|
+
Trigger.trigger(
|
|
1360
|
+
this,
|
|
1361
|
+
{
|
|
1362
|
+
file: 'meeting/index',
|
|
1363
|
+
function: 'setUpBreakoutsListener',
|
|
1364
|
+
},
|
|
1365
|
+
EVENT_TRIGGERS.MEETING_BREAKOUTS_UPDATE
|
|
1366
|
+
);
|
|
1367
|
+
});
|
|
1244
1368
|
}
|
|
1245
1369
|
|
|
1246
1370
|
/**
|
|
@@ -1786,6 +1910,18 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
1786
1910
|
}
|
|
1787
1911
|
);
|
|
1788
1912
|
|
|
1913
|
+
this.locusInfo.on(LOCUSINFO.EVENTS.CONTROLS_MEETING_BREAKOUT_UPDATED, ({breakout}) => {
|
|
1914
|
+
this.breakouts.updateBreakout(breakout);
|
|
1915
|
+
Trigger.trigger(
|
|
1916
|
+
this,
|
|
1917
|
+
{
|
|
1918
|
+
file: 'meeting/index',
|
|
1919
|
+
function: 'setupLocusControlsListener',
|
|
1920
|
+
},
|
|
1921
|
+
EVENT_TRIGGERS.MEETING_BREAKOUTS_UPDATE
|
|
1922
|
+
);
|
|
1923
|
+
});
|
|
1924
|
+
|
|
1789
1925
|
this.locusInfo.on(LOCUSINFO.EVENTS.CONTROLS_ENTRY_EXIT_TONE_UPDATED, ({entryExitTone}) => {
|
|
1790
1926
|
Trigger.trigger(
|
|
1791
1927
|
this,
|
|
@@ -2041,8 +2177,26 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
2041
2177
|
private setUpLocusUrlListener() {
|
|
2042
2178
|
this.locusInfo.on(EVENTS.LOCUS_INFO_UPDATE_URL, (payload) => {
|
|
2043
2179
|
this.members.locusUrlUpdate(payload);
|
|
2180
|
+
this.breakouts.locusUrlUpdate(payload);
|
|
2044
2181
|
this.locusUrl = payload;
|
|
2045
2182
|
this.locusId = this.locusUrl?.split('/').pop();
|
|
2183
|
+
this.recordingController.setLocusUrl(this.locusUrl);
|
|
2184
|
+
});
|
|
2185
|
+
}
|
|
2186
|
+
|
|
2187
|
+
/**
|
|
2188
|
+
* Set up the locus info service link listener
|
|
2189
|
+
* update the locusInfo for recording controller
|
|
2190
|
+
* does not currently re-emit the event as it's internal only
|
|
2191
|
+
* payload is unused
|
|
2192
|
+
* @returns {undefined}
|
|
2193
|
+
* @private
|
|
2194
|
+
* @memberof Meeting
|
|
2195
|
+
*/
|
|
2196
|
+
private setUpLocusServicesListener() {
|
|
2197
|
+
this.locusInfo.on(LOCUSINFO.EVENTS.LINKS_SERVICES, (payload) => {
|
|
2198
|
+
this.recordingController.setServiceUrl(payload?.services?.record?.url);
|
|
2199
|
+
this.recordingController.setSessionId(this.locusInfo?.fullState?.sessionId);
|
|
2046
2200
|
});
|
|
2047
2201
|
}
|
|
2048
2202
|
|
|
@@ -2092,10 +2246,10 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
2092
2246
|
canAdmitParticipant: MeetingUtil.canAdmitParticipant(payload.info.userDisplayHints),
|
|
2093
2247
|
canLock: MeetingUtil.canUserLock(payload.info.userDisplayHints),
|
|
2094
2248
|
canUnlock: MeetingUtil.canUserUnlock(payload.info.userDisplayHints),
|
|
2095
|
-
canStartRecording:
|
|
2096
|
-
canStopRecording:
|
|
2097
|
-
canPauseRecording:
|
|
2098
|
-
canResumeRecording:
|
|
2249
|
+
canStartRecording: RecordingUtil.canUserStart(payload.info.userDisplayHints),
|
|
2250
|
+
canStopRecording: RecordingUtil.canUserStop(payload.info.userDisplayHints),
|
|
2251
|
+
canPauseRecording: RecordingUtil.canUserPause(payload.info.userDisplayHints),
|
|
2252
|
+
canResumeRecording: RecordingUtil.canUserResume(payload.info.userDisplayHints),
|
|
2099
2253
|
canRaiseHand: MeetingUtil.canUserRaiseHand(payload.info.userDisplayHints),
|
|
2100
2254
|
canLowerAllHands: MeetingUtil.canUserLowerAllHands(payload.info.userDisplayHints),
|
|
2101
2255
|
canLowerSomeoneElsesHand: MeetingUtil.canUserLowerSomeoneElsesHand(
|
|
@@ -2127,6 +2281,8 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
2127
2281
|
),
|
|
2128
2282
|
});
|
|
2129
2283
|
|
|
2284
|
+
this.recordingController.setDisplayHints(payload.info.userDisplayHints);
|
|
2285
|
+
|
|
2130
2286
|
if (changed) {
|
|
2131
2287
|
Trigger.trigger(
|
|
2132
2288
|
this,
|
|
@@ -2150,6 +2306,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
2150
2306
|
* @returns {void}
|
|
2151
2307
|
*/
|
|
2152
2308
|
handleDataChannelUrlChange(datachannelUrl) {
|
|
2309
|
+
// @ts-ignore - config coming from registerPlugin
|
|
2153
2310
|
if (datachannelUrl && this.config.enableAutomaticLLM) {
|
|
2154
2311
|
// Defer this as updateLLMConnection relies upon this.locusInfo.url which is only set
|
|
2155
2312
|
// after the MEETING_INFO_UPDATED callback finishes
|
|
@@ -2330,6 +2487,18 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
2330
2487
|
);
|
|
2331
2488
|
});
|
|
2332
2489
|
|
|
2490
|
+
this.locusInfo.on(LOCUSINFO.EVENTS.SELF_MEETING_BREAKOUTS_CHANGED, (payload) => {
|
|
2491
|
+
this.breakouts.updateBreakoutSessions(payload);
|
|
2492
|
+
Trigger.trigger(
|
|
2493
|
+
this,
|
|
2494
|
+
{
|
|
2495
|
+
file: 'meeting/index',
|
|
2496
|
+
function: 'setUpLocusInfoSelfListener',
|
|
2497
|
+
},
|
|
2498
|
+
EVENT_TRIGGERS.MEETING_BREAKOUTS_UPDATE
|
|
2499
|
+
);
|
|
2500
|
+
});
|
|
2501
|
+
|
|
2333
2502
|
this.locusInfo.on(LOCUSINFO.EVENTS.SELF_IS_SHARING_BLOCKED_CHANGE, (payload) => {
|
|
2334
2503
|
Trigger.trigger(
|
|
2335
2504
|
this,
|
|
@@ -2365,7 +2534,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
2365
2534
|
.catch((error) => {
|
|
2366
2535
|
// @ts-ignore
|
|
2367
2536
|
LoggerProxy.logger.error(
|
|
2368
|
-
`Meeting:index#setUpLocusInfoMeetingListener --> REMOTE_RESPONSE. Issue with leave for meeting, meeting still in collection: ${this
|
|
2537
|
+
`Meeting:index#setUpLocusInfoMeetingListener --> REMOTE_RESPONSE. Issue with leave for meeting, meeting still in collection: ${this}, error: ${error}`
|
|
2369
2538
|
);
|
|
2370
2539
|
});
|
|
2371
2540
|
}
|
|
@@ -2400,7 +2569,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
2400
2569
|
.catch((error) => {
|
|
2401
2570
|
// @ts-ignore
|
|
2402
2571
|
LoggerProxy.logger.error(
|
|
2403
|
-
`Meeting:index#setUpLocusInfoMeetingListener --> DESTROY_MEETING. Issue with leave for meeting, meeting still in collection: ${this
|
|
2572
|
+
`Meeting:index#setUpLocusInfoMeetingListener --> DESTROY_MEETING. Issue with leave for meeting, meeting still in collection: ${this}, error: ${error}`
|
|
2404
2573
|
);
|
|
2405
2574
|
});
|
|
2406
2575
|
} else {
|
|
@@ -2642,6 +2811,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
2642
2811
|
this.locusUrl = locusMeetingObject?.url || webexMeetingInfo?.locusUrl || this.locusUrl;
|
|
2643
2812
|
// @ts-ignore - config coming from registerPlugin
|
|
2644
2813
|
this.setSipUri(
|
|
2814
|
+
// @ts-ignore
|
|
2645
2815
|
this.config.experimental.enableUnifiedMeetings
|
|
2646
2816
|
? locusMeetingObject?.info.sipUri || webexMeetingInfo?.sipUrl
|
|
2647
2817
|
: locusMeetingObject?.info.sipUri || webexMeetingInfo?.sipMeetingUri || this.sipUri
|
|
@@ -3590,6 +3760,20 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3590
3760
|
return false;
|
|
3591
3761
|
}
|
|
3592
3762
|
|
|
3763
|
+
/**
|
|
3764
|
+
* Check if the meeting supports the Reactions
|
|
3765
|
+
* @returns {boolean}
|
|
3766
|
+
*/
|
|
3767
|
+
isReactionsSupported() {
|
|
3768
|
+
if (this.locusInfo?.controls?.reactions.enabled) {
|
|
3769
|
+
return true;
|
|
3770
|
+
}
|
|
3771
|
+
|
|
3772
|
+
LoggerProxy.logger.error('Meeting:index#isReactionsSupported --> Reactions is not supported');
|
|
3773
|
+
|
|
3774
|
+
return false;
|
|
3775
|
+
}
|
|
3776
|
+
|
|
3593
3777
|
/**
|
|
3594
3778
|
* Monitor the Low-Latency Mercury (LLM) web socket connection on `onError` and `onClose` states
|
|
3595
3779
|
* @private
|
|
@@ -3641,6 +3825,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3641
3825
|
// @ts-ignore - fix type
|
|
3642
3826
|
const {
|
|
3643
3827
|
body: {webSocketUrl},
|
|
3828
|
+
// @ts-ignore
|
|
3644
3829
|
} = await this.request({
|
|
3645
3830
|
method: HTTP_VERBS.POST,
|
|
3646
3831
|
uri: datachannelUrl,
|
|
@@ -3690,6 +3875,44 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3690
3875
|
}
|
|
3691
3876
|
}
|
|
3692
3877
|
|
|
3878
|
+
/**
|
|
3879
|
+
* Callback called when a relay event is received from meeting LLM Connection
|
|
3880
|
+
* @param {RelayEvent} e Event object coming from LLM Connection
|
|
3881
|
+
* @private
|
|
3882
|
+
* @returns {void}
|
|
3883
|
+
*/
|
|
3884
|
+
private processRelayEvent = (e: RelayEvent): void => {
|
|
3885
|
+
switch (e.data.relayType) {
|
|
3886
|
+
case REACTION_RELAY_TYPES.REACTION:
|
|
3887
|
+
if (
|
|
3888
|
+
// @ts-ignore - config coming from registerPlugin
|
|
3889
|
+
(this.config.receiveReactions || options.receiveReactions) &&
|
|
3890
|
+
this.isReactionsSupported()
|
|
3891
|
+
) {
|
|
3892
|
+
const {name} = this.members.membersCollection.get(e.data.sender.participantId);
|
|
3893
|
+
const processedReaction: ProcessedReaction = {
|
|
3894
|
+
reaction: e.data.reaction,
|
|
3895
|
+
sender: {
|
|
3896
|
+
id: e.data.sender.participantId,
|
|
3897
|
+
name,
|
|
3898
|
+
},
|
|
3899
|
+
};
|
|
3900
|
+
Trigger.trigger(
|
|
3901
|
+
this,
|
|
3902
|
+
{
|
|
3903
|
+
file: 'meeting/index',
|
|
3904
|
+
function: 'join',
|
|
3905
|
+
},
|
|
3906
|
+
EVENT_TRIGGERS.MEETING_RECEIVE_REACTIONS,
|
|
3907
|
+
processedReaction
|
|
3908
|
+
);
|
|
3909
|
+
}
|
|
3910
|
+
break;
|
|
3911
|
+
default:
|
|
3912
|
+
break;
|
|
3913
|
+
}
|
|
3914
|
+
};
|
|
3915
|
+
|
|
3693
3916
|
/**
|
|
3694
3917
|
* stop recieving Transcription by closing
|
|
3695
3918
|
* the web socket connection properly
|
|
@@ -3863,8 +4086,12 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3863
4086
|
return join;
|
|
3864
4087
|
})
|
|
3865
4088
|
.then(async (join) => {
|
|
4089
|
+
// @ts-ignore - config coming from registerPlugin
|
|
3866
4090
|
if (this.config.enableAutomaticLLM) {
|
|
3867
4091
|
await this.updateLLMConnection();
|
|
4092
|
+
// @ts-ignore - Fix type
|
|
4093
|
+
this.webex.internal.llm.on('event:relay.event', this.processRelayEvent);
|
|
4094
|
+
LoggerProxy.logger.info('Meeting:index#join --> enabled to receive relay events!');
|
|
3868
4095
|
}
|
|
3869
4096
|
|
|
3870
4097
|
return join;
|
|
@@ -3931,21 +4158,28 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3931
4158
|
* @returns {Promise}
|
|
3932
4159
|
*/
|
|
3933
4160
|
async updateLLMConnection() {
|
|
4161
|
+
// @ts-ignore - Fix type
|
|
3934
4162
|
const {url, info: {datachannelUrl} = {}} = this.locusInfo;
|
|
3935
4163
|
|
|
3936
4164
|
const isJoined = this.joinedWith && this.joinedWith.state === 'JOINED';
|
|
3937
4165
|
|
|
4166
|
+
// @ts-ignore - Fix type
|
|
3938
4167
|
if (this.webex.internal.llm.isConnected()) {
|
|
4168
|
+
// @ts-ignore - Fix type
|
|
3939
4169
|
if (url === this.webex.internal.llm.getLocusUrl() && isJoined) {
|
|
3940
4170
|
return undefined;
|
|
3941
4171
|
}
|
|
4172
|
+
// @ts-ignore - Fix type
|
|
3942
4173
|
await this.webex.internal.llm.disconnectLLM();
|
|
4174
|
+
// @ts-ignore - Fix type
|
|
4175
|
+
this.webex.internal.llm.off('event:relay.event', this.processRelayEvent);
|
|
3943
4176
|
}
|
|
3944
4177
|
|
|
3945
4178
|
if (!isJoined) {
|
|
3946
4179
|
return undefined;
|
|
3947
4180
|
}
|
|
3948
4181
|
|
|
4182
|
+
// @ts-ignore - Fix type
|
|
3949
4183
|
return this.webex.internal.llm.registerAndConnect(url, datachannelUrl);
|
|
3950
4184
|
}
|
|
3951
4185
|
|
|
@@ -3988,28 +4222,31 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3988
4222
|
|
|
3989
4223
|
if (!this.dialInUrl) this.dialInUrl = `dialin:///${uuid.v4()}`;
|
|
3990
4224
|
|
|
3991
|
-
return
|
|
3992
|
-
.
|
|
3993
|
-
|
|
3994
|
-
|
|
3995
|
-
|
|
3996
|
-
|
|
3997
|
-
|
|
3998
|
-
|
|
3999
|
-
|
|
4000
|
-
|
|
4001
|
-
|
|
4002
|
-
|
|
4003
|
-
|
|
4004
|
-
|
|
4005
|
-
|
|
4006
|
-
|
|
4007
|
-
|
|
4008
|
-
|
|
4009
|
-
|
|
4225
|
+
return (
|
|
4226
|
+
this.meetingRequest
|
|
4227
|
+
// @ts-ignore
|
|
4228
|
+
.dialIn({
|
|
4229
|
+
correlationId,
|
|
4230
|
+
dialInUrl: this.dialInUrl,
|
|
4231
|
+
locusUrl,
|
|
4232
|
+
clientUrl: this.deviceUrl,
|
|
4233
|
+
})
|
|
4234
|
+
.then((res) => {
|
|
4235
|
+
this.locusInfo.onFullLocus(res.body.locus);
|
|
4236
|
+
})
|
|
4237
|
+
.catch((error) => {
|
|
4238
|
+
Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.ADD_DIAL_IN_FAILURE, {
|
|
4239
|
+
correlation_id: this.correlationId,
|
|
4240
|
+
dial_in_url: this.dialInUrl,
|
|
4241
|
+
locus_id: locusUrl.split('/').pop(),
|
|
4242
|
+
client_url: this.deviceUrl,
|
|
4243
|
+
reason: error.error?.message,
|
|
4244
|
+
stack: error.stack,
|
|
4245
|
+
});
|
|
4010
4246
|
|
|
4011
|
-
|
|
4012
|
-
|
|
4247
|
+
return Promise.reject(error);
|
|
4248
|
+
})
|
|
4249
|
+
);
|
|
4013
4250
|
}
|
|
4014
4251
|
|
|
4015
4252
|
/**
|
|
@@ -4026,29 +4263,32 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4026
4263
|
|
|
4027
4264
|
if (!this.dialOutUrl) this.dialOutUrl = `dialout:///${uuid.v4()}`;
|
|
4028
4265
|
|
|
4029
|
-
return
|
|
4030
|
-
.
|
|
4031
|
-
|
|
4032
|
-
|
|
4033
|
-
|
|
4034
|
-
|
|
4035
|
-
|
|
4036
|
-
|
|
4037
|
-
|
|
4038
|
-
|
|
4039
|
-
|
|
4040
|
-
|
|
4041
|
-
|
|
4042
|
-
|
|
4043
|
-
|
|
4044
|
-
|
|
4045
|
-
|
|
4046
|
-
|
|
4047
|
-
|
|
4048
|
-
|
|
4266
|
+
return (
|
|
4267
|
+
this.meetingRequest
|
|
4268
|
+
// @ts-ignore
|
|
4269
|
+
.dialOut({
|
|
4270
|
+
correlationId,
|
|
4271
|
+
dialOutUrl: this.dialOutUrl,
|
|
4272
|
+
phoneNumber,
|
|
4273
|
+
locusUrl,
|
|
4274
|
+
clientUrl: this.deviceUrl,
|
|
4275
|
+
})
|
|
4276
|
+
.then((res) => {
|
|
4277
|
+
this.locusInfo.onFullLocus(res.body.locus);
|
|
4278
|
+
})
|
|
4279
|
+
.catch((error) => {
|
|
4280
|
+
Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.ADD_DIAL_OUT_FAILURE, {
|
|
4281
|
+
correlation_id: this.correlationId,
|
|
4282
|
+
dial_out_url: this.dialOutUrl,
|
|
4283
|
+
locus_id: locusUrl.split('/').pop(),
|
|
4284
|
+
client_url: this.deviceUrl,
|
|
4285
|
+
reason: error.error?.message,
|
|
4286
|
+
stack: error.stack,
|
|
4287
|
+
});
|
|
4049
4288
|
|
|
4050
|
-
|
|
4051
|
-
|
|
4289
|
+
return Promise.reject(error);
|
|
4290
|
+
})
|
|
4291
|
+
);
|
|
4052
4292
|
}
|
|
4053
4293
|
|
|
4054
4294
|
/**
|
|
@@ -4395,7 +4635,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4395
4635
|
Metrics.sendBehavioralMetric(metricName, data, metadata);
|
|
4396
4636
|
};
|
|
4397
4637
|
|
|
4398
|
-
if (error instanceof
|
|
4638
|
+
if (error instanceof Errors.SdpOfferCreationError) {
|
|
4399
4639
|
sendBehavioralMetric(BEHAVIORAL_METRICS.PEERCONNECTION_FAILURE, error, this.id);
|
|
4400
4640
|
|
|
4401
4641
|
Metrics.postEvent({
|
|
@@ -4409,8 +4649,8 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4409
4649
|
},
|
|
4410
4650
|
});
|
|
4411
4651
|
} else if (
|
|
4412
|
-
error instanceof
|
|
4413
|
-
error instanceof
|
|
4652
|
+
error instanceof Errors.SdpOfferHandlingError ||
|
|
4653
|
+
error instanceof Errors.SdpAnswerHandlingError
|
|
4414
4654
|
) {
|
|
4415
4655
|
sendBehavioralMetric(BEHAVIORAL_METRICS.PEERCONNECTION_FAILURE, error, this.id);
|
|
4416
4656
|
|
|
@@ -4424,8 +4664,8 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4424
4664
|
],
|
|
4425
4665
|
},
|
|
4426
4666
|
});
|
|
4427
|
-
} else if (error instanceof
|
|
4428
|
-
// this covers also the case of
|
|
4667
|
+
} else if (error instanceof Errors.SdpError) {
|
|
4668
|
+
// this covers also the case of Errors.IceGatheringError which extends Errors.SdpError
|
|
4429
4669
|
sendBehavioralMetric(BEHAVIORAL_METRICS.INVALID_ICE_CANDIDATE, error, this.id);
|
|
4430
4670
|
|
|
4431
4671
|
Metrics.postEvent({
|
|
@@ -4442,19 +4682,19 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4442
4682
|
};
|
|
4443
4683
|
|
|
4444
4684
|
setupMediaConnectionListeners = () => {
|
|
4445
|
-
this.mediaProperties.webrtcMediaConnection.on(
|
|
4685
|
+
this.mediaProperties.webrtcMediaConnection.on(Event.ROAP_STARTED, () => {
|
|
4446
4686
|
this.isRoapInProgress = true;
|
|
4447
4687
|
});
|
|
4448
4688
|
|
|
4449
|
-
this.mediaProperties.webrtcMediaConnection.on(
|
|
4689
|
+
this.mediaProperties.webrtcMediaConnection.on(Event.ROAP_DONE, () => {
|
|
4450
4690
|
this.mediaNegotiatedEvent();
|
|
4451
4691
|
this.isRoapInProgress = false;
|
|
4452
4692
|
this.processNextQueuedMediaUpdate();
|
|
4453
4693
|
});
|
|
4454
4694
|
|
|
4455
|
-
this.mediaProperties.webrtcMediaConnection.on(
|
|
4695
|
+
this.mediaProperties.webrtcMediaConnection.on(Event.ROAP_FAILURE, this.handleRoapFailure);
|
|
4456
4696
|
|
|
4457
|
-
this.mediaProperties.webrtcMediaConnection.on(
|
|
4697
|
+
this.mediaProperties.webrtcMediaConnection.on(Event.ROAP_MESSAGE_TO_SEND, (event) => {
|
|
4458
4698
|
const LOG_HEADER = 'Meeting:index#setupMediaConnectionListeners.ROAP_MESSAGE_TO_SEND -->';
|
|
4459
4699
|
|
|
4460
4700
|
switch (event.roapMessage.messageType) {
|
|
@@ -4536,8 +4776,8 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4536
4776
|
|
|
4537
4777
|
case 'ERROR':
|
|
4538
4778
|
if (
|
|
4539
|
-
event.roapMessage.errorType ===
|
|
4540
|
-
event.roapMessage.errorType ===
|
|
4779
|
+
event.roapMessage.errorType === ErrorType.CONFLICT ||
|
|
4780
|
+
event.roapMessage.errorType === ErrorType.DOUBLECONFLICT
|
|
4541
4781
|
) {
|
|
4542
4782
|
Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.ROAP_GLARE_CONDITION, {
|
|
4543
4783
|
correlation_id: this.correlationId,
|
|
@@ -4569,7 +4809,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4569
4809
|
});
|
|
4570
4810
|
|
|
4571
4811
|
// eslint-disable-next-line no-param-reassign
|
|
4572
|
-
this.mediaProperties.webrtcMediaConnection.on(
|
|
4812
|
+
this.mediaProperties.webrtcMediaConnection.on(Event.REMOTE_TRACK_ADDED, (event) => {
|
|
4573
4813
|
LoggerProxy.logger.log(
|
|
4574
4814
|
`Meeting:index#setupMediaConnectionListeners --> REMOTE_TRACK_ADDED event received for webrtcMediaConnection: ${JSON.stringify(
|
|
4575
4815
|
event
|
|
@@ -4582,15 +4822,15 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4582
4822
|
let eventType;
|
|
4583
4823
|
|
|
4584
4824
|
switch (event.type) {
|
|
4585
|
-
case
|
|
4825
|
+
case RemoteTrackType.AUDIO:
|
|
4586
4826
|
eventType = EVENT_TYPES.REMOTE_AUDIO;
|
|
4587
4827
|
this.mediaProperties.setRemoteAudioTrack(event.track);
|
|
4588
4828
|
break;
|
|
4589
|
-
case
|
|
4829
|
+
case RemoteTrackType.VIDEO:
|
|
4590
4830
|
eventType = EVENT_TYPES.REMOTE_VIDEO;
|
|
4591
4831
|
this.mediaProperties.setRemoteVideoTrack(event.track);
|
|
4592
4832
|
break;
|
|
4593
|
-
case
|
|
4833
|
+
case RemoteTrackType.SCREENSHARE_VIDEO:
|
|
4594
4834
|
if (event.track) {
|
|
4595
4835
|
eventType = EVENT_TYPES.REMOTE_SHARE;
|
|
4596
4836
|
this.mediaProperties.setRemoteShare(event.track);
|
|
@@ -4623,7 +4863,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4623
4863
|
}
|
|
4624
4864
|
});
|
|
4625
4865
|
|
|
4626
|
-
this.mediaProperties.webrtcMediaConnection.on(
|
|
4866
|
+
this.mediaProperties.webrtcMediaConnection.on(Event.CONNECTION_STATE_CHANGED, (event) => {
|
|
4627
4867
|
const connectionFailed = () => {
|
|
4628
4868
|
// we know the media connection failed and browser will not attempt to recover it any more
|
|
4629
4869
|
// so reset the timer as it's not needed anymore, we want to reconnect immediately
|
|
@@ -4656,10 +4896,10 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4656
4896
|
`Meeting:index#setupMediaConnectionListeners --> connection state changed to ${event.state}`
|
|
4657
4897
|
);
|
|
4658
4898
|
switch (event.state) {
|
|
4659
|
-
case
|
|
4899
|
+
case ConnectionState.Connecting:
|
|
4660
4900
|
Metrics.postEvent({event: eventType.ICE_START, meeting: this});
|
|
4661
4901
|
break;
|
|
4662
|
-
case
|
|
4902
|
+
case ConnectionState.Connected:
|
|
4663
4903
|
Metrics.postEvent({event: eventType.ICE_END, meeting: this});
|
|
4664
4904
|
Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.CONNECTION_SUCCESS, {
|
|
4665
4905
|
correlation_id: this.correlationId,
|
|
@@ -4668,7 +4908,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4668
4908
|
this.setNetworkStatus(NETWORK_STATUS.CONNECTED);
|
|
4669
4909
|
this.reconnectionManager.iceReconnected();
|
|
4670
4910
|
break;
|
|
4671
|
-
case
|
|
4911
|
+
case ConnectionState.Disconnected:
|
|
4672
4912
|
this.setNetworkStatus(NETWORK_STATUS.DISCONNECTED);
|
|
4673
4913
|
this.reconnectionManager.waitForIceReconnect().catch(() => {
|
|
4674
4914
|
LoggerProxy.logger.info(
|
|
@@ -4678,7 +4918,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4678
4918
|
connectionFailed();
|
|
4679
4919
|
});
|
|
4680
4920
|
break;
|
|
4681
|
-
case
|
|
4921
|
+
case ConnectionState.Failed:
|
|
4682
4922
|
connectionFailed();
|
|
4683
4923
|
break;
|
|
4684
4924
|
default:
|
|
@@ -4686,7 +4926,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4686
4926
|
}
|
|
4687
4927
|
});
|
|
4688
4928
|
|
|
4689
|
-
this.mediaProperties.webrtcMediaConnection.on(
|
|
4929
|
+
this.mediaProperties.webrtcMediaConnection.on(Event.ACTIVE_SPEAKERS_CHANGED, (msg) => {
|
|
4690
4930
|
Trigger.trigger(
|
|
4691
4931
|
this,
|
|
4692
4932
|
{
|
|
@@ -4697,6 +4937,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4697
4937
|
{
|
|
4698
4938
|
seqNum: msg.seqNum,
|
|
4699
4939
|
memberIds: msg.csis
|
|
4940
|
+
// @ts-ignore
|
|
4700
4941
|
.map((csi) => this.members.findMemberByCsi(csi)?.id)
|
|
4701
4942
|
.filter((item) => item !== undefined),
|
|
4702
4943
|
}
|
|
@@ -4704,7 +4945,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4704
4945
|
});
|
|
4705
4946
|
|
|
4706
4947
|
this.mediaProperties.webrtcMediaConnection.on(
|
|
4707
|
-
|
|
4948
|
+
Event.VIDEO_SOURCES_COUNT_CHANGED,
|
|
4708
4949
|
(numTotalSources, numLiveSources) => {
|
|
4709
4950
|
Trigger.trigger(
|
|
4710
4951
|
this,
|
|
@@ -4722,7 +4963,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4722
4963
|
);
|
|
4723
4964
|
|
|
4724
4965
|
this.mediaProperties.webrtcMediaConnection.on(
|
|
4725
|
-
|
|
4966
|
+
Event.AUDIO_SOURCES_COUNT_CHANGED,
|
|
4726
4967
|
(numTotalSources, numLiveSources) => {
|
|
4727
4968
|
Trigger.trigger(
|
|
4728
4969
|
this,
|
|
@@ -4752,6 +4993,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4752
4993
|
// Add ip address info if geo hint is present
|
|
4753
4994
|
// @ts-ignore fix type
|
|
4754
4995
|
options.data.intervalMetadata.peerReflexiveIP =
|
|
4996
|
+
// @ts-ignore
|
|
4755
4997
|
this.webex.meetings.geoHintInfo?.clientAddress ||
|
|
4756
4998
|
options.data.intervalMetadata.peerReflexiveIP ||
|
|
4757
4999
|
MQA_STATS.DEFAULT_IP;
|
|
@@ -5082,7 +5324,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5082
5324
|
this
|
|
5083
5325
|
);
|
|
5084
5326
|
|
|
5085
|
-
if (error instanceof
|
|
5327
|
+
if (error instanceof Errors.SdpError) {
|
|
5086
5328
|
this.leave({reason: MEETING_REMOVED_REASON.MEETING_CONNECTION_FAILED});
|
|
5087
5329
|
}
|
|
5088
5330
|
|
|
@@ -5110,7 +5352,14 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5110
5352
|
* @private
|
|
5111
5353
|
* @memberof Meeting
|
|
5112
5354
|
*/
|
|
5113
|
-
private enqueueMediaUpdate(mediaUpdateType: string, options:
|
|
5355
|
+
private enqueueMediaUpdate(mediaUpdateType: string, options: any) {
|
|
5356
|
+
if (mediaUpdateType === MEDIA_UPDATE_TYPE.LAMBDA && typeof options?.lambda !== 'function') {
|
|
5357
|
+
return Promise.reject(
|
|
5358
|
+
new Error('lambda must be specified when enqueuing MEDIA_UPDATE_TYPE.LAMBDA')
|
|
5359
|
+
);
|
|
5360
|
+
}
|
|
5361
|
+
const canUpdateMediaNow = this.canUpdateMedia();
|
|
5362
|
+
|
|
5114
5363
|
return new Promise((resolve, reject) => {
|
|
5115
5364
|
const queueItem = {
|
|
5116
5365
|
pendingPromiseResolve: resolve,
|
|
@@ -5123,6 +5372,10 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5123
5372
|
`Meeting:index#enqueueMediaUpdate --> enqueuing media update type=${mediaUpdateType}`
|
|
5124
5373
|
);
|
|
5125
5374
|
this.queuedMediaUpdates.push(queueItem);
|
|
5375
|
+
|
|
5376
|
+
if (canUpdateMediaNow) {
|
|
5377
|
+
this.processNextQueuedMediaUpdate();
|
|
5378
|
+
}
|
|
5126
5379
|
});
|
|
5127
5380
|
}
|
|
5128
5381
|
|
|
@@ -5175,6 +5428,9 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5175
5428
|
case MEDIA_UPDATE_TYPE.SHARE:
|
|
5176
5429
|
this.updateShare(options).then(pendingPromiseResolve, pendingPromiseReject);
|
|
5177
5430
|
break;
|
|
5431
|
+
case MEDIA_UPDATE_TYPE.LAMBDA:
|
|
5432
|
+
options.lambda().then(pendingPromiseResolve, pendingPromiseReject);
|
|
5433
|
+
break;
|
|
5178
5434
|
default:
|
|
5179
5435
|
LoggerProxy.logger.error(
|
|
5180
5436
|
`Peer-connection-manager:index#processNextQueuedMediaUpdate --> unsupported media update type ${mediaUpdateType} found in the queue`
|
|
@@ -5259,17 +5515,26 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5259
5515
|
// now it's called independently from the roap message (so might be before it), check if that's OK
|
|
5260
5516
|
// if not, ensure it's called after (now it's called after roap message is sent out, but we're not
|
|
5261
5517
|
// waiting for sendRoapMediaRequest() to be resolved)
|
|
5262
|
-
.then(() =>
|
|
5263
|
-
|
|
5264
|
-
|
|
5265
|
-
|
|
5266
|
-
|
|
5267
|
-
|
|
5268
|
-
|
|
5269
|
-
|
|
5270
|
-
|
|
5271
|
-
|
|
5272
|
-
|
|
5518
|
+
.then(() =>
|
|
5519
|
+
this.enqueueMediaUpdate(MEDIA_UPDATE_TYPE.LAMBDA, {
|
|
5520
|
+
lambda: () => {
|
|
5521
|
+
return Promise.resolve()
|
|
5522
|
+
.then(() =>
|
|
5523
|
+
this.checkForStopShare(mediaSettings.sendShare, previousSendShareStatus)
|
|
5524
|
+
)
|
|
5525
|
+
.then((startShare) => {
|
|
5526
|
+
// This is a special case if we do an /floor grant followed by /media
|
|
5527
|
+
// we actually get a OFFER from the server and a GLAR condition happens
|
|
5528
|
+
if (startShare) {
|
|
5529
|
+
// We are assuming that the clients are connected when doing an update
|
|
5530
|
+
return this.requestScreenShareFloor();
|
|
5531
|
+
}
|
|
5532
|
+
|
|
5533
|
+
return Promise.resolve();
|
|
5534
|
+
});
|
|
5535
|
+
},
|
|
5536
|
+
})
|
|
5537
|
+
)
|
|
5273
5538
|
);
|
|
5274
5539
|
}
|
|
5275
5540
|
|
|
@@ -5465,13 +5730,17 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5465
5730
|
remoteQualityLevel: this.mediaProperties.remoteQualityLevel,
|
|
5466
5731
|
},
|
|
5467
5732
|
})
|
|
5468
|
-
.then(() =>
|
|
5469
|
-
|
|
5470
|
-
|
|
5471
|
-
|
|
5733
|
+
.then(() =>
|
|
5734
|
+
this.enqueueMediaUpdate(MEDIA_UPDATE_TYPE.LAMBDA, {
|
|
5735
|
+
lambda: async () => {
|
|
5736
|
+
if (startShare) {
|
|
5737
|
+
return this.requestScreenShareFloor();
|
|
5738
|
+
}
|
|
5472
5739
|
|
|
5473
|
-
|
|
5474
|
-
|
|
5740
|
+
return undefined;
|
|
5741
|
+
},
|
|
5742
|
+
})
|
|
5743
|
+
)
|
|
5475
5744
|
)
|
|
5476
5745
|
.then(() => {
|
|
5477
5746
|
this.mediaProperties.mediaDirection.sendShare = sendShare;
|
|
@@ -5852,7 +6121,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5852
6121
|
* @memberof Meeting
|
|
5853
6122
|
*/
|
|
5854
6123
|
public startRecording() {
|
|
5855
|
-
return
|
|
6124
|
+
return this.recordingController.startRecording();
|
|
5856
6125
|
}
|
|
5857
6126
|
|
|
5858
6127
|
/**
|
|
@@ -5862,7 +6131,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5862
6131
|
* @memberof Meeting
|
|
5863
6132
|
*/
|
|
5864
6133
|
public stopRecording() {
|
|
5865
|
-
return
|
|
6134
|
+
return this.recordingController.stopRecording();
|
|
5866
6135
|
}
|
|
5867
6136
|
|
|
5868
6137
|
/**
|
|
@@ -5872,7 +6141,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5872
6141
|
* @memberof Meeting
|
|
5873
6142
|
*/
|
|
5874
6143
|
public pauseRecording() {
|
|
5875
|
-
return
|
|
6144
|
+
return this.recordingController.pauseRecording();
|
|
5876
6145
|
}
|
|
5877
6146
|
|
|
5878
6147
|
/**
|
|
@@ -5882,7 +6151,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5882
6151
|
* @memberof Meeting
|
|
5883
6152
|
*/
|
|
5884
6153
|
public resumeRecording() {
|
|
5885
|
-
return
|
|
6154
|
+
return this.recordingController.resumeRecording();
|
|
5886
6155
|
}
|
|
5887
6156
|
|
|
5888
6157
|
/**
|