@webex/plugin-meetings 3.0.0-next.2 → 3.0.0-next.21
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 +1 -1
- package/dist/breakouts/index.js +1 -1
- package/dist/constants.d.ts +3 -9
- package/dist/constants.js +4 -9
- package/dist/constants.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/interpretation/index.js +3 -3
- package/dist/interpretation/index.js.map +1 -1
- package/dist/interpretation/siLanguage.js +1 -1
- package/dist/locus-info/mediaSharesUtils.js +15 -1
- package/dist/locus-info/mediaSharesUtils.js.map +1 -1
- package/dist/media/index.js +4 -1
- package/dist/media/index.js.map +1 -1
- package/dist/mediaQualityMetrics/config.d.ts +9 -1
- package/dist/mediaQualityMetrics/config.js +10 -1
- package/dist/mediaQualityMetrics/config.js.map +1 -1
- package/dist/meeting/index.d.ts +18 -7
- package/dist/meeting/index.js +745 -567
- package/dist/meeting/index.js.map +1 -1
- package/dist/meeting/muteState.d.ts +2 -8
- package/dist/meeting/muteState.js +37 -25
- package/dist/meeting/muteState.js.map +1 -1
- package/dist/meeting/request.d.ts +3 -0
- package/dist/meeting/request.js +32 -23
- package/dist/meeting/request.js.map +1 -1
- package/dist/meeting/util.js +1 -0
- package/dist/meeting/util.js.map +1 -1
- package/dist/multistream/mediaRequestManager.d.ts +1 -2
- package/dist/multistream/mediaRequestManager.js.map +1 -1
- package/dist/multistream/remoteMediaGroup.d.ts +1 -1
- package/dist/multistream/remoteMediaGroup.js.map +1 -1
- package/dist/multistream/remoteMediaManager.d.ts +1 -2
- package/dist/multistream/remoteMediaManager.js.map +1 -1
- package/dist/multistream/sendSlotManager.d.ts +1 -2
- package/dist/multistream/sendSlotManager.js.map +1 -1
- package/dist/reachability/request.js +12 -10
- package/dist/reachability/request.js.map +1 -1
- package/dist/reconnection-manager/index.js +2 -1
- package/dist/reconnection-manager/index.js.map +1 -1
- package/dist/roap/index.d.ts +10 -2
- package/dist/roap/index.js +15 -0
- package/dist/roap/index.js.map +1 -1
- package/dist/roap/request.js +3 -3
- package/dist/roap/request.js.map +1 -1
- package/dist/roap/turnDiscovery.d.ts +64 -17
- package/dist/roap/turnDiscovery.js +307 -126
- package/dist/roap/turnDiscovery.js.map +1 -1
- package/dist/statsAnalyzer/index.js +61 -41
- package/dist/statsAnalyzer/index.js.map +1 -1
- package/dist/webinar/index.js +1 -1
- package/package.json +22 -22
- package/src/constants.ts +3 -9
- package/src/index.ts +1 -0
- package/src/interpretation/index.ts +2 -2
- package/src/locus-info/mediaSharesUtils.ts +16 -0
- package/src/media/index.ts +3 -1
- package/src/mediaQualityMetrics/config.ts +11 -1
- package/src/meeting/index.ts +264 -90
- package/src/meeting/muteState.ts +34 -20
- package/src/meeting/request.ts +18 -2
- package/src/meeting/util.ts +1 -0
- package/src/multistream/mediaRequestManager.ts +1 -1
- package/src/multistream/remoteMediaGroup.ts +1 -1
- package/src/multistream/remoteMediaManager.ts +1 -2
- package/src/multistream/sendSlotManager.ts +1 -2
- package/src/reachability/request.ts +15 -11
- package/src/reconnection-manager/index.ts +1 -1
- package/src/roap/index.ts +25 -3
- package/src/roap/request.ts +3 -3
- package/src/roap/turnDiscovery.ts +244 -78
- package/src/statsAnalyzer/index.ts +72 -43
- package/test/integration/spec/journey.js +14 -14
- package/test/integration/spec/space-meeting.js +1 -1
- package/test/unit/spec/interpretation/index.ts +4 -1
- package/test/unit/spec/locus-info/mediaSharesUtils.ts +9 -0
- package/test/unit/spec/media/index.ts +89 -78
- package/test/unit/spec/meeting/index.js +611 -125
- package/test/unit/spec/meeting/muteState.js +219 -67
- package/test/unit/spec/meeting/request.js +21 -0
- package/test/unit/spec/meeting/utils.js +6 -1
- package/test/unit/spec/meetings/index.js +27 -20
- package/test/unit/spec/multistream/remoteMediaGroup.ts +0 -1
- package/test/unit/spec/multistream/remoteMediaManager.ts +0 -1
- package/test/unit/spec/reachability/request.js +15 -7
- package/test/unit/spec/reconnection-manager/index.js +28 -0
- package/test/unit/spec/roap/index.ts +61 -6
- package/test/unit/spec/roap/turnDiscovery.ts +298 -16
- package/test/unit/spec/stats-analyzer/index.js +183 -8
- package/dist/member/member.types.d.ts +0 -11
- package/dist/member/member.types.js +0 -17
- package/dist/member/member.types.js.map +0 -1
- package/src/member/member.types.ts +0 -13
package/src/meeting/index.ts
CHANGED
|
@@ -10,6 +10,8 @@ import {
|
|
|
10
10
|
ClientEventLeaveReason,
|
|
11
11
|
CallDiagnosticUtils,
|
|
12
12
|
} from '@webex/internal-plugin-metrics';
|
|
13
|
+
import {ClientEvent as RawClientEvent} from '@webex/event-dictionary-ts';
|
|
14
|
+
|
|
13
15
|
import {
|
|
14
16
|
ConnectionState,
|
|
15
17
|
Errors,
|
|
@@ -51,7 +53,11 @@ import {StatsAnalyzer, EVENTS as StatsAnalyzerEvents} from '../statsAnalyzer';
|
|
|
51
53
|
import NetworkQualityMonitor from '../networkQualityMonitor';
|
|
52
54
|
import LoggerProxy from '../common/logs/logger-proxy';
|
|
53
55
|
import Trigger from '../common/events/trigger-proxy';
|
|
54
|
-
import Roap
|
|
56
|
+
import Roap, {
|
|
57
|
+
type TurnDiscoveryResult,
|
|
58
|
+
type TurnServerInfo,
|
|
59
|
+
type TurnDiscoverySkipReason,
|
|
60
|
+
} from '../roap/index';
|
|
55
61
|
import Media, {type BundlePolicy} from '../media';
|
|
56
62
|
import MediaProperties from '../media/properties';
|
|
57
63
|
import MeetingStateMachine from './state';
|
|
@@ -120,7 +126,6 @@ import {
|
|
|
120
126
|
MeetingInfoV2CaptchaError,
|
|
121
127
|
MeetingInfoV2PolicyError,
|
|
122
128
|
} from '../meeting-info/meeting-info-v2';
|
|
123
|
-
import BrowserDetection from '../common/browser-detection';
|
|
124
129
|
import {CSI, ReceiveSlotManager} from '../multistream/receiveSlotManager';
|
|
125
130
|
import SendSlotManager from '../multistream/sendSlotManager';
|
|
126
131
|
import {MediaRequestManager} from '../multistream/mediaRequestManager';
|
|
@@ -148,8 +153,6 @@ import ControlsOptionsManager from '../controls-options-manager';
|
|
|
148
153
|
import PermissionError from '../common/errors/permission';
|
|
149
154
|
import {LocusMediaRequest} from './locusMediaRequest';
|
|
150
155
|
|
|
151
|
-
const {isBrowser} = BrowserDetection();
|
|
152
|
-
|
|
153
156
|
const logRequest = (request: any, {logText = ''}) => {
|
|
154
157
|
LoggerProxy.logger.info(`${logText} - sending request`);
|
|
155
158
|
|
|
@@ -615,8 +618,8 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
615
618
|
resourceUrl: string;
|
|
616
619
|
selfId: string;
|
|
617
620
|
state: any;
|
|
618
|
-
localAudioStreamMuteStateHandler: (
|
|
619
|
-
localVideoStreamMuteStateHandler: (
|
|
621
|
+
localAudioStreamMuteStateHandler: () => void;
|
|
622
|
+
localVideoStreamMuteStateHandler: () => void;
|
|
620
623
|
localOutputTrackChangeHandler: () => void;
|
|
621
624
|
roles: any[];
|
|
622
625
|
environment: string;
|
|
@@ -624,7 +627,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
624
627
|
allowMediaInLobby: boolean;
|
|
625
628
|
localShareInstanceId: string;
|
|
626
629
|
remoteShareInstanceId: string;
|
|
627
|
-
turnDiscoverySkippedReason:
|
|
630
|
+
turnDiscoverySkippedReason: TurnDiscoverySkipReason;
|
|
628
631
|
turnServerUsed: boolean;
|
|
629
632
|
areVoiceaEventsSetup = false;
|
|
630
633
|
voiceaListenerCallbacks: object = {
|
|
@@ -1381,12 +1384,12 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
1381
1384
|
*/
|
|
1382
1385
|
this.remoteMediaManager = null;
|
|
1383
1386
|
|
|
1384
|
-
this.localAudioStreamMuteStateHandler = (
|
|
1385
|
-
this.audio.handleLocalStreamMuteStateChange(this
|
|
1387
|
+
this.localAudioStreamMuteStateHandler = () => {
|
|
1388
|
+
this.audio.handleLocalStreamMuteStateChange(this);
|
|
1386
1389
|
};
|
|
1387
1390
|
|
|
1388
|
-
this.localVideoStreamMuteStateHandler = (
|
|
1389
|
-
this.video.handleLocalStreamMuteStateChange(this
|
|
1391
|
+
this.localVideoStreamMuteStateHandler = () => {
|
|
1392
|
+
this.video.handleLocalStreamMuteStateChange(this);
|
|
1390
1393
|
};
|
|
1391
1394
|
|
|
1392
1395
|
// The handling of output track changes should be done inside
|
|
@@ -2519,6 +2522,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
2519
2522
|
{
|
|
2520
2523
|
annotationInfo: contentShare?.annotation,
|
|
2521
2524
|
meetingId: this.id,
|
|
2525
|
+
resourceType: contentShare?.resourceType,
|
|
2522
2526
|
}
|
|
2523
2527
|
);
|
|
2524
2528
|
}
|
|
@@ -2547,7 +2551,8 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
2547
2551
|
contentShare.deviceUrlSharing === previousContentShare.deviceUrlSharing &&
|
|
2548
2552
|
whiteboardShare.beneficiaryId === previousWhiteboardShare?.beneficiaryId &&
|
|
2549
2553
|
whiteboardShare.disposition === previousWhiteboardShare?.disposition &&
|
|
2550
|
-
whiteboardShare.resourceUrl === previousWhiteboardShare?.resourceUrl
|
|
2554
|
+
whiteboardShare.resourceUrl === previousWhiteboardShare?.resourceUrl &&
|
|
2555
|
+
contentShare.resourceType === previousContentShare?.resourceType
|
|
2551
2556
|
) {
|
|
2552
2557
|
// nothing changed, so ignore
|
|
2553
2558
|
// (this happens when we steal presentation from remote)
|
|
@@ -2669,6 +2674,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
2669
2674
|
url: contentShare.url,
|
|
2670
2675
|
shareInstanceId: this.remoteShareInstanceId,
|
|
2671
2676
|
annotationInfo: contentShare.annotation,
|
|
2677
|
+
resourceType: contentShare.resourceType,
|
|
2672
2678
|
}
|
|
2673
2679
|
);
|
|
2674
2680
|
};
|
|
@@ -2761,6 +2767,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
2761
2767
|
url: contentShare.url,
|
|
2762
2768
|
shareInstanceId: this.remoteShareInstanceId,
|
|
2763
2769
|
annotationInfo: contentShare.annotation,
|
|
2770
|
+
resourceType: contentShare.resourceType,
|
|
2764
2771
|
}
|
|
2765
2772
|
);
|
|
2766
2773
|
this.members.locusMediaSharesUpdate(payload);
|
|
@@ -3070,6 +3077,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3070
3077
|
options: {meetingId: this.id},
|
|
3071
3078
|
});
|
|
3072
3079
|
}
|
|
3080
|
+
this.updateLLMConnection();
|
|
3073
3081
|
});
|
|
3074
3082
|
|
|
3075
3083
|
// @ts-ignore - check if MEDIA_INACTIVITY exists
|
|
@@ -3454,8 +3462,10 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3454
3462
|
this.owner =
|
|
3455
3463
|
locusMeetingObject?.info.owner || meetingInfo?.owner || meetingInfo?.hostId || this.owner;
|
|
3456
3464
|
this.permissionToken = meetingInfo?.permissionToken;
|
|
3457
|
-
this.
|
|
3458
|
-
|
|
3465
|
+
if (this.permissionToken) {
|
|
3466
|
+
this.setPermissionTokenPayload(meetingInfo?.permissionToken);
|
|
3467
|
+
this.setSelfUserPolicies();
|
|
3468
|
+
}
|
|
3459
3469
|
// Need to populate environment when sending CA event
|
|
3460
3470
|
this.environment = locusMeetingObject?.info.channel || meetingInfo?.channel;
|
|
3461
3471
|
}
|
|
@@ -3891,7 +3901,14 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3891
3901
|
private async setLocalAudioStream(localStream?: LocalMicrophoneStream) {
|
|
3892
3902
|
const oldStream = this.mediaProperties.audioStream;
|
|
3893
3903
|
|
|
3894
|
-
oldStream?.off(
|
|
3904
|
+
oldStream?.off(
|
|
3905
|
+
LocalStreamEventNames.UserMuteStateChange,
|
|
3906
|
+
this.localAudioStreamMuteStateHandler
|
|
3907
|
+
);
|
|
3908
|
+
oldStream?.off(
|
|
3909
|
+
LocalStreamEventNames.SystemMuteStateChange,
|
|
3910
|
+
this.localAudioStreamMuteStateHandler
|
|
3911
|
+
);
|
|
3895
3912
|
oldStream?.off(LocalStreamEventNames.OutputTrackChange, this.localOutputTrackChangeHandler);
|
|
3896
3913
|
|
|
3897
3914
|
// we don't update this.mediaProperties.mediaDirection.sendAudio, because we always keep it as true to avoid extra SDP exchanges
|
|
@@ -3899,7 +3916,14 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3899
3916
|
|
|
3900
3917
|
this.audio.handleLocalStreamChange(this);
|
|
3901
3918
|
|
|
3902
|
-
localStream?.on(
|
|
3919
|
+
localStream?.on(
|
|
3920
|
+
LocalStreamEventNames.UserMuteStateChange,
|
|
3921
|
+
this.localAudioStreamMuteStateHandler
|
|
3922
|
+
);
|
|
3923
|
+
localStream?.on(
|
|
3924
|
+
LocalStreamEventNames.SystemMuteStateChange,
|
|
3925
|
+
this.localAudioStreamMuteStateHandler
|
|
3926
|
+
);
|
|
3903
3927
|
localStream?.on(LocalStreamEventNames.OutputTrackChange, this.localOutputTrackChangeHandler);
|
|
3904
3928
|
|
|
3905
3929
|
if (!this.isMultistream || !localStream) {
|
|
@@ -3919,7 +3943,14 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3919
3943
|
private async setLocalVideoStream(localStream?: LocalCameraStream) {
|
|
3920
3944
|
const oldStream = this.mediaProperties.videoStream;
|
|
3921
3945
|
|
|
3922
|
-
oldStream?.off(
|
|
3946
|
+
oldStream?.off(
|
|
3947
|
+
LocalStreamEventNames.UserMuteStateChange,
|
|
3948
|
+
this.localVideoStreamMuteStateHandler
|
|
3949
|
+
);
|
|
3950
|
+
oldStream?.off(
|
|
3951
|
+
LocalStreamEventNames.SystemMuteStateChange,
|
|
3952
|
+
this.localVideoStreamMuteStateHandler
|
|
3953
|
+
);
|
|
3923
3954
|
oldStream?.off(LocalStreamEventNames.OutputTrackChange, this.localOutputTrackChangeHandler);
|
|
3924
3955
|
|
|
3925
3956
|
// we don't update this.mediaProperties.mediaDirection.sendVideo, because we always keep it as true to avoid extra SDP exchanges
|
|
@@ -3927,7 +3958,14 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3927
3958
|
|
|
3928
3959
|
this.video.handleLocalStreamChange(this);
|
|
3929
3960
|
|
|
3930
|
-
localStream?.on(
|
|
3961
|
+
localStream?.on(
|
|
3962
|
+
LocalStreamEventNames.UserMuteStateChange,
|
|
3963
|
+
this.localVideoStreamMuteStateHandler
|
|
3964
|
+
);
|
|
3965
|
+
localStream?.on(
|
|
3966
|
+
LocalStreamEventNames.SystemMuteStateChange,
|
|
3967
|
+
this.localVideoStreamMuteStateHandler
|
|
3968
|
+
);
|
|
3931
3969
|
localStream?.on(LocalStreamEventNames.OutputTrackChange, this.localOutputTrackChangeHandler);
|
|
3932
3970
|
|
|
3933
3971
|
if (!this.isMultistream || !localStream) {
|
|
@@ -3948,14 +3986,17 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3948
3986
|
private async setLocalShareVideoStream(localDisplayStream?: LocalDisplayStream) {
|
|
3949
3987
|
const oldStream = this.mediaProperties.shareVideoStream;
|
|
3950
3988
|
|
|
3951
|
-
oldStream?.off(
|
|
3989
|
+
oldStream?.off(
|
|
3990
|
+
LocalStreamEventNames.SystemMuteStateChange,
|
|
3991
|
+
this.handleShareVideoStreamMuteStateChange
|
|
3992
|
+
);
|
|
3952
3993
|
oldStream?.off(StreamEventNames.Ended, this.handleShareVideoStreamEnded);
|
|
3953
3994
|
oldStream?.off(LocalStreamEventNames.OutputTrackChange, this.localOutputTrackChangeHandler);
|
|
3954
3995
|
|
|
3955
3996
|
this.mediaProperties.setLocalShareVideoStream(localDisplayStream);
|
|
3956
3997
|
|
|
3957
3998
|
localDisplayStream?.on(
|
|
3958
|
-
|
|
3999
|
+
LocalStreamEventNames.SystemMuteStateChange,
|
|
3959
4000
|
this.handleShareVideoStreamMuteStateChange
|
|
3960
4001
|
);
|
|
3961
4002
|
localDisplayStream?.on(StreamEventNames.Ended, this.handleShareVideoStreamEnded);
|
|
@@ -4041,10 +4082,24 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4041
4082
|
public cleanupLocalStreams() {
|
|
4042
4083
|
const {audioStream, videoStream, shareAudioStream, shareVideoStream} = this.mediaProperties;
|
|
4043
4084
|
|
|
4044
|
-
audioStream?.off(
|
|
4085
|
+
audioStream?.off(
|
|
4086
|
+
LocalStreamEventNames.UserMuteStateChange,
|
|
4087
|
+
this.localAudioStreamMuteStateHandler
|
|
4088
|
+
);
|
|
4089
|
+
audioStream?.off(
|
|
4090
|
+
LocalStreamEventNames.SystemMuteStateChange,
|
|
4091
|
+
this.localAudioStreamMuteStateHandler
|
|
4092
|
+
);
|
|
4045
4093
|
audioStream?.off(LocalStreamEventNames.OutputTrackChange, this.localOutputTrackChangeHandler);
|
|
4046
4094
|
|
|
4047
|
-
videoStream?.off(
|
|
4095
|
+
videoStream?.off(
|
|
4096
|
+
LocalStreamEventNames.UserMuteStateChange,
|
|
4097
|
+
this.localVideoStreamMuteStateHandler
|
|
4098
|
+
);
|
|
4099
|
+
videoStream?.off(
|
|
4100
|
+
LocalStreamEventNames.SystemMuteStateChange,
|
|
4101
|
+
this.localVideoStreamMuteStateHandler
|
|
4102
|
+
);
|
|
4048
4103
|
videoStream?.off(LocalStreamEventNames.OutputTrackChange, this.localOutputTrackChangeHandler);
|
|
4049
4104
|
|
|
4050
4105
|
shareAudioStream?.off(StreamEventNames.Ended, this.handleShareAudioStreamEnded);
|
|
@@ -4052,8 +4107,9 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4052
4107
|
LocalStreamEventNames.OutputTrackChange,
|
|
4053
4108
|
this.localOutputTrackChangeHandler
|
|
4054
4109
|
);
|
|
4110
|
+
|
|
4055
4111
|
shareVideoStream?.off(
|
|
4056
|
-
|
|
4112
|
+
LocalStreamEventNames.SystemMuteStateChange,
|
|
4057
4113
|
this.handleShareVideoStreamMuteStateChange
|
|
4058
4114
|
);
|
|
4059
4115
|
shareVideoStream?.off(StreamEventNames.Ended, this.handleShareVideoStreamEnded);
|
|
@@ -4445,47 +4501,90 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4445
4501
|
* }
|
|
4446
4502
|
* })
|
|
4447
4503
|
*/
|
|
4448
|
-
public joinWithMedia(
|
|
4504
|
+
public async joinWithMedia(
|
|
4449
4505
|
options: {
|
|
4450
4506
|
joinOptions?: any;
|
|
4451
4507
|
mediaOptions?: AddMediaOptions;
|
|
4452
4508
|
} = {}
|
|
4453
4509
|
) {
|
|
4454
|
-
const {mediaOptions, joinOptions} = options;
|
|
4510
|
+
const {mediaOptions, joinOptions = {}} = options;
|
|
4455
4511
|
|
|
4456
4512
|
if (!mediaOptions?.allowMediaInLobby) {
|
|
4457
4513
|
return Promise.reject(
|
|
4458
4514
|
new ParameterError('joinWithMedia() can only be used with allowMediaInLobby set to true')
|
|
4459
4515
|
);
|
|
4460
4516
|
}
|
|
4517
|
+
this.allowMediaInLobby = true;
|
|
4461
4518
|
|
|
4462
4519
|
LoggerProxy.logger.info('Meeting:index#joinWithMedia called');
|
|
4463
4520
|
|
|
4464
|
-
|
|
4465
|
-
.then((joinResponse) =>
|
|
4466
|
-
this.addMedia(mediaOptions).then((mediaResponse) => ({
|
|
4467
|
-
join: joinResponse,
|
|
4468
|
-
media: mediaResponse,
|
|
4469
|
-
}))
|
|
4470
|
-
)
|
|
4471
|
-
.catch((error) => {
|
|
4472
|
-
LoggerProxy.logger.error('Meeting:index#joinWithMedia --> ', error);
|
|
4521
|
+
let joined = false;
|
|
4473
4522
|
|
|
4474
|
-
|
|
4475
|
-
|
|
4476
|
-
|
|
4477
|
-
correlation_id: this.correlationId,
|
|
4478
|
-
locus_id: this.locusUrl.split('/').pop(),
|
|
4479
|
-
reason: error.message,
|
|
4480
|
-
stack: error.stack,
|
|
4481
|
-
},
|
|
4482
|
-
{
|
|
4483
|
-
type: error.name,
|
|
4484
|
-
}
|
|
4485
|
-
);
|
|
4523
|
+
try {
|
|
4524
|
+
let turnServerInfo;
|
|
4525
|
+
let turnDiscoverySkippedReason;
|
|
4486
4526
|
|
|
4487
|
-
|
|
4488
|
-
|
|
4527
|
+
// @ts-ignore
|
|
4528
|
+
joinOptions.reachability = await this.webex.meetings.reachability.getReachabilityResults();
|
|
4529
|
+
const turnDiscoveryRequest = await this.roap.generateTurnDiscoveryRequestMessage(this, true);
|
|
4530
|
+
|
|
4531
|
+
({turnDiscoverySkippedReason} = turnDiscoveryRequest);
|
|
4532
|
+
joinOptions.roapMessage = turnDiscoveryRequest.roapMessage;
|
|
4533
|
+
|
|
4534
|
+
const joinResponse = await this.join(joinOptions);
|
|
4535
|
+
|
|
4536
|
+
joined = true;
|
|
4537
|
+
|
|
4538
|
+
if (joinOptions.roapMessage) {
|
|
4539
|
+
({turnServerInfo, turnDiscoverySkippedReason} =
|
|
4540
|
+
await this.roap.handleTurnDiscoveryHttpResponse(this, joinResponse));
|
|
4541
|
+
|
|
4542
|
+
this.turnDiscoverySkippedReason = turnDiscoverySkippedReason;
|
|
4543
|
+
this.turnServerUsed = !!turnServerInfo;
|
|
4544
|
+
|
|
4545
|
+
if (turnServerInfo === undefined) {
|
|
4546
|
+
this.roap.abortTurnDiscovery();
|
|
4547
|
+
}
|
|
4548
|
+
}
|
|
4549
|
+
|
|
4550
|
+
const mediaResponse = await this.addMedia(mediaOptions, turnServerInfo);
|
|
4551
|
+
|
|
4552
|
+
return {
|
|
4553
|
+
join: joinResponse,
|
|
4554
|
+
media: mediaResponse,
|
|
4555
|
+
};
|
|
4556
|
+
} catch (error) {
|
|
4557
|
+
LoggerProxy.logger.error('Meeting:index#joinWithMedia --> ', error);
|
|
4558
|
+
|
|
4559
|
+
let leaveError;
|
|
4560
|
+
|
|
4561
|
+
this.roap.abortTurnDiscovery();
|
|
4562
|
+
|
|
4563
|
+
if (joined) {
|
|
4564
|
+
try {
|
|
4565
|
+
await this.leave({resourceId: joinOptions?.resourceId, reason: 'joinWithMedia failure'});
|
|
4566
|
+
} catch (e) {
|
|
4567
|
+
LoggerProxy.logger.error('Meeting:index#joinWithMedia --> leave error', e);
|
|
4568
|
+
leaveError = e;
|
|
4569
|
+
}
|
|
4570
|
+
}
|
|
4571
|
+
|
|
4572
|
+
Metrics.sendBehavioralMetric(
|
|
4573
|
+
BEHAVIORAL_METRICS.JOIN_WITH_MEDIA_FAILURE,
|
|
4574
|
+
{
|
|
4575
|
+
correlation_id: this.correlationId,
|
|
4576
|
+
locus_id: this.locusUrl?.split('/').pop(), // if join fails, we may end up with no locusUrl
|
|
4577
|
+
reason: error.message,
|
|
4578
|
+
stack: error.stack,
|
|
4579
|
+
leaveErrorReason: leaveError?.message,
|
|
4580
|
+
},
|
|
4581
|
+
{
|
|
4582
|
+
type: error.name,
|
|
4583
|
+
}
|
|
4584
|
+
);
|
|
4585
|
+
|
|
4586
|
+
throw error;
|
|
4587
|
+
}
|
|
4489
4588
|
}
|
|
4490
4589
|
|
|
4491
4590
|
/**
|
|
@@ -5608,7 +5707,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5608
5707
|
logText: `${LOG_HEADER} Roap Offer`,
|
|
5609
5708
|
}
|
|
5610
5709
|
).catch(() => {
|
|
5611
|
-
this.deferSDPAnswer.reject();
|
|
5710
|
+
this.deferSDPAnswer.reject(new Error('failed to send ROAP SDP offer'));
|
|
5612
5711
|
clearTimeout(this.sdpResponseTimer);
|
|
5613
5712
|
this.sdpResponseTimer = undefined;
|
|
5614
5713
|
});
|
|
@@ -5955,6 +6054,20 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5955
6054
|
meetingId: this.id,
|
|
5956
6055
|
},
|
|
5957
6056
|
});
|
|
6057
|
+
|
|
6058
|
+
if (data.type === 'share') {
|
|
6059
|
+
// @ts-ignore
|
|
6060
|
+
this.webex.internal.newMetrics.submitClientEvent({
|
|
6061
|
+
name: 'client.media.render.start',
|
|
6062
|
+
payload: {
|
|
6063
|
+
mediaType: 'share',
|
|
6064
|
+
shareInstanceId: this.remoteShareInstanceId,
|
|
6065
|
+
},
|
|
6066
|
+
options: {
|
|
6067
|
+
meetingId: this.id,
|
|
6068
|
+
},
|
|
6069
|
+
});
|
|
6070
|
+
}
|
|
5958
6071
|
});
|
|
5959
6072
|
this.statsAnalyzer.on(StatsAnalyzerEvents.REMOTE_MEDIA_STOPPED, (data) => {
|
|
5960
6073
|
// @ts-ignore
|
|
@@ -5968,6 +6081,20 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5968
6081
|
meetingId: this.id,
|
|
5969
6082
|
},
|
|
5970
6083
|
});
|
|
6084
|
+
|
|
6085
|
+
if (data.type === 'share') {
|
|
6086
|
+
// @ts-ignore
|
|
6087
|
+
this.webex.internal.newMetrics.submitClientEvent({
|
|
6088
|
+
name: 'client.media.render.stop',
|
|
6089
|
+
payload: {
|
|
6090
|
+
mediaType: 'share',
|
|
6091
|
+
shareInstanceId: this.remoteShareInstanceId,
|
|
6092
|
+
},
|
|
6093
|
+
options: {
|
|
6094
|
+
meetingId: this.id,
|
|
6095
|
+
},
|
|
6096
|
+
});
|
|
6097
|
+
}
|
|
5971
6098
|
});
|
|
5972
6099
|
};
|
|
5973
6100
|
|
|
@@ -6074,16 +6201,22 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
6074
6201
|
private async setUpLocalStreamReferences(localStreams: LocalStreams) {
|
|
6075
6202
|
const setUpStreamPromises = [];
|
|
6076
6203
|
|
|
6077
|
-
if (localStreams?.microphone) {
|
|
6204
|
+
if (localStreams?.microphone && localStreams?.microphone?.readyState !== 'ended') {
|
|
6078
6205
|
setUpStreamPromises.push(this.setLocalAudioStream(localStreams.microphone));
|
|
6079
6206
|
}
|
|
6080
|
-
if (localStreams?.camera) {
|
|
6207
|
+
if (localStreams?.camera && localStreams?.camera?.readyState !== 'ended') {
|
|
6081
6208
|
setUpStreamPromises.push(this.setLocalVideoStream(localStreams.camera));
|
|
6082
6209
|
}
|
|
6083
|
-
if (
|
|
6210
|
+
if (
|
|
6211
|
+
localStreams?.screenShare?.video &&
|
|
6212
|
+
localStreams?.screenShare?.video?.readyState !== 'ended'
|
|
6213
|
+
) {
|
|
6084
6214
|
setUpStreamPromises.push(this.setLocalShareVideoStream(localStreams.screenShare.video));
|
|
6085
6215
|
}
|
|
6086
|
-
if (
|
|
6216
|
+
if (
|
|
6217
|
+
localStreams?.screenShare?.audio &&
|
|
6218
|
+
localStreams?.screenShare?.audio?.readyState !== 'ended'
|
|
6219
|
+
) {
|
|
6087
6220
|
setUpStreamPromises.push(this.setLocalShareAudioStream(localStreams.screenShare.audio));
|
|
6088
6221
|
}
|
|
6089
6222
|
|
|
@@ -6328,6 +6461,44 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
6328
6461
|
}
|
|
6329
6462
|
}
|
|
6330
6463
|
|
|
6464
|
+
/**
|
|
6465
|
+
* Performs TURN discovery as a separate call to the Locus /media API
|
|
6466
|
+
*
|
|
6467
|
+
* @param {boolean} isRetry
|
|
6468
|
+
* @param {boolean} isForced
|
|
6469
|
+
* @returns {Promise}
|
|
6470
|
+
*/
|
|
6471
|
+
private async doTurnDiscovery(isRetry: boolean, isForced: boolean): Promise<TurnDiscoveryResult> {
|
|
6472
|
+
// @ts-ignore
|
|
6473
|
+
const cdl = this.webex.internal.newMetrics.callDiagnosticLatencies;
|
|
6474
|
+
|
|
6475
|
+
// @ts-ignore
|
|
6476
|
+
this.webex.internal.newMetrics.submitInternalEvent({
|
|
6477
|
+
name: 'internal.client.add-media.turn-discovery.start',
|
|
6478
|
+
});
|
|
6479
|
+
|
|
6480
|
+
const turnDiscoveryResult = await this.roap.doTurnDiscovery(this, isRetry, isForced);
|
|
6481
|
+
|
|
6482
|
+
this.turnDiscoverySkippedReason = turnDiscoveryResult?.turnDiscoverySkippedReason;
|
|
6483
|
+
this.turnServerUsed = !this.turnDiscoverySkippedReason;
|
|
6484
|
+
|
|
6485
|
+
// @ts-ignore
|
|
6486
|
+
this.webex.internal.newMetrics.submitInternalEvent({
|
|
6487
|
+
name: 'internal.client.add-media.turn-discovery.end',
|
|
6488
|
+
});
|
|
6489
|
+
|
|
6490
|
+
if (this.turnServerUsed && turnDiscoveryResult.turnServerInfo) {
|
|
6491
|
+
Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.TURN_DISCOVERY_LATENCY, {
|
|
6492
|
+
correlation_id: this.correlationId,
|
|
6493
|
+
latency: cdl.getTurnDiscoveryTime(),
|
|
6494
|
+
turnServerUsed: this.turnServerUsed,
|
|
6495
|
+
retriedWithTurnServer: this.retriedWithTurnServer,
|
|
6496
|
+
});
|
|
6497
|
+
}
|
|
6498
|
+
|
|
6499
|
+
return turnDiscoveryResult;
|
|
6500
|
+
}
|
|
6501
|
+
|
|
6331
6502
|
/**
|
|
6332
6503
|
* Does TURN discovery, SDP offer/answer exhange, establishes ICE connection and DTLS handshake.
|
|
6333
6504
|
*
|
|
@@ -6335,43 +6506,21 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
6335
6506
|
* @param {RemoteMediaManagerConfiguration} [remoteMediaManagerConfig]
|
|
6336
6507
|
* @param {BundlePolicy} [bundlePolicy]
|
|
6337
6508
|
* @param {boolean} [isForced] - let isForced be true to do turn discovery regardless of reachability results
|
|
6509
|
+
* @param {TurnServerInfo} [turnServerInfo]
|
|
6338
6510
|
* @returns {Promise<void>}
|
|
6339
6511
|
*/
|
|
6340
6512
|
private async establishMediaConnection(
|
|
6341
6513
|
remoteMediaManagerConfig?: RemoteMediaManagerConfiguration,
|
|
6342
6514
|
bundlePolicy?: BundlePolicy,
|
|
6343
|
-
isForced?: boolean
|
|
6515
|
+
isForced?: boolean,
|
|
6516
|
+
turnServerInfo?: TurnServerInfo
|
|
6344
6517
|
): Promise<void> {
|
|
6345
6518
|
const LOG_HEADER = 'Meeting:index#addMedia():establishMediaConnection -->';
|
|
6346
|
-
// @ts-ignore
|
|
6347
|
-
const cdl = this.webex.internal.newMetrics.callDiagnosticLatencies;
|
|
6348
6519
|
const isRetry = this.retriedWithTurnServer;
|
|
6349
6520
|
|
|
6350
6521
|
try {
|
|
6351
|
-
|
|
6352
|
-
|
|
6353
|
-
name: 'internal.client.add-media.turn-discovery.start',
|
|
6354
|
-
});
|
|
6355
|
-
|
|
6356
|
-
const turnDiscoveryObject = await this.roap.doTurnDiscovery(this, isRetry, isForced);
|
|
6357
|
-
|
|
6358
|
-
this.turnDiscoverySkippedReason = turnDiscoveryObject?.turnDiscoverySkippedReason;
|
|
6359
|
-
this.turnServerUsed = !this.turnDiscoverySkippedReason;
|
|
6360
|
-
|
|
6361
|
-
// @ts-ignore
|
|
6362
|
-
this.webex.internal.newMetrics.submitInternalEvent({
|
|
6363
|
-
name: 'internal.client.add-media.turn-discovery.end',
|
|
6364
|
-
});
|
|
6365
|
-
|
|
6366
|
-
const {turnServerInfo} = turnDiscoveryObject;
|
|
6367
|
-
|
|
6368
|
-
if (this.turnServerUsed && turnServerInfo) {
|
|
6369
|
-
Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.TURN_DISCOVERY_LATENCY, {
|
|
6370
|
-
correlation_id: this.correlationId,
|
|
6371
|
-
latency: cdl.getTurnDiscoveryTime(),
|
|
6372
|
-
turnServerUsed: this.turnServerUsed,
|
|
6373
|
-
retriedWithTurnServer: this.retriedWithTurnServer,
|
|
6374
|
-
});
|
|
6522
|
+
if (!turnServerInfo) {
|
|
6523
|
+
({turnServerInfo} = await this.doTurnDiscovery(isRetry, isForced));
|
|
6375
6524
|
}
|
|
6376
6525
|
|
|
6377
6526
|
const mc = await this.createMediaConnection(turnServerInfo, bundlePolicy);
|
|
@@ -6488,15 +6637,21 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
6488
6637
|
* Creates a media connection to the server. Media connection is required for sending or receiving any audio/video.
|
|
6489
6638
|
*
|
|
6490
6639
|
* @param {AddMediaOptions} options
|
|
6640
|
+
* @param {TurnServerInfo} turnServerInfo - TURN server information (used only internally by the SDK)
|
|
6491
6641
|
* @returns {Promise<void>}
|
|
6492
6642
|
* @public
|
|
6493
6643
|
* @memberof Meeting
|
|
6494
6644
|
*/
|
|
6495
|
-
async addMedia(
|
|
6645
|
+
async addMedia(
|
|
6646
|
+
options: AddMediaOptions = {},
|
|
6647
|
+
turnServerInfo: TurnServerInfo = undefined
|
|
6648
|
+
): Promise<void> {
|
|
6496
6649
|
this.retriedWithTurnServer = false;
|
|
6497
6650
|
this.hasMediaConnectionConnectedAtLeastOnce = false;
|
|
6498
6651
|
const LOG_HEADER = 'Meeting:index#addMedia -->';
|
|
6499
|
-
LoggerProxy.logger.info(
|
|
6652
|
+
LoggerProxy.logger.info(
|
|
6653
|
+
`${LOG_HEADER} called with: ${JSON.stringify(options)}, ${JSON.stringify(turnServerInfo)}`
|
|
6654
|
+
);
|
|
6500
6655
|
|
|
6501
6656
|
if (options.allowMediaInLobby !== true && this.meetingState !== FULL_STATE.ACTIVE) {
|
|
6502
6657
|
throw new MeetingNotActiveError();
|
|
@@ -6514,14 +6669,13 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
6514
6669
|
shareVideoEnabled = true,
|
|
6515
6670
|
remoteMediaManagerConfig,
|
|
6516
6671
|
bundlePolicy,
|
|
6517
|
-
allowMediaInLobby,
|
|
6518
6672
|
} = options;
|
|
6519
6673
|
|
|
6520
6674
|
this.allowMediaInLobby = options?.allowMediaInLobby;
|
|
6521
6675
|
|
|
6522
6676
|
// If the user is unjoined or guest waiting in lobby dont allow the user to addMedia
|
|
6523
6677
|
// @ts-ignore - isUserUnadmitted coming from SelfUtil
|
|
6524
|
-
if (this.isUserUnadmitted && !this.wirelessShare && !allowMediaInLobby) {
|
|
6678
|
+
if (this.isUserUnadmitted && !this.wirelessShare && !this.allowMediaInLobby) {
|
|
6525
6679
|
throw new UserInLobbyError();
|
|
6526
6680
|
}
|
|
6527
6681
|
|
|
@@ -6590,9 +6744,18 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
6590
6744
|
|
|
6591
6745
|
this.createStatsAnalyzer();
|
|
6592
6746
|
|
|
6593
|
-
await this.establishMediaConnection(
|
|
6747
|
+
await this.establishMediaConnection(
|
|
6748
|
+
remoteMediaManagerConfig,
|
|
6749
|
+
bundlePolicy,
|
|
6750
|
+
false,
|
|
6751
|
+
turnServerInfo
|
|
6752
|
+
);
|
|
6594
6753
|
|
|
6595
|
-
|
|
6754
|
+
if (audioEnabled || videoEnabled) {
|
|
6755
|
+
await Meeting.handleDeviceLogging();
|
|
6756
|
+
} else {
|
|
6757
|
+
LoggerProxy.logger.info(`${LOG_HEADER} device logging not required`);
|
|
6758
|
+
}
|
|
6596
6759
|
|
|
6597
6760
|
if (this.mediaProperties.hasLocalShareStream()) {
|
|
6598
6761
|
await this.enqueueScreenShareFloorRequest();
|
|
@@ -7784,9 +7947,9 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
7784
7947
|
|
|
7785
7948
|
/**
|
|
7786
7949
|
*
|
|
7787
|
-
* @returns {string} one of 'attendee','host','cohost', returns the user type of the current user
|
|
7950
|
+
* @returns {string} one of 'panelist', 'attendee', 'host', 'cohost', returns the user type of the current user
|
|
7788
7951
|
*/
|
|
7789
|
-
getCurUserType() {
|
|
7952
|
+
getCurUserType(): RawClientEvent['userType'] | null {
|
|
7790
7953
|
const {roles} = this;
|
|
7791
7954
|
if (roles) {
|
|
7792
7955
|
if (roles.includes(SELF_ROLES.MODERATOR)) {
|
|
@@ -7795,8 +7958,8 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
7795
7958
|
if (roles.includes(SELF_ROLES.COHOST)) {
|
|
7796
7959
|
return 'cohost';
|
|
7797
7960
|
}
|
|
7798
|
-
if (roles.includes(SELF_ROLES.
|
|
7799
|
-
return '
|
|
7961
|
+
if (roles.includes(SELF_ROLES.PANELIST)) {
|
|
7962
|
+
return 'panelist';
|
|
7800
7963
|
}
|
|
7801
7964
|
if (roles.includes(SELF_ROLES.ATTENDEE)) {
|
|
7802
7965
|
return 'attendee';
|
|
@@ -8201,6 +8364,17 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
8201
8364
|
return;
|
|
8202
8365
|
}
|
|
8203
8366
|
|
|
8367
|
+
if (
|
|
8368
|
+
streams?.microphone?.readyState === 'ended' ||
|
|
8369
|
+
streams?.camera?.readyState === 'ended' ||
|
|
8370
|
+
streams?.screenShare?.audio?.readyState === 'ended' ||
|
|
8371
|
+
streams?.screenShare?.video?.readyState === 'ended'
|
|
8372
|
+
) {
|
|
8373
|
+
throw new Error(
|
|
8374
|
+
`Attempted to publish stream with ended readyState, correlationId=${this.correlationId}`
|
|
8375
|
+
);
|
|
8376
|
+
}
|
|
8377
|
+
|
|
8204
8378
|
let floorRequestNeeded = false;
|
|
8205
8379
|
|
|
8206
8380
|
// Screenshare Audio is supported only in multi stream. So we check for screenshare audio presence only if it's a multi stream meeting
|