@webex/plugin-meetings 3.0.0-beta.170 → 3.0.0-beta.172
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/README.md +0 -6
- package/dist/breakouts/breakout.js +1 -1
- package/dist/breakouts/index.js +1 -1
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -1
- package/dist/interpretation/index.js +1 -1
- package/dist/interpretation/siLanguage.js +1 -1
- package/dist/media/index.js +3 -3
- package/dist/media/index.js.map +1 -1
- package/dist/media/properties.js +22 -5
- package/dist/media/properties.js.map +1 -1
- package/dist/meeting/index.js +529 -415
- package/dist/meeting/index.js.map +1 -1
- package/dist/member/index.js +15 -0
- package/dist/member/index.js.map +1 -1
- package/dist/member/types.js +11 -1
- package/dist/member/types.js.map +1 -1
- package/dist/member/util.js +15 -0
- package/dist/member/util.js.map +1 -1
- package/dist/reconnection-manager/index.js +1 -2
- package/dist/reconnection-manager/index.js.map +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/media/properties.d.ts +10 -3
- package/dist/types/meeting/index.d.ts +41 -9
- package/dist/types/member/index.d.ts +2 -1
- package/dist/types/member/types.d.ts +11 -0
- package/package.json +20 -20
- package/src/index.ts +2 -0
- package/src/media/index.ts +11 -5
- package/src/media/properties.ts +24 -5
- package/src/meeting/index.ts +210 -77
- package/src/member/index.ts +17 -1
- package/src/member/types.ts +14 -0
- package/src/member/util.ts +23 -1
- package/src/reconnection-manager/index.ts +4 -1
- package/test/integration/spec/journey.js +9 -9
- package/test/unit/spec/media/index.ts +13 -3
- package/test/unit/spec/meeting/index.js +106 -15
- package/test/unit/spec/member/index.js +36 -13
- package/test/unit/spec/member/util.js +31 -0
package/src/meeting/index.ts
CHANGED
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
LocalTrack,
|
|
20
20
|
LocalCameraTrack,
|
|
21
21
|
LocalDisplayTrack,
|
|
22
|
+
LocalSystemAudioTrack,
|
|
22
23
|
LocalMicrophoneTrack,
|
|
23
24
|
LocalTrackEvents,
|
|
24
25
|
TrackMuteEvent,
|
|
@@ -145,7 +146,7 @@ export type LocalTracks = {
|
|
|
145
146
|
microphone?: LocalMicrophoneTrack;
|
|
146
147
|
camera?: LocalCameraTrack;
|
|
147
148
|
screenShare?: {
|
|
148
|
-
audio?:
|
|
149
|
+
audio?: LocalSystemAudioTrack;
|
|
149
150
|
video?: LocalDisplayTrack;
|
|
150
151
|
};
|
|
151
152
|
annotationInfo?: AnnotationInfo;
|
|
@@ -167,6 +168,12 @@ export const MEDIA_UPDATE_TYPE = {
|
|
|
167
168
|
UPDATE_MEDIA: 'UPDATE_MEDIA',
|
|
168
169
|
};
|
|
169
170
|
|
|
171
|
+
export enum ScreenShareFloorStatus {
|
|
172
|
+
PENDING = 'floor_request_pending',
|
|
173
|
+
GRANTED = 'floor_request_granted',
|
|
174
|
+
RELEASED = 'floor_released',
|
|
175
|
+
}
|
|
176
|
+
|
|
170
177
|
/**
|
|
171
178
|
* MediaDirection
|
|
172
179
|
* @typedef {Object} MediaDirection
|
|
@@ -484,7 +491,6 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
484
491
|
inMeetingActions: InMeetingActions;
|
|
485
492
|
isLocalShareLive: boolean;
|
|
486
493
|
isRoapInProgress: boolean;
|
|
487
|
-
isSharing: boolean;
|
|
488
494
|
keepAliveTimerId: NodeJS.Timeout;
|
|
489
495
|
lastVideoLayoutInfo: any;
|
|
490
496
|
locusInfo: any;
|
|
@@ -511,6 +517,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
511
517
|
receiveSlotManager: ReceiveSlotManager;
|
|
512
518
|
selfUserPolicies: any;
|
|
513
519
|
shareStatus: string;
|
|
520
|
+
screenShareFloorState: ScreenShareFloorStatus;
|
|
514
521
|
statsAnalyzer: StatsAnalyzer;
|
|
515
522
|
transcription: Transcription;
|
|
516
523
|
updateMediaConnections: (mediaConnections: any[]) => void;
|
|
@@ -964,24 +971,20 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
964
971
|
*/
|
|
965
972
|
this.inMeetingActions = new InMeetingActions();
|
|
966
973
|
/**
|
|
967
|
-
* This is deprecated, please use shareStatus instead.
|
|
968
974
|
* @instance
|
|
969
|
-
* @type {
|
|
975
|
+
* @type {string}
|
|
970
976
|
* @readonly
|
|
971
977
|
* @public
|
|
972
978
|
* @memberof Meeting
|
|
973
|
-
* @deprecated after v1.118.13
|
|
974
979
|
*/
|
|
975
|
-
this.
|
|
980
|
+
this.shareStatus = SHARE_STATUS.NO_SHARE;
|
|
976
981
|
/**
|
|
977
982
|
* @instance
|
|
978
|
-
* @type {
|
|
979
|
-
* @
|
|
980
|
-
* @
|
|
981
|
-
* @memberof Meeting
|
|
983
|
+
* @type {ScreenShareFloorStatus}
|
|
984
|
+
* @private
|
|
985
|
+
* @memberof
|
|
982
986
|
*/
|
|
983
|
-
this.
|
|
984
|
-
|
|
987
|
+
this.screenShareFloorState = ScreenShareFloorStatus.RELEASED;
|
|
985
988
|
/**
|
|
986
989
|
* @instance
|
|
987
990
|
* @type {Array}
|
|
@@ -2224,7 +2227,10 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
2224
2227
|
this.mediaProperties.mediaDirection?.sendShare &&
|
|
2225
2228
|
oldShareStatus === SHARE_STATUS.LOCAL_SHARE_ACTIVE
|
|
2226
2229
|
) {
|
|
2227
|
-
await this.unpublishTracks([
|
|
2230
|
+
await this.unpublishTracks([
|
|
2231
|
+
this.mediaProperties.shareVideoTrack,
|
|
2232
|
+
this.mediaProperties.shareAudioTrack,
|
|
2233
|
+
]);
|
|
2228
2234
|
}
|
|
2229
2235
|
} finally {
|
|
2230
2236
|
sendStartedSharingRemote();
|
|
@@ -2902,8 +2908,11 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
2902
2908
|
|
|
2903
2909
|
// TODO: Handle sharing and wireless sharing when meeting end
|
|
2904
2910
|
if (this.wirelessShare) {
|
|
2905
|
-
if (this.mediaProperties.
|
|
2906
|
-
await this.
|
|
2911
|
+
if (this.mediaProperties.shareVideoTrack) {
|
|
2912
|
+
await this.setLocalShareVideoTrack(undefined);
|
|
2913
|
+
}
|
|
2914
|
+
if (this.mediaProperties.shareAudioTrack) {
|
|
2915
|
+
await this.setLocalShareAudioTrack(undefined);
|
|
2907
2916
|
}
|
|
2908
2917
|
}
|
|
2909
2918
|
// when multiple WEB deviceType join with same user
|
|
@@ -3359,34 +3368,62 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3359
3368
|
}
|
|
3360
3369
|
|
|
3361
3370
|
/**
|
|
3362
|
-
* Stores the reference to a new screen share track, sets up the required event listeners
|
|
3371
|
+
* Stores the reference to a new screen share video track, sets up the required event listeners
|
|
3363
3372
|
* on it, cleans up previous track, etc.
|
|
3364
|
-
* It also sends the floor grant/release request.
|
|
3365
3373
|
*
|
|
3366
3374
|
* @param {LocalDisplayTrack | undefined} localDisplayTrack local camera track
|
|
3367
3375
|
* @returns {Promise<void>}
|
|
3368
3376
|
*/
|
|
3369
|
-
private async
|
|
3370
|
-
const oldTrack = this.mediaProperties.
|
|
3377
|
+
private async setLocalShareVideoTrack(localDisplayTrack?: LocalDisplayTrack) {
|
|
3378
|
+
const oldTrack = this.mediaProperties.shareVideoTrack;
|
|
3371
3379
|
|
|
3372
|
-
oldTrack?.off(LocalTrackEvents.Ended, this.
|
|
3380
|
+
oldTrack?.off(LocalTrackEvents.Ended, this.handleShareVideoTrackEnded);
|
|
3373
3381
|
oldTrack?.off(LocalTrackEvents.UnderlyingTrackChange, this.underlyingLocalTrackChangeHandler);
|
|
3374
3382
|
|
|
3375
|
-
this.mediaProperties.
|
|
3383
|
+
this.mediaProperties.setLocalShareVideoTrack(localDisplayTrack);
|
|
3376
3384
|
|
|
3377
|
-
localDisplayTrack?.on(LocalTrackEvents.Ended, this.
|
|
3385
|
+
localDisplayTrack?.on(LocalTrackEvents.Ended, this.handleShareVideoTrackEnded);
|
|
3378
3386
|
localDisplayTrack?.on(
|
|
3379
3387
|
LocalTrackEvents.UnderlyingTrackChange,
|
|
3380
3388
|
this.underlyingLocalTrackChangeHandler
|
|
3381
3389
|
);
|
|
3382
3390
|
|
|
3383
|
-
this.mediaProperties.mediaDirection.sendShare =
|
|
3391
|
+
this.mediaProperties.mediaDirection.sendShare = this.mediaProperties.hasLocalShareTrack();
|
|
3384
3392
|
|
|
3385
3393
|
if (!this.isMultistream || !localDisplayTrack) {
|
|
3386
3394
|
// for multistream WCME automatically un-publishes the old track when we publish a new one
|
|
3387
3395
|
await this.unpublishTrack(oldTrack);
|
|
3388
3396
|
}
|
|
3389
|
-
await this.publishTrack(this.mediaProperties.
|
|
3397
|
+
await this.publishTrack(this.mediaProperties.shareVideoTrack);
|
|
3398
|
+
}
|
|
3399
|
+
|
|
3400
|
+
/**
|
|
3401
|
+
* Stores the reference to a new screen share audio track, sets up the required event listeners
|
|
3402
|
+
* on it, cleans up previous track, etc.
|
|
3403
|
+
*
|
|
3404
|
+
* @param {LocalSystemAudioTrack | undefined} localSystemAudioTrack local system audio track
|
|
3405
|
+
* @returns {Promise<void>}
|
|
3406
|
+
*/
|
|
3407
|
+
private async setLocalShareAudioTrack(localSystemAudioTrack?: LocalSystemAudioTrack) {
|
|
3408
|
+
const oldTrack = this.mediaProperties.shareAudioTrack;
|
|
3409
|
+
|
|
3410
|
+
oldTrack?.off(LocalTrackEvents.Ended, this.handleShareAudioTrackEnded);
|
|
3411
|
+
oldTrack?.off(LocalTrackEvents.UnderlyingTrackChange, this.underlyingLocalTrackChangeHandler);
|
|
3412
|
+
|
|
3413
|
+
this.mediaProperties.setLocalShareAudioTrack(localSystemAudioTrack);
|
|
3414
|
+
|
|
3415
|
+
localSystemAudioTrack?.on(LocalTrackEvents.Ended, this.handleShareAudioTrackEnded);
|
|
3416
|
+
localSystemAudioTrack?.on(
|
|
3417
|
+
LocalTrackEvents.UnderlyingTrackChange,
|
|
3418
|
+
this.underlyingLocalTrackChangeHandler
|
|
3419
|
+
);
|
|
3420
|
+
|
|
3421
|
+
this.mediaProperties.mediaDirection.sendShare = this.mediaProperties.hasLocalShareTrack();
|
|
3422
|
+
|
|
3423
|
+
if (!this.isMultistream || !localSystemAudioTrack) {
|
|
3424
|
+
await this.unpublishTrack(oldTrack);
|
|
3425
|
+
}
|
|
3426
|
+
await this.publishTrack(this.mediaProperties.shareAudioTrack);
|
|
3390
3427
|
}
|
|
3391
3428
|
|
|
3392
3429
|
/**
|
|
@@ -3397,7 +3434,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3397
3434
|
* @returns {void}
|
|
3398
3435
|
*/
|
|
3399
3436
|
public cleanupLocalTracks() {
|
|
3400
|
-
const {audioTrack, videoTrack,
|
|
3437
|
+
const {audioTrack, videoTrack, shareVideoTrack, shareAudioTrack} = this.mediaProperties;
|
|
3401
3438
|
|
|
3402
3439
|
audioTrack?.off(LocalTrackEvents.Muted, this.localAudioTrackMuteStateHandler);
|
|
3403
3440
|
audioTrack?.off(LocalTrackEvents.UnderlyingTrackChange, this.underlyingLocalTrackChangeHandler);
|
|
@@ -3405,12 +3442,22 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3405
3442
|
videoTrack?.off(LocalTrackEvents.Muted, this.localVideoTrackMuteStateHandler);
|
|
3406
3443
|
videoTrack?.off(LocalTrackEvents.UnderlyingTrackChange, this.underlyingLocalTrackChangeHandler);
|
|
3407
3444
|
|
|
3408
|
-
|
|
3409
|
-
|
|
3445
|
+
shareVideoTrack?.off(LocalTrackEvents.Ended, this.handleShareVideoTrackEnded);
|
|
3446
|
+
shareVideoTrack?.off(
|
|
3447
|
+
LocalTrackEvents.UnderlyingTrackChange,
|
|
3448
|
+
this.underlyingLocalTrackChangeHandler
|
|
3449
|
+
);
|
|
3450
|
+
|
|
3451
|
+
shareAudioTrack?.off(LocalTrackEvents.Ended, this.handleShareAudioTrackEnded);
|
|
3452
|
+
shareAudioTrack?.off(
|
|
3453
|
+
LocalTrackEvents.UnderlyingTrackChange,
|
|
3454
|
+
this.underlyingLocalTrackChangeHandler
|
|
3455
|
+
);
|
|
3410
3456
|
|
|
3411
3457
|
this.mediaProperties.setLocalAudioTrack(undefined);
|
|
3412
3458
|
this.mediaProperties.setLocalVideoTrack(undefined);
|
|
3413
|
-
this.mediaProperties.
|
|
3459
|
+
this.mediaProperties.setLocalShareVideoTrack(undefined);
|
|
3460
|
+
this.mediaProperties.setLocalShareAudioTrack(undefined);
|
|
3414
3461
|
|
|
3415
3462
|
this.mediaProperties.mediaDirection.sendAudio = false;
|
|
3416
3463
|
this.mediaProperties.mediaDirection.sendVideo = false;
|
|
@@ -3420,7 +3467,8 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3420
3467
|
// (we have to do it for transcoded meetings anyway, so we might as well do for multistream too)
|
|
3421
3468
|
audioTrack?.setPublished(false);
|
|
3422
3469
|
videoTrack?.setPublished(false);
|
|
3423
|
-
|
|
3470
|
+
shareVideoTrack?.setPublished(false);
|
|
3471
|
+
shareAudioTrack?.setPublished(false);
|
|
3424
3472
|
}
|
|
3425
3473
|
|
|
3426
3474
|
/**
|
|
@@ -3522,6 +3570,18 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3522
3570
|
this.correlationId = id;
|
|
3523
3571
|
}
|
|
3524
3572
|
|
|
3573
|
+
/**
|
|
3574
|
+
* Enqueue request for screenshare floor and set the status to pending
|
|
3575
|
+
* @returns {Promise}
|
|
3576
|
+
* @private
|
|
3577
|
+
* @memberof Meeting
|
|
3578
|
+
*/
|
|
3579
|
+
private enqueueScreenShareFloorRequest() {
|
|
3580
|
+
this.screenShareFloorState = ScreenShareFloorStatus.PENDING;
|
|
3581
|
+
|
|
3582
|
+
return this.enqueueMediaUpdate(MEDIA_UPDATE_TYPE.SHARE_FLOOR_REQUEST);
|
|
3583
|
+
}
|
|
3584
|
+
|
|
3525
3585
|
/**
|
|
3526
3586
|
* Mute the audio for a meeting
|
|
3527
3587
|
* @returns {Promise} resolves the data from muting audio {mute, self} or rejects if there is no audio set
|
|
@@ -4501,7 +4561,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4501
4561
|
// Clean up the camera , microphone track and re initiate it
|
|
4502
4562
|
|
|
4503
4563
|
try {
|
|
4504
|
-
if (this.
|
|
4564
|
+
if (this.screenShareFloorState === ScreenShareFloorStatus.GRANTED) {
|
|
4505
4565
|
await this.releaseScreenShareFloor();
|
|
4506
4566
|
}
|
|
4507
4567
|
const mediaSettings = {
|
|
@@ -5106,8 +5166,11 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5106
5166
|
if (this.mediaProperties.videoTrack) {
|
|
5107
5167
|
await this.publishTrack(this.mediaProperties.videoTrack);
|
|
5108
5168
|
}
|
|
5109
|
-
if (this.mediaProperties.
|
|
5110
|
-
await this.publishTrack(this.mediaProperties.
|
|
5169
|
+
if (this.mediaProperties.shareVideoTrack) {
|
|
5170
|
+
await this.publishTrack(this.mediaProperties.shareVideoTrack);
|
|
5171
|
+
}
|
|
5172
|
+
if (this.isMultistream && this.mediaProperties.shareAudioTrack) {
|
|
5173
|
+
await this.publishTrack(this.mediaProperties.shareAudioTrack);
|
|
5111
5174
|
}
|
|
5112
5175
|
|
|
5113
5176
|
return mc;
|
|
@@ -5243,7 +5306,10 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5243
5306
|
promises.push(this.setLocalVideoTrack(localTracks.camera));
|
|
5244
5307
|
}
|
|
5245
5308
|
if (localTracks?.screenShare?.video) {
|
|
5246
|
-
promises.push(this.
|
|
5309
|
+
promises.push(this.setLocalShareVideoTrack(localTracks.screenShare.video));
|
|
5310
|
+
}
|
|
5311
|
+
if (this.isMultistream && localTracks?.screenShare?.audio) {
|
|
5312
|
+
promises.push(this.setLocalShareAudioTrack(localTracks.screenShare.audio));
|
|
5247
5313
|
}
|
|
5248
5314
|
|
|
5249
5315
|
return Promise.all(promises)
|
|
@@ -5373,9 +5439,11 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5373
5439
|
})
|
|
5374
5440
|
)
|
|
5375
5441
|
.then(() => {
|
|
5376
|
-
if (
|
|
5377
|
-
this.
|
|
5442
|
+
if (this.mediaProperties.hasLocalShareTrack()) {
|
|
5443
|
+
return this.enqueueScreenShareFloorRequest();
|
|
5378
5444
|
}
|
|
5445
|
+
|
|
5446
|
+
return Promise.resolve();
|
|
5379
5447
|
})
|
|
5380
5448
|
.then(() => this.mediaProperties.getCurrentConnectionType())
|
|
5381
5449
|
.then((connectionType) => {
|
|
@@ -5821,7 +5889,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5821
5889
|
return this.meetingRequest
|
|
5822
5890
|
.changeMeetingFloor(body)
|
|
5823
5891
|
.then(() => {
|
|
5824
|
-
this.
|
|
5892
|
+
this.screenShareFloorState = ScreenShareFloorStatus.RELEASED;
|
|
5825
5893
|
|
|
5826
5894
|
return Promise.resolve();
|
|
5827
5895
|
})
|
|
@@ -5902,16 +5970,23 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5902
5970
|
* @memberof Meeting
|
|
5903
5971
|
*/
|
|
5904
5972
|
private requestScreenShareFloor() {
|
|
5905
|
-
if (
|
|
5973
|
+
if (
|
|
5974
|
+
!this.mediaProperties.hasLocalShareTrack() ||
|
|
5975
|
+
!this.mediaProperties.mediaDirection.sendShare
|
|
5976
|
+
) {
|
|
5906
5977
|
LoggerProxy.logger.log(
|
|
5907
|
-
`Meeting:index#requestScreenShareFloor --> NOT requesting floor, because we don't have the share track anymore (
|
|
5908
|
-
this.mediaProperties.
|
|
5909
|
-
},
|
|
5978
|
+
`Meeting:index#requestScreenShareFloor --> NOT requesting floor, because we don't have the share track anymore (shareVideoTrack=${
|
|
5979
|
+
this.mediaProperties.shareVideoTrack ? 'yes' : 'no'
|
|
5980
|
+
}, shareAudioTrack=${this.mediaProperties.shareAudioTrack ? 'yes' : 'no'}, sendShare=${
|
|
5981
|
+
this.mediaProperties.mediaDirection.sendShare
|
|
5982
|
+
})`
|
|
5910
5983
|
);
|
|
5984
|
+
this.screenShareFloorState = ScreenShareFloorStatus.RELEASED;
|
|
5911
5985
|
|
|
5912
5986
|
return Promise.resolve({});
|
|
5913
5987
|
}
|
|
5914
5988
|
if (this.state === MEETING_STATE.STATES.JOINED) {
|
|
5989
|
+
this.screenShareFloorState = ScreenShareFloorStatus.PENDING;
|
|
5915
5990
|
const content = this.locusInfo.mediaShares.find((element) => element.name === CONTENT);
|
|
5916
5991
|
|
|
5917
5992
|
if (content && this.shareStatus !== SHARE_STATUS.LOCAL_SHARE_ACTIVE) {
|
|
@@ -5934,7 +6009,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5934
6009
|
annotationInfo: this.annotationInfo,
|
|
5935
6010
|
})
|
|
5936
6011
|
.then(() => {
|
|
5937
|
-
this.
|
|
6012
|
+
this.screenShareFloorState = ScreenShareFloorStatus.GRANTED;
|
|
5938
6013
|
|
|
5939
6014
|
return Promise.resolve();
|
|
5940
6015
|
})
|
|
@@ -5948,11 +6023,18 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5948
6023
|
stack: error.stack,
|
|
5949
6024
|
});
|
|
5950
6025
|
|
|
6026
|
+
this.screenShareFloorState = ScreenShareFloorStatus.RELEASED;
|
|
6027
|
+
|
|
5951
6028
|
return Promise.reject(error);
|
|
5952
6029
|
});
|
|
5953
6030
|
}
|
|
6031
|
+
if (!content) {
|
|
6032
|
+
this.screenShareFloorState = ScreenShareFloorStatus.RELEASED;
|
|
6033
|
+
|
|
6034
|
+
return Promise.reject(new ParameterError('Cannot share without content.'));
|
|
6035
|
+
}
|
|
5954
6036
|
|
|
5955
|
-
return Promise.
|
|
6037
|
+
return Promise.resolve();
|
|
5956
6038
|
}
|
|
5957
6039
|
this.floorGrantPending = true;
|
|
5958
6040
|
|
|
@@ -5982,6 +6064,10 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5982
6064
|
private releaseScreenShareFloor() {
|
|
5983
6065
|
const content = this.locusInfo.mediaShares.find((element) => element.name === CONTENT);
|
|
5984
6066
|
|
|
6067
|
+
if (this.screenShareFloorState === ScreenShareFloorStatus.RELEASED) {
|
|
6068
|
+
return Promise.resolve();
|
|
6069
|
+
}
|
|
6070
|
+
this.screenShareFloorState = ScreenShareFloorStatus.RELEASED;
|
|
5985
6071
|
if (content) {
|
|
5986
6072
|
// @ts-ignore
|
|
5987
6073
|
this.webex.internal.newMetrics.submitClientEvent({
|
|
@@ -5994,8 +6080,6 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5994
6080
|
|
|
5995
6081
|
if (content.floor?.beneficiary.id !== this.selfId) {
|
|
5996
6082
|
// remote participant started sharing and caused our sharing to stop, we don't want to send any floor action request in that case
|
|
5997
|
-
this.isSharing = false;
|
|
5998
|
-
|
|
5999
6083
|
return Promise.resolve();
|
|
6000
6084
|
}
|
|
6001
6085
|
|
|
@@ -6018,15 +6102,10 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
6018
6102
|
});
|
|
6019
6103
|
|
|
6020
6104
|
return Promise.reject(error);
|
|
6021
|
-
})
|
|
6022
|
-
.finally(() => {
|
|
6023
|
-
this.isSharing = false;
|
|
6024
6105
|
});
|
|
6025
6106
|
}
|
|
6026
6107
|
|
|
6027
6108
|
// according to Locus there is no content, so we don't need to release the floor (it's probably already been released)
|
|
6028
|
-
this.isSharing = false;
|
|
6029
|
-
|
|
6030
6109
|
return Promise.resolve();
|
|
6031
6110
|
}
|
|
6032
6111
|
|
|
@@ -6330,37 +6409,75 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
6330
6409
|
}
|
|
6331
6410
|
|
|
6332
6411
|
/**
|
|
6333
|
-
* Functionality for when a share is ended.
|
|
6412
|
+
* Functionality for when a share audio is ended.
|
|
6334
6413
|
* @private
|
|
6335
6414
|
* @memberof Meeting
|
|
6336
|
-
* @param {MediaStream} localShare
|
|
6337
6415
|
* @returns {undefined}
|
|
6338
6416
|
*/
|
|
6339
|
-
private
|
|
6340
|
-
|
|
6417
|
+
private handleShareAudioTrackEnded = async () => {
|
|
6418
|
+
// current share audio track has ended, but there might be an active
|
|
6419
|
+
// share video track. we only leave from wireless share if share has
|
|
6420
|
+
// completely ended, which means no share audio or video tracks active
|
|
6421
|
+
if (this.wirelessShare && !this.mediaProperties.shareVideoTrack) {
|
|
6341
6422
|
this.leave({reason: MEETING_REMOVED_REASON.USER_ENDED_SHARE_STREAMS});
|
|
6342
6423
|
} else {
|
|
6343
6424
|
try {
|
|
6344
|
-
await this.unpublishTracks([this.mediaProperties.
|
|
6425
|
+
await this.unpublishTracks([this.mediaProperties.shareAudioTrack]);
|
|
6345
6426
|
} catch (error) {
|
|
6346
6427
|
LoggerProxy.logger.log(
|
|
6347
|
-
'Meeting:index#
|
|
6428
|
+
'Meeting:index#handleShareAudioTrackEnded --> Error stopping share: ',
|
|
6348
6429
|
error
|
|
6349
6430
|
);
|
|
6350
6431
|
}
|
|
6351
6432
|
}
|
|
6433
|
+
this.triggerStoppedSharing();
|
|
6434
|
+
};
|
|
6352
6435
|
|
|
6353
|
-
|
|
6354
|
-
|
|
6355
|
-
|
|
6356
|
-
|
|
6357
|
-
|
|
6358
|
-
|
|
6359
|
-
|
|
6360
|
-
|
|
6361
|
-
|
|
6436
|
+
/**
|
|
6437
|
+
* Functionality for when a share video is ended.
|
|
6438
|
+
* @private
|
|
6439
|
+
* @memberof Meeting
|
|
6440
|
+
* @returns {undefined}
|
|
6441
|
+
*/
|
|
6442
|
+
private handleShareVideoTrackEnded = async () => {
|
|
6443
|
+
// current share video track has ended, but there might be an active
|
|
6444
|
+
// share audio track. we only leave from wireless share if share has
|
|
6445
|
+
// completely ended, which means no share audio or video tracks active
|
|
6446
|
+
if (this.wirelessShare && !this.mediaProperties.shareAudioTrack) {
|
|
6447
|
+
this.leave({reason: MEETING_REMOVED_REASON.USER_ENDED_SHARE_STREAMS});
|
|
6448
|
+
} else {
|
|
6449
|
+
try {
|
|
6450
|
+
await this.unpublishTracks([this.mediaProperties.shareVideoTrack]);
|
|
6451
|
+
} catch (error) {
|
|
6452
|
+
LoggerProxy.logger.log(
|
|
6453
|
+
'Meeting:index#handleShareVideoTrackEnded --> Error stopping share: ',
|
|
6454
|
+
error
|
|
6455
|
+
);
|
|
6362
6456
|
}
|
|
6363
|
-
|
|
6457
|
+
}
|
|
6458
|
+
this.triggerStoppedSharing();
|
|
6459
|
+
};
|
|
6460
|
+
|
|
6461
|
+
/**
|
|
6462
|
+
* Emits meeting:stoppedSharingLocal
|
|
6463
|
+
* @private
|
|
6464
|
+
* @returns {undefined}
|
|
6465
|
+
* @memberof Meeting
|
|
6466
|
+
*/
|
|
6467
|
+
private triggerStoppedSharing = () => {
|
|
6468
|
+
if (!this.mediaProperties.hasLocalShareTrack()) {
|
|
6469
|
+
Trigger.trigger(
|
|
6470
|
+
this,
|
|
6471
|
+
{
|
|
6472
|
+
file: 'meeting/index',
|
|
6473
|
+
function: 'handleShareTrackEnded',
|
|
6474
|
+
},
|
|
6475
|
+
EVENT_TRIGGERS.MEETING_STOPPED_SHARING_LOCAL,
|
|
6476
|
+
{
|
|
6477
|
+
reason: SHARE_STOPPED_REASON.TRACK_ENDED,
|
|
6478
|
+
}
|
|
6479
|
+
);
|
|
6480
|
+
}
|
|
6364
6481
|
};
|
|
6365
6482
|
|
|
6366
6483
|
/**
|
|
@@ -6499,7 +6616,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
6499
6616
|
clearMeetingData = () => {
|
|
6500
6617
|
this.audio = null;
|
|
6501
6618
|
this.video = null;
|
|
6502
|
-
this.
|
|
6619
|
+
this.screenShareFloorState = ScreenShareFloorStatus.RELEASED;
|
|
6503
6620
|
if (this.shareStatus === SHARE_STATUS.LOCAL_SHARE_ACTIVE) {
|
|
6504
6621
|
this.shareStatus = SHARE_STATUS.NO_SHARE;
|
|
6505
6622
|
}
|
|
@@ -6683,7 +6800,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
6683
6800
|
localTracks: {
|
|
6684
6801
|
audio: this.mediaProperties.audioTrack?.underlyingTrack || null,
|
|
6685
6802
|
video: this.mediaProperties.videoTrack?.underlyingTrack || null,
|
|
6686
|
-
screenShareVideo: this.mediaProperties.
|
|
6803
|
+
screenShareVideo: this.mediaProperties.shareVideoTrack?.underlyingTrack || null,
|
|
6687
6804
|
},
|
|
6688
6805
|
direction: {
|
|
6689
6806
|
audio: Media.getDirection(
|
|
@@ -6783,9 +6900,15 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
6783
6900
|
let floorRequestNeeded = false;
|
|
6784
6901
|
|
|
6785
6902
|
if (tracks.screenShare?.video) {
|
|
6786
|
-
await this.
|
|
6903
|
+
await this.setLocalShareVideoTrack(tracks.screenShare?.video);
|
|
6787
6904
|
|
|
6788
|
-
floorRequestNeeded =
|
|
6905
|
+
floorRequestNeeded = this.screenShareFloorState === ScreenShareFloorStatus.RELEASED;
|
|
6906
|
+
}
|
|
6907
|
+
|
|
6908
|
+
if (this.isMultistream && tracks.screenShare?.audio) {
|
|
6909
|
+
await this.setLocalShareAudioTrack(tracks.screenShare.audio);
|
|
6910
|
+
|
|
6911
|
+
floorRequestNeeded = this.screenShareFloorState === ScreenShareFloorStatus.RELEASED;
|
|
6789
6912
|
}
|
|
6790
6913
|
|
|
6791
6914
|
if (tracks.microphone) {
|
|
@@ -6804,7 +6927,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
6804
6927
|
// we're sending the http request to Locus to request the screen share floor
|
|
6805
6928
|
// only after the SDP update, because that's how it's always been done for transcoded meetings
|
|
6806
6929
|
// and also if sharing from the start, we need confluence to have been created
|
|
6807
|
-
await this.
|
|
6930
|
+
await this.enqueueScreenShareFloorRequest();
|
|
6808
6931
|
}
|
|
6809
6932
|
}
|
|
6810
6933
|
|
|
@@ -6820,13 +6943,12 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
6820
6943
|
const promises = [];
|
|
6821
6944
|
|
|
6822
6945
|
for (const track of tracks.filter((t) => !!t)) {
|
|
6823
|
-
if (track === this.mediaProperties.
|
|
6824
|
-
|
|
6825
|
-
|
|
6826
|
-
|
|
6827
|
-
|
|
6828
|
-
|
|
6829
|
-
promises.push(this.setLocalShareTrack(undefined));
|
|
6946
|
+
if (track === this.mediaProperties.shareVideoTrack) {
|
|
6947
|
+
promises.push(this.setLocalShareVideoTrack(undefined));
|
|
6948
|
+
}
|
|
6949
|
+
|
|
6950
|
+
if (track === this.mediaProperties.shareAudioTrack) {
|
|
6951
|
+
promises.push(this.setLocalShareAudioTrack(undefined));
|
|
6830
6952
|
}
|
|
6831
6953
|
|
|
6832
6954
|
if (track === this.mediaProperties.audioTrack) {
|
|
@@ -6843,5 +6965,16 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
6843
6965
|
}
|
|
6844
6966
|
|
|
6845
6967
|
await Promise.all(promises);
|
|
6968
|
+
|
|
6969
|
+
// we're allowing for the SDK to support just audio share as well
|
|
6970
|
+
// so a share could be active with only video, only audio, or both
|
|
6971
|
+
// we're only releasing the floor if both tracks have ended
|
|
6972
|
+
if (!this.mediaProperties.hasLocalShareTrack()) {
|
|
6973
|
+
try {
|
|
6974
|
+
this.releaseScreenShareFloor(); // we ignore the returned promise here on purpose
|
|
6975
|
+
} catch (e) {
|
|
6976
|
+
// nothing to do here, error is logged already inside releaseScreenShareFloor()
|
|
6977
|
+
}
|
|
6978
|
+
}
|
|
6846
6979
|
}
|
|
6847
6980
|
}
|
package/src/member/index.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.
|
|
3
3
|
*/
|
|
4
4
|
import {MEETINGS, _IN_LOBBY_, _NOT_IN_MEETING_, _IN_MEETING_} from '../constants';
|
|
5
|
-
import {IExternalRoles, ParticipantWithRoles} from './types';
|
|
5
|
+
import {IExternalRoles, IMediaStatus, ParticipantWithRoles} from './types';
|
|
6
6
|
|
|
7
7
|
import MemberUtil from './util';
|
|
8
8
|
|
|
@@ -30,6 +30,7 @@ export default class Member {
|
|
|
30
30
|
isUser: any;
|
|
31
31
|
isVideoMuted: any;
|
|
32
32
|
roles: IExternalRoles;
|
|
33
|
+
mediaStatus: IMediaStatus;
|
|
33
34
|
name: any;
|
|
34
35
|
participant: any;
|
|
35
36
|
status: any;
|
|
@@ -247,6 +248,19 @@ export default class Member {
|
|
|
247
248
|
* @memberof Member
|
|
248
249
|
*/
|
|
249
250
|
this.roles = null;
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* @instance
|
|
254
|
+
* @type {IMediaStatus}
|
|
255
|
+
* @public
|
|
256
|
+
* @memberof Member
|
|
257
|
+
* @example {audio: MediaStatus.RECVONLY, video: MediaStatus.SENDRECV}
|
|
258
|
+
*/
|
|
259
|
+
this.mediaStatus = {
|
|
260
|
+
audio: null,
|
|
261
|
+
video: null,
|
|
262
|
+
};
|
|
263
|
+
|
|
250
264
|
// TODO: more participant types
|
|
251
265
|
// such as native client, web client, is a device, what type of phone, etc
|
|
252
266
|
this.processParticipant(participant);
|
|
@@ -325,6 +339,8 @@ export default class Member {
|
|
|
325
339
|
this.isAudioMuted,
|
|
326
340
|
this.type
|
|
327
341
|
);
|
|
342
|
+
|
|
343
|
+
this.mediaStatus = MemberUtil.extractMediaStatus(this.participant);
|
|
328
344
|
}
|
|
329
345
|
|
|
330
346
|
/**
|
package/src/member/types.ts
CHANGED
|
@@ -22,3 +22,17 @@ export type ParticipantWithRoles = {
|
|
|
22
22
|
};
|
|
23
23
|
};
|
|
24
24
|
};
|
|
25
|
+
|
|
26
|
+
// values are inherited from locus so don't update these
|
|
27
|
+
export enum MediaStatus {
|
|
28
|
+
RECVONLY = 'RECVONLY', // participant only receiving and not sending
|
|
29
|
+
SENDONLY = 'SENDONLY', // participant only sending and not receiving
|
|
30
|
+
SENDRECV = 'SENDRECV', // participant both sending and receiving
|
|
31
|
+
INACTIVE = 'INACTIVE', // participant is not connected to media source
|
|
32
|
+
UNKNOWN = 'UNKNOWN', // participant has not added media in the meeting
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface IMediaStatus {
|
|
36
|
+
audio: MediaStatus;
|
|
37
|
+
video: MediaStatus;
|
|
38
|
+
}
|
package/src/member/util.ts
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
IExternalRoles,
|
|
3
|
+
ParticipantWithRoles,
|
|
4
|
+
ServerRoles,
|
|
5
|
+
ServerRoleShape,
|
|
6
|
+
IMediaStatus,
|
|
7
|
+
} from './types';
|
|
2
8
|
import {
|
|
3
9
|
_USER_,
|
|
4
10
|
_RESOURCE_ROOM_,
|
|
@@ -347,6 +353,22 @@ MemberUtil.extractId = (participant: any) => {
|
|
|
347
353
|
return null;
|
|
348
354
|
};
|
|
349
355
|
|
|
356
|
+
/**
|
|
357
|
+
* extracts the media status from nested participant object
|
|
358
|
+
* @param {Object} participant the locus participant
|
|
359
|
+
* @returns {Object}
|
|
360
|
+
*/
|
|
361
|
+
MemberUtil.extractMediaStatus = (participant: any): IMediaStatus => {
|
|
362
|
+
if (!participant) {
|
|
363
|
+
throw new ParameterError('Media status could not be extracted, participant is undefined.');
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
return {
|
|
367
|
+
audio: participant.status.audioStatus,
|
|
368
|
+
video: participant.status.videoStatus,
|
|
369
|
+
};
|
|
370
|
+
};
|
|
371
|
+
|
|
350
372
|
/**
|
|
351
373
|
* @param {Object} participant the locus participant
|
|
352
374
|
* @returns {String}
|
|
@@ -239,7 +239,10 @@ export default class ReconnectionManager {
|
|
|
239
239
|
* @memberof ReconnectionManager
|
|
240
240
|
*/
|
|
241
241
|
private async stopLocalShareTrack(reason: string) {
|
|
242
|
-
await this.meeting.unpublishTracks([
|
|
242
|
+
await this.meeting.unpublishTracks([
|
|
243
|
+
this.meeting.mediaProperties.shareVideoTrack,
|
|
244
|
+
this.meeting.mediaProperties.shareAudioTrack,
|
|
245
|
+
]);
|
|
243
246
|
Trigger.trigger(
|
|
244
247
|
this.meeting,
|
|
245
248
|
{
|