@webex/plugin-meetings 3.11.0-webex-services-ready.1 → 3.12.0-mobius-socket.1
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/aiEnableRequest/index.js +184 -0
- package/dist/aiEnableRequest/index.js.map +1 -0
- package/dist/aiEnableRequest/utils.js +36 -0
- package/dist/aiEnableRequest/utils.js.map +1 -0
- package/dist/annotation/index.js +14 -5
- package/dist/annotation/index.js.map +1 -1
- package/dist/breakouts/breakout.js +1 -1
- package/dist/breakouts/index.js +1 -1
- package/dist/config.js +7 -2
- package/dist/config.js.map +1 -1
- package/dist/constants.js +28 -6
- package/dist/constants.js.map +1 -1
- package/dist/hashTree/constants.js +3 -1
- package/dist/hashTree/constants.js.map +1 -1
- package/dist/hashTree/hashTree.js +18 -0
- package/dist/hashTree/hashTree.js.map +1 -1
- package/dist/hashTree/hashTreeParser.js +868 -419
- package/dist/hashTree/hashTreeParser.js.map +1 -1
- package/dist/hashTree/types.js +4 -2
- package/dist/hashTree/types.js.map +1 -1
- package/dist/hashTree/utils.js +10 -0
- package/dist/hashTree/utils.js.map +1 -1
- package/dist/index.js +11 -2
- package/dist/index.js.map +1 -1
- package/dist/interceptors/constant.js +12 -0
- package/dist/interceptors/constant.js.map +1 -0
- package/dist/interceptors/dataChannelAuthToken.js +290 -0
- package/dist/interceptors/dataChannelAuthToken.js.map +1 -0
- package/dist/interceptors/index.js +7 -0
- package/dist/interceptors/index.js.map +1 -1
- package/dist/interceptors/utils.js +27 -0
- package/dist/interceptors/utils.js.map +1 -0
- package/dist/interpretation/index.js +2 -2
- package/dist/interpretation/index.js.map +1 -1
- package/dist/interpretation/siLanguage.js +1 -1
- package/dist/locus-info/controlsUtils.js +5 -3
- package/dist/locus-info/controlsUtils.js.map +1 -1
- package/dist/locus-info/index.js +522 -131
- package/dist/locus-info/index.js.map +1 -1
- package/dist/locus-info/selfUtils.js +1 -0
- package/dist/locus-info/selfUtils.js.map +1 -1
- package/dist/locus-info/types.js.map +1 -1
- package/dist/media/MediaConnectionAwaiter.js +57 -1
- package/dist/media/MediaConnectionAwaiter.js.map +1 -1
- package/dist/media/properties.js +4 -2
- package/dist/media/properties.js.map +1 -1
- package/dist/meeting/in-meeting-actions.js +7 -1
- package/dist/meeting/in-meeting-actions.js.map +1 -1
- package/dist/meeting/index.js +1293 -929
- package/dist/meeting/index.js.map +1 -1
- package/dist/meeting/request.js +50 -0
- package/dist/meeting/request.js.map +1 -1
- package/dist/meeting/request.type.js.map +1 -1
- package/dist/meeting/util.js +133 -3
- package/dist/meeting/util.js.map +1 -1
- package/dist/meetings/index.js +117 -48
- package/dist/meetings/index.js.map +1 -1
- package/dist/member/index.js +10 -0
- package/dist/member/index.js.map +1 -1
- package/dist/member/util.js +10 -0
- package/dist/member/util.js.map +1 -1
- package/dist/metrics/constants.js +6 -1
- package/dist/metrics/constants.js.map +1 -1
- package/dist/multistream/mediaRequestManager.js +9 -60
- package/dist/multistream/mediaRequestManager.js.map +1 -1
- package/dist/multistream/remoteMediaManager.js +11 -0
- package/dist/multistream/remoteMediaManager.js.map +1 -1
- package/dist/multistream/sendSlotManager.js +116 -2
- package/dist/multistream/sendSlotManager.js.map +1 -1
- package/dist/reactions/reactions.type.js.map +1 -1
- package/dist/reconnection-manager/index.js +0 -1
- package/dist/reconnection-manager/index.js.map +1 -1
- package/dist/types/aiEnableRequest/index.d.ts +5 -0
- package/dist/types/aiEnableRequest/utils.d.ts +2 -0
- package/dist/types/config.d.ts +4 -0
- package/dist/types/constants.d.ts +23 -1
- package/dist/types/hashTree/constants.d.ts +1 -0
- package/dist/types/hashTree/hashTree.d.ts +7 -0
- package/dist/types/hashTree/hashTreeParser.d.ts +122 -14
- package/dist/types/hashTree/types.d.ts +3 -0
- package/dist/types/hashTree/utils.d.ts +6 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/interceptors/constant.d.ts +5 -0
- package/dist/types/interceptors/dataChannelAuthToken.d.ts +43 -0
- package/dist/types/interceptors/index.d.ts +2 -1
- package/dist/types/interceptors/utils.d.ts +1 -0
- package/dist/types/locus-info/index.d.ts +60 -8
- package/dist/types/locus-info/types.d.ts +7 -0
- package/dist/types/media/MediaConnectionAwaiter.d.ts +10 -1
- package/dist/types/media/properties.d.ts +2 -1
- package/dist/types/meeting/in-meeting-actions.d.ts +6 -0
- package/dist/types/meeting/index.d.ts +72 -7
- package/dist/types/meeting/request.d.ts +16 -1
- package/dist/types/meeting/request.type.d.ts +5 -0
- package/dist/types/meeting/util.d.ts +31 -0
- package/dist/types/meetings/index.d.ts +4 -2
- package/dist/types/member/index.d.ts +1 -0
- package/dist/types/member/util.d.ts +5 -0
- package/dist/types/metrics/constants.d.ts +5 -0
- package/dist/types/multistream/mediaRequestManager.d.ts +0 -23
- package/dist/types/multistream/sendSlotManager.d.ts +23 -1
- package/dist/types/reactions/reactions.type.d.ts +1 -0
- package/dist/types/webinar/utils.d.ts +6 -0
- package/dist/webinar/index.js +438 -163
- package/dist/webinar/index.js.map +1 -1
- package/dist/webinar/utils.js +25 -0
- package/dist/webinar/utils.js.map +1 -0
- package/package.json +24 -23
- package/src/aiEnableRequest/README.md +84 -0
- package/src/aiEnableRequest/index.ts +170 -0
- package/src/aiEnableRequest/utils.ts +25 -0
- package/src/annotation/index.ts +27 -7
- package/src/config.ts +4 -0
- package/src/constants.ts +29 -1
- package/src/hashTree/constants.ts +1 -0
- package/src/hashTree/hashTree.ts +17 -0
- package/src/hashTree/hashTreeParser.ts +761 -260
- package/src/hashTree/types.ts +4 -0
- package/src/hashTree/utils.ts +9 -0
- package/src/index.ts +8 -1
- package/src/interceptors/constant.ts +6 -0
- package/src/interceptors/dataChannelAuthToken.ts +170 -0
- package/src/interceptors/index.ts +2 -1
- package/src/interceptors/utils.ts +16 -0
- package/src/interpretation/index.ts +2 -2
- package/src/locus-info/controlsUtils.ts +11 -0
- package/src/locus-info/index.ts +579 -113
- package/src/locus-info/selfUtils.ts +1 -0
- package/src/locus-info/types.ts +8 -0
- package/src/media/MediaConnectionAwaiter.ts +41 -1
- package/src/media/properties.ts +3 -1
- package/src/meeting/in-meeting-actions.ts +12 -0
- package/src/meeting/index.ts +372 -86
- package/src/meeting/request.ts +42 -0
- package/src/meeting/request.type.ts +6 -0
- package/src/meeting/util.ts +160 -2
- package/src/meetings/index.ts +157 -44
- package/src/member/index.ts +10 -0
- package/src/member/util.ts +12 -0
- package/src/metrics/constants.ts +6 -0
- package/src/multistream/mediaRequestManager.ts +4 -54
- package/src/multistream/remoteMediaManager.ts +13 -0
- package/src/multistream/sendSlotManager.ts +97 -3
- package/src/reactions/reactions.type.ts +1 -0
- package/src/reconnection-manager/index.ts +0 -1
- package/src/webinar/index.ts +265 -6
- package/src/webinar/utils.ts +16 -0
- package/test/unit/spec/aiEnableRequest/index.ts +981 -0
- package/test/unit/spec/aiEnableRequest/utils.ts +130 -0
- package/test/unit/spec/annotation/index.ts +69 -7
- package/test/unit/spec/hashTree/hashTree.ts +66 -0
- package/test/unit/spec/hashTree/hashTreeParser.ts +2321 -175
- package/test/unit/spec/interceptors/dataChannelAuthToken.ts +210 -0
- package/test/unit/spec/interceptors/utils.ts +75 -0
- package/test/unit/spec/locus-info/controlsUtils.js +29 -0
- package/test/unit/spec/locus-info/index.js +1134 -55
- package/test/unit/spec/media/MediaConnectionAwaiter.ts +41 -1
- package/test/unit/spec/media/properties.ts +12 -3
- package/test/unit/spec/meeting/in-meeting-actions.ts +8 -2
- package/test/unit/spec/meeting/index.js +829 -121
- package/test/unit/spec/meeting/request.js +70 -0
- package/test/unit/spec/meeting/utils.js +438 -26
- package/test/unit/spec/meetings/index.js +653 -32
- package/test/unit/spec/member/index.js +28 -4
- package/test/unit/spec/member/util.js +65 -27
- package/test/unit/spec/multistream/mediaRequestManager.ts +2 -85
- package/test/unit/spec/multistream/remoteMediaManager.ts +30 -0
- package/test/unit/spec/multistream/sendSlotManager.ts +135 -36
- package/test/unit/spec/reconnection-manager/index.js +4 -8
- package/test/unit/spec/webinar/index.ts +534 -37
- package/test/unit/spec/webinar/utils.ts +39 -0
|
@@ -18,6 +18,7 @@ describe('member', () => {
|
|
|
18
18
|
assert.exists(member.supportsBreakouts);
|
|
19
19
|
assert.exists(member.supportLiveAnnotation);
|
|
20
20
|
assert.exists(member.canReclaimHost);
|
|
21
|
+
assert.exists(member.canApproveAIEnablement);
|
|
21
22
|
});
|
|
22
23
|
|
|
23
24
|
describe('roles', () => {
|
|
@@ -47,17 +48,24 @@ describe('member', () => {
|
|
|
47
48
|
it('checks that processParticipant calls canReclaimHost', () => {
|
|
48
49
|
sinon.spy(MemberUtil, 'canReclaimHost');
|
|
49
50
|
member.processParticipant(participant);
|
|
50
|
-
|
|
51
|
+
|
|
51
52
|
assert.calledOnceWithExactly(MemberUtil.canReclaimHost, participant);
|
|
52
53
|
});
|
|
53
54
|
|
|
54
55
|
it('checks that processParticipant calls isPresenterAssignmentProhibited', () => {
|
|
55
56
|
sinon.spy(MemberUtil, 'isPresenterAssignmentProhibited');
|
|
56
57
|
member.processParticipant(participant);
|
|
57
|
-
|
|
58
|
+
|
|
58
59
|
assert.calledOnceWithExactly(MemberUtil.isPresenterAssignmentProhibited, participant);
|
|
59
60
|
});
|
|
60
|
-
|
|
61
|
+
|
|
62
|
+
it('checks that processParticipant calls canApproveAIEnablement', () => {
|
|
63
|
+
sinon.spy(MemberUtil, 'canApproveAIEnablement');
|
|
64
|
+
member.processParticipant(participant);
|
|
65
|
+
|
|
66
|
+
assert.calledOnceWithExactly(MemberUtil.canApproveAIEnablement, participant);
|
|
67
|
+
});
|
|
68
|
+
});
|
|
61
69
|
|
|
62
70
|
describe('#processMember', () => {
|
|
63
71
|
it('checks that processMember calls isRemovable', () => {
|
|
@@ -80,5 +88,21 @@ describe('member', () => {
|
|
|
80
88
|
|
|
81
89
|
assert.calledOnceWithExactly(MemberUtil.extractMediaStatus, participant);
|
|
82
90
|
});
|
|
83
|
-
})
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
describe('canApproveAIEnablement integration', () => {
|
|
94
|
+
it('sets canApproveAIEnablement to the value returned by MemberUtil.canApproveAIEnablement', () => {
|
|
95
|
+
const testParticipant = {controls: {}, status: {}};
|
|
96
|
+
|
|
97
|
+
sinon.stub(MemberUtil, 'canApproveAIEnablement').returns(true);
|
|
98
|
+
const memberWithTrue = new Member(testParticipant);
|
|
99
|
+
assert.isTrue(memberWithTrue.canApproveAIEnablement);
|
|
100
|
+
|
|
101
|
+
MemberUtil.canApproveAIEnablement.restore();
|
|
102
|
+
|
|
103
|
+
sinon.stub(MemberUtil, 'canApproveAIEnablement').returns(false);
|
|
104
|
+
const memberWithFalse = new Member(testParticipant);
|
|
105
|
+
assert.isFalse(memberWithFalse.canApproveAIEnablement);
|
|
106
|
+
});
|
|
107
|
+
});
|
|
84
108
|
});
|
|
@@ -82,6 +82,46 @@ describe('plugin-meetings', () => {
|
|
|
82
82
|
});
|
|
83
83
|
});
|
|
84
84
|
|
|
85
|
+
describe('MemberUtil.canApproveAIEnablement', () => {
|
|
86
|
+
it('returns false when there is no participant', () => {
|
|
87
|
+
assert.isFalse(MemberUtil.canApproveAIEnablement());
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('returns false when there is null participant', () => {
|
|
91
|
+
assert.isFalse(MemberUtil.canApproveAIEnablement(null));
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('returns true when attendeeRequestAiAssistantNotAllowed is false', () => {
|
|
95
|
+
const participant = {
|
|
96
|
+
attendeeRequestAiAssistantNotAllowed: false,
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
assert.isTrue(MemberUtil.canApproveAIEnablement(participant));
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('returns false when attendeeRequestAiAssistantNotAllowed is true', () => {
|
|
103
|
+
const participant = {
|
|
104
|
+
attendeeRequestAiAssistantNotAllowed: true,
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
assert.isFalse(MemberUtil.canApproveAIEnablement(participant));
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it('returns true when attendeeRequestAiAssistantNotAllowed is undefined', () => {
|
|
111
|
+
const participant = {
|
|
112
|
+
attendeeRequestAiAssistantNotAllowed: undefined,
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
assert.isTrue(MemberUtil.canApproveAIEnablement(participant));
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it('returns true when attendeeRequestAiAssistantNotAllowed is not present', () => {
|
|
119
|
+
const participant = {};
|
|
120
|
+
|
|
121
|
+
assert.isTrue(MemberUtil.canApproveAIEnablement(participant));
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
|
|
85
125
|
describe('MemberUtil.extractControlRoles', () => {
|
|
86
126
|
it('happy path extract control roles', () => {
|
|
87
127
|
const participant = {
|
|
@@ -377,7 +417,6 @@ describe('plugin-meetings', () => {
|
|
|
377
417
|
assert.isFalse(MemberUtil.isBrb(participant));
|
|
378
418
|
});
|
|
379
419
|
|
|
380
|
-
|
|
381
420
|
it('returns false when brb is not present', () => {
|
|
382
421
|
const participant = {
|
|
383
422
|
controls: {},
|
|
@@ -417,29 +456,28 @@ describe('plugin-meetings', () => {
|
|
|
417
456
|
});
|
|
418
457
|
});
|
|
419
458
|
|
|
420
|
-
describe('MemberUtil.isSupportsSingleUserAutoEndMeeting', () => {
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
459
|
+
describe('MemberUtil.isSupportsSingleUserAutoEndMeeting', () => {
|
|
460
|
+
it('throws an error when there is no participant', () => {
|
|
461
|
+
assert.throws(() => {
|
|
462
|
+
MemberUtil.isSupportsSingleUserAutoEndMeeting();
|
|
463
|
+
}, 'Single user auto end meeting support could not be processed, participant is undefined.');
|
|
464
|
+
});
|
|
426
465
|
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
466
|
+
it('returns true when single user auto end meeting is supported', () => {
|
|
467
|
+
const participant = {
|
|
468
|
+
supportsSingleUserAutoEndMeeting: {},
|
|
469
|
+
};
|
|
470
|
+
assert.isTrue(MemberUtil.isSupportsSingleUserAutoEndMeeting(participant));
|
|
471
|
+
});
|
|
433
472
|
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
473
|
+
it('returns false when single user auto end meeting is not supported', () => {
|
|
474
|
+
const participant = {
|
|
475
|
+
doesNotSupportSingleUserAutoEndMeeting: {},
|
|
476
|
+
};
|
|
438
477
|
|
|
439
|
-
|
|
478
|
+
assert.isFalse(MemberUtil.isSupportsSingleUserAutoEndMeeting(participant));
|
|
479
|
+
});
|
|
440
480
|
});
|
|
441
|
-
});
|
|
442
|
-
|
|
443
481
|
|
|
444
482
|
describe('MemberUtil.isLiveAnnotationSupported', () => {
|
|
445
483
|
it('throws an error when there is no participant', () => {
|
|
@@ -585,7 +623,7 @@ describe('MemberUtil.isSupportsSingleUserAutoEndMeeting', () => {
|
|
|
585
623
|
describe('MemberUtil.isPresenterAssignmentProhibited', () => {
|
|
586
624
|
it('returns true when isPresenterAssignmentProhibited is true', () => {
|
|
587
625
|
const participant = {
|
|
588
|
-
presenterAssignmentNotAllowed: true
|
|
626
|
+
presenterAssignmentNotAllowed: true,
|
|
589
627
|
};
|
|
590
628
|
|
|
591
629
|
assert.isTrue(MemberUtil.isPresenterAssignmentProhibited(participant));
|
|
@@ -610,16 +648,16 @@ describe('MemberUtil.isSupportsSingleUserAutoEndMeeting', () => {
|
|
|
610
648
|
describe('extractMediaStatus', () => {
|
|
611
649
|
it('throws an error when there is no participant', () => {
|
|
612
650
|
assert.throws(() => {
|
|
613
|
-
MemberUtil.extractMediaStatus()
|
|
651
|
+
MemberUtil.extractMediaStatus();
|
|
614
652
|
}, 'Media status could not be extracted, participant is undefined.');
|
|
615
653
|
});
|
|
616
654
|
|
|
617
655
|
it('returns undefined media status when participant audio/video status is not present', () => {
|
|
618
656
|
const participant = {
|
|
619
|
-
status: {}
|
|
657
|
+
status: {},
|
|
620
658
|
};
|
|
621
659
|
|
|
622
|
-
const mediaStatus = MemberUtil.extractMediaStatus(participant)
|
|
660
|
+
const mediaStatus = MemberUtil.extractMediaStatus(participant);
|
|
623
661
|
|
|
624
662
|
assert.deepEqual(mediaStatus, {audio: undefined, video: undefined});
|
|
625
663
|
});
|
|
@@ -628,11 +666,11 @@ describe('extractMediaStatus', () => {
|
|
|
628
666
|
const participant = {
|
|
629
667
|
status: {
|
|
630
668
|
audioStatus: 'RECVONLY',
|
|
631
|
-
videoStatus: 'SENDRECV'
|
|
632
|
-
}
|
|
669
|
+
videoStatus: 'SENDRECV',
|
|
670
|
+
},
|
|
633
671
|
};
|
|
634
672
|
|
|
635
|
-
const mediaStatus = MemberUtil.extractMediaStatus(participant)
|
|
673
|
+
const mediaStatus = MemberUtil.extractMediaStatus(participant);
|
|
636
674
|
|
|
637
675
|
assert.deepEqual(mediaStatus, {audio: 'RECVONLY', video: 'SENDRECV'});
|
|
638
676
|
});
|
|
@@ -666,8 +666,8 @@ describe('MediaRequestManager', () => {
|
|
|
666
666
|
]);
|
|
667
667
|
});
|
|
668
668
|
|
|
669
|
-
it('
|
|
670
|
-
// send some requests and commit them
|
|
669
|
+
it('clears all the requests on reset()', () => {
|
|
670
|
+
// send some requests and commit them
|
|
671
671
|
addReceiverSelectedRequest(1500, fakeReceiveSlots[0], MAX_FS_1080p, false);
|
|
672
672
|
addReceiverSelectedRequest(1501, fakeReceiveSlots[1], MAX_FS_1080p, false);
|
|
673
673
|
addActiveSpeakerRequest(
|
|
@@ -722,95 +722,12 @@ describe('MediaRequestManager', () => {
|
|
|
722
722
|
},
|
|
723
723
|
]);
|
|
724
724
|
|
|
725
|
-
// check that when calling commit()
|
|
726
|
-
// all requests are not re-sent again (avoid duplicate requests)
|
|
727
|
-
mediaRequestManager.commit();
|
|
728
|
-
|
|
729
|
-
assert.notCalled(sendMediaRequestsCallback);
|
|
730
|
-
|
|
731
|
-
// now reset everything
|
|
732
|
-
mediaRequestManager.reset();
|
|
733
|
-
|
|
734
|
-
// calling commit now should not cause any requests to be sent out
|
|
735
|
-
mediaRequestManager.commit();
|
|
736
|
-
checkMediaRequestsSent([]);
|
|
737
|
-
});
|
|
738
|
-
|
|
739
|
-
it('makes sure to call requests correctly after reset was called and another request was added', () => {
|
|
740
|
-
addReceiverSelectedRequest(1500, fakeReceiveSlots[0], MAX_FS_1080p, false);
|
|
741
|
-
|
|
742
|
-
assert.notCalled(sendMediaRequestsCallback);
|
|
743
|
-
|
|
744
|
-
mediaRequestManager.commit();
|
|
745
|
-
checkMediaRequestsSent([
|
|
746
|
-
{
|
|
747
|
-
policy: 'receiver-selected',
|
|
748
|
-
csi: 1500,
|
|
749
|
-
receiveSlot: fakeWcmeSlots[0],
|
|
750
|
-
maxPayloadBitsPerSecond: MAX_PAYLOADBITSPS_1080p,
|
|
751
|
-
maxFs: MAX_FS_1080p,
|
|
752
|
-
maxMbps: MAX_MBPS_1080p,
|
|
753
|
-
},
|
|
754
|
-
]);
|
|
755
|
-
|
|
756
725
|
// now reset everything
|
|
757
726
|
mediaRequestManager.reset();
|
|
758
727
|
|
|
759
728
|
// calling commit now should not cause any requests to be sent out
|
|
760
729
|
mediaRequestManager.commit();
|
|
761
730
|
checkMediaRequestsSent([]);
|
|
762
|
-
|
|
763
|
-
//add new request
|
|
764
|
-
addReceiverSelectedRequest(1501, fakeReceiveSlots[1], MAX_FS_1080p, false);
|
|
765
|
-
|
|
766
|
-
// commit
|
|
767
|
-
mediaRequestManager.commit();
|
|
768
|
-
|
|
769
|
-
// check the new request was sent
|
|
770
|
-
checkMediaRequestsSent([
|
|
771
|
-
{
|
|
772
|
-
policy: 'receiver-selected',
|
|
773
|
-
csi: 1501,
|
|
774
|
-
receiveSlot: fakeWcmeSlots[1],
|
|
775
|
-
maxPayloadBitsPerSecond: MAX_PAYLOADBITSPS_1080p,
|
|
776
|
-
maxFs: MAX_FS_1080p,
|
|
777
|
-
maxMbps: MAX_MBPS_1080p,
|
|
778
|
-
},
|
|
779
|
-
]);
|
|
780
|
-
});
|
|
781
|
-
|
|
782
|
-
it('can send same media request after previous requests have been cleared', () => {
|
|
783
|
-
// add a request and commit
|
|
784
|
-
addReceiverSelectedRequest(1500, fakeReceiveSlots[0], MAX_FS_1080p, false);
|
|
785
|
-
mediaRequestManager.commit();
|
|
786
|
-
checkMediaRequestsSent([
|
|
787
|
-
{
|
|
788
|
-
policy: 'receiver-selected',
|
|
789
|
-
csi: 1500,
|
|
790
|
-
maxPayloadBitsPerSecond: MAX_PAYLOADBITSPS_1080p,
|
|
791
|
-
receiveSlot: fakeWcmeSlots[0],
|
|
792
|
-
maxFs: MAX_FS_1080p,
|
|
793
|
-
maxMbps: MAX_MBPS_1080p,
|
|
794
|
-
},
|
|
795
|
-
]);
|
|
796
|
-
|
|
797
|
-
// clear previous requests
|
|
798
|
-
mediaRequestManager.clearPreviousRequests();
|
|
799
|
-
|
|
800
|
-
// commit same request
|
|
801
|
-
mediaRequestManager.commit();
|
|
802
|
-
|
|
803
|
-
// check the request was sent
|
|
804
|
-
checkMediaRequestsSent([
|
|
805
|
-
{
|
|
806
|
-
policy: 'receiver-selected',
|
|
807
|
-
csi: 1500,
|
|
808
|
-
maxPayloadBitsPerSecond: MAX_PAYLOADBITSPS_1080p,
|
|
809
|
-
receiveSlot: fakeWcmeSlots[0],
|
|
810
|
-
maxFs: MAX_FS_1080p,
|
|
811
|
-
maxMbps: MAX_MBPS_1080p,
|
|
812
|
-
},
|
|
813
|
-
]);
|
|
814
731
|
});
|
|
815
732
|
|
|
816
733
|
it('re-sends media requests after degradation preferences are set', () => {
|
|
@@ -965,6 +965,36 @@ describe('RemoteMediaManager', () => {
|
|
|
965
965
|
);
|
|
966
966
|
});
|
|
967
967
|
|
|
968
|
+
it('allocates 25 video slots for AllEqual25 layout', async () => {
|
|
969
|
+
const config = cloneDeep(DefaultTestConfiguration);
|
|
970
|
+
config.video.layouts['AllEqual25'] = {
|
|
971
|
+
activeSpeakerVideoPaneGroups: [
|
|
972
|
+
{id: 'main', numPanes: 25, size: 'best', priority: 255},
|
|
973
|
+
],
|
|
974
|
+
};
|
|
975
|
+
config.video.initialLayoutId = 'AllEqual25';
|
|
976
|
+
|
|
977
|
+
let slotCount = 0;
|
|
978
|
+
fakeReceiveSlotManager.allocateSlot.callsFake((mediaType: MediaType) => {
|
|
979
|
+
if (mediaType === MediaType.VideoMain) {
|
|
980
|
+
slotCount += 1;
|
|
981
|
+
return Promise.resolve(new FakeSlot(mediaType, `fake video ${slotCount}`));
|
|
982
|
+
}
|
|
983
|
+
return Promise.resolve(fakeAudioSlot);
|
|
984
|
+
});
|
|
985
|
+
|
|
986
|
+
remoteMediaManager = new RemoteMediaManager(
|
|
987
|
+
fakeReceiveSlotManager,
|
|
988
|
+
fakeMediaRequestManagers,
|
|
989
|
+
config
|
|
990
|
+
);
|
|
991
|
+
|
|
992
|
+
await remoteMediaManager.start();
|
|
993
|
+
|
|
994
|
+
assert.strictEqual(remoteMediaManager.getLayoutId(), 'AllEqual25');
|
|
995
|
+
assert.strictEqual(remoteMediaManager.slots.video.activeSpeaker.length, 25);
|
|
996
|
+
});
|
|
997
|
+
|
|
968
998
|
it('releases slots when switching to layout that requires less active speaker slots', async () => {
|
|
969
999
|
// start with "AllEqual" layout that needs just 9 video slots
|
|
970
1000
|
const config = cloneDeep(DefaultTestConfiguration);
|
|
@@ -1,19 +1,28 @@
|
|
|
1
1
|
import 'jsdom-global/register';
|
|
2
2
|
import SendSlotManager from '@webex/plugin-meetings/src/multistream/sendSlotManager';
|
|
3
|
-
import { LocalStream, MediaType, MultistreamRoapMediaConnection } from "@webex/internal-media-core";
|
|
4
|
-
import {expect} from '@webex/test-helper-chai';
|
|
3
|
+
import { LocalStream, MediaType, MultistreamRoapMediaConnection, MediaCodecMimeType } from "@webex/internal-media-core";
|
|
4
|
+
import {assert, expect} from '@webex/test-helper-chai';
|
|
5
5
|
import sinon from 'sinon';
|
|
6
|
+
import Metrics from '@webex/plugin-meetings/src/metrics';
|
|
7
|
+
import BEHAVIORAL_METRICS from '@webex/plugin-meetings/src/metrics/constants';
|
|
6
8
|
|
|
7
9
|
describe('SendSlotsManager', () => {
|
|
8
10
|
let sendSlotsManager: SendSlotManager;
|
|
9
11
|
const LoggerProxy = {
|
|
10
12
|
logger: {
|
|
11
13
|
info: sinon.stub(),
|
|
14
|
+
warn: sinon.stub(),
|
|
15
|
+
error: sinon.stub(),
|
|
12
16
|
},
|
|
13
17
|
};
|
|
14
18
|
|
|
15
19
|
beforeEach(() => {
|
|
16
20
|
sendSlotsManager = new SendSlotManager(LoggerProxy);
|
|
21
|
+
sinon.stub(Metrics, 'sendBehavioralMetric');
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
afterEach(() => {
|
|
25
|
+
sinon.restore();
|
|
17
26
|
});
|
|
18
27
|
|
|
19
28
|
describe('createSlot', () => {
|
|
@@ -29,13 +38,13 @@ describe('SendSlotsManager', () => {
|
|
|
29
38
|
it('should create a slot for the given mediaType', () => {
|
|
30
39
|
sendSlotsManager.createSlot(mediaConnection, mediaType);
|
|
31
40
|
|
|
32
|
-
|
|
41
|
+
assert.calledWith(mediaConnection.createSendSlot, mediaType, true);
|
|
33
42
|
});
|
|
34
43
|
|
|
35
44
|
it('should create a slot for the given mediaType & active state', () => {
|
|
36
45
|
sendSlotsManager.createSlot(mediaConnection, mediaType, false);
|
|
37
46
|
|
|
38
|
-
|
|
47
|
+
assert.calledWith(mediaConnection.createSendSlot, mediaType, false);
|
|
39
48
|
});
|
|
40
49
|
|
|
41
50
|
it('should throw an error if a slot for the given mediaType already exists', () => {
|
|
@@ -86,14 +95,12 @@ describe('SendSlotsManager', () => {
|
|
|
86
95
|
|
|
87
96
|
await sendSlotsManager.publishStream(mediaType, stream);
|
|
88
97
|
|
|
89
|
-
|
|
98
|
+
assert.calledWith(slot.publishStream, stream);
|
|
90
99
|
});
|
|
91
100
|
|
|
92
|
-
it('should throw an error if a slot for the given mediaType does not exist', (
|
|
93
|
-
sendSlotsManager.publishStream(mediaType, stream)
|
|
94
|
-
|
|
95
|
-
done();
|
|
96
|
-
});
|
|
101
|
+
it('should throw an error if a slot for the given mediaType does not exist', async () => {
|
|
102
|
+
await expect(sendSlotsManager.publishStream(mediaType, stream))
|
|
103
|
+
.to.be.rejectedWith(`Slot for ${mediaType} does not exist`);
|
|
97
104
|
});
|
|
98
105
|
});
|
|
99
106
|
|
|
@@ -116,14 +123,12 @@ describe('SendSlotsManager', () => {
|
|
|
116
123
|
|
|
117
124
|
await sendSlotsManager.unpublishStream(mediaType);
|
|
118
125
|
|
|
119
|
-
|
|
126
|
+
assert.called(slot.unpublishStream);
|
|
120
127
|
});
|
|
121
128
|
|
|
122
|
-
it('should throw an error if a slot for the given mediaType does not exist',(
|
|
123
|
-
sendSlotsManager.unpublishStream(mediaType)
|
|
124
|
-
|
|
125
|
-
done();
|
|
126
|
-
});
|
|
129
|
+
it('should throw an error if a slot for the given mediaType does not exist', async () => {
|
|
130
|
+
await expect(sendSlotsManager.unpublishStream(mediaType))
|
|
131
|
+
.to.be.rejectedWith(`Slot for ${mediaType} does not exist`);
|
|
127
132
|
});
|
|
128
133
|
});
|
|
129
134
|
|
|
@@ -147,7 +152,7 @@ describe('SendSlotsManager', () => {
|
|
|
147
152
|
|
|
148
153
|
await sendSlotsManager.setNamedMediaGroups(mediaType, groups);
|
|
149
154
|
|
|
150
|
-
|
|
155
|
+
assert.calledWith(slot.setNamedMediaGroups, groups);
|
|
151
156
|
});
|
|
152
157
|
|
|
153
158
|
it('should throw an error if the given mediaType is not audio', () => {
|
|
@@ -169,16 +174,16 @@ describe('SendSlotsManager', () => {
|
|
|
169
174
|
} as MultistreamRoapMediaConnection;
|
|
170
175
|
});
|
|
171
176
|
|
|
172
|
-
it('should set the active state of the sendSlot for the given mediaType',
|
|
177
|
+
it('should set the active state of the sendSlot for the given mediaType', () => {
|
|
173
178
|
const slot = {
|
|
174
|
-
|
|
179
|
+
active: false,
|
|
175
180
|
};
|
|
176
181
|
mediaConnection.createSendSlot.returns(slot);
|
|
177
182
|
sendSlotsManager.createSlot(mediaConnection, mediaType);
|
|
178
183
|
|
|
179
|
-
|
|
184
|
+
sendSlotsManager.setActive(mediaType, true);
|
|
180
185
|
|
|
181
|
-
expect(slot.
|
|
186
|
+
expect(slot.active).to.be.true;
|
|
182
187
|
});
|
|
183
188
|
|
|
184
189
|
it('should throw an error if a slot for the given mediaType does not exist', () => {
|
|
@@ -197,7 +202,7 @@ describe('SendSlotsManager', () => {
|
|
|
197
202
|
} as MultistreamRoapMediaConnection;
|
|
198
203
|
});
|
|
199
204
|
|
|
200
|
-
it('should
|
|
205
|
+
it('should delegate to slot.setCodecParameters, log deprecation warning and send deprecation metric', async () => {
|
|
201
206
|
const slot = {
|
|
202
207
|
setCodecParameters: sinon.stub().resolves(),
|
|
203
208
|
};
|
|
@@ -206,14 +211,17 @@ describe('SendSlotsManager', () => {
|
|
|
206
211
|
|
|
207
212
|
await sendSlotsManager.setCodecParameters(mediaType, codecParameters);
|
|
208
213
|
|
|
209
|
-
|
|
214
|
+
assert.calledWith(slot.setCodecParameters, codecParameters);
|
|
215
|
+
assert.called(LoggerProxy.logger.warn);
|
|
216
|
+
assert.calledWith(Metrics.sendBehavioralMetric as sinon.SinonStub,
|
|
217
|
+
BEHAVIORAL_METRICS.DEPRECATED_SET_CODEC_PARAMETERS_USED,
|
|
218
|
+
{ mediaType, codecParameters }
|
|
219
|
+
);
|
|
210
220
|
});
|
|
211
221
|
|
|
212
|
-
it('should throw an error if a slot for the given mediaType does not exist', (
|
|
213
|
-
sendSlotsManager.setCodecParameters(mediaType, codecParameters)
|
|
214
|
-
|
|
215
|
-
done();
|
|
216
|
-
});
|
|
222
|
+
it('should throw an error if a slot for the given mediaType does not exist', async () => {
|
|
223
|
+
await expect(sendSlotsManager.setCodecParameters(mediaType, codecParameters))
|
|
224
|
+
.to.be.rejectedWith(`Slot for ${mediaType} does not exist`);
|
|
217
225
|
});
|
|
218
226
|
});
|
|
219
227
|
|
|
@@ -227,23 +235,114 @@ describe('SendSlotsManager', () => {
|
|
|
227
235
|
} as MultistreamRoapMediaConnection;
|
|
228
236
|
});
|
|
229
237
|
|
|
230
|
-
it('should
|
|
238
|
+
it('should delegate to slot.deleteCodecParameters, log deprecation warning and send deprecation metric', async () => {
|
|
231
239
|
const slot = {
|
|
232
240
|
deleteCodecParameters: sinon.stub().resolves(),
|
|
233
241
|
};
|
|
234
242
|
mediaConnection.createSendSlot.returns(slot);
|
|
235
243
|
sendSlotsManager.createSlot(mediaConnection, mediaType);
|
|
236
244
|
|
|
237
|
-
await sendSlotsManager.deleteCodecParameters(mediaType,[]);
|
|
245
|
+
await sendSlotsManager.deleteCodecParameters(mediaType, []);
|
|
246
|
+
|
|
247
|
+
assert.calledWith(slot.deleteCodecParameters, []);
|
|
248
|
+
assert.called(LoggerProxy.logger.warn);
|
|
249
|
+
assert.calledWith(Metrics.sendBehavioralMetric as sinon.SinonStub,
|
|
250
|
+
BEHAVIORAL_METRICS.DEPRECATED_DELETE_CODEC_PARAMETERS_USED,
|
|
251
|
+
{ mediaType, parameters: [] }
|
|
252
|
+
);
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
it('should throw an error if a slot for the given mediaType does not exist', async () => {
|
|
256
|
+
await expect(sendSlotsManager.deleteCodecParameters(mediaType, []))
|
|
257
|
+
.to.be.rejectedWith(`Slot for ${mediaType} does not exist`);
|
|
258
|
+
});
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
describe('setCustomCodecParameters', () => {
|
|
262
|
+
let mediaConnection;
|
|
263
|
+
const mediaType = MediaType.AudioMain;
|
|
264
|
+
const codecMimeType = MediaCodecMimeType.OPUS;
|
|
265
|
+
const parameters = { maxaveragebitrate: '64000' };
|
|
266
|
+
|
|
267
|
+
beforeEach(() => {
|
|
268
|
+
mediaConnection = {
|
|
269
|
+
createSendSlot: sinon.stub(),
|
|
270
|
+
} as MultistreamRoapMediaConnection;
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
it('should set custom codec parameters on the sendSlot for the given mediaType and codec, log info and send metric', async () => {
|
|
274
|
+
const slot = {
|
|
275
|
+
setCustomCodecParameters: sinon.stub().resolves(),
|
|
276
|
+
};
|
|
277
|
+
mediaConnection.createSendSlot.returns(slot);
|
|
278
|
+
sendSlotsManager.createSlot(mediaConnection, mediaType);
|
|
279
|
+
|
|
280
|
+
await sendSlotsManager.setCustomCodecParameters(mediaType, codecMimeType, parameters);
|
|
281
|
+
|
|
282
|
+
assert.calledWith(slot.setCustomCodecParameters, codecMimeType, parameters);
|
|
283
|
+
assert.called(LoggerProxy.logger.info);
|
|
284
|
+
assert.calledWith(Metrics.sendBehavioralMetric as sinon.SinonStub,
|
|
285
|
+
BEHAVIORAL_METRICS.SET_CUSTOM_CODEC_PARAMETERS_USED,
|
|
286
|
+
{ mediaType, codecMimeType, parameters }
|
|
287
|
+
);
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
it('should throw an error if a slot for the given mediaType does not exist', async () => {
|
|
291
|
+
await expect(sendSlotsManager.setCustomCodecParameters(mediaType, codecMimeType, parameters))
|
|
292
|
+
.to.be.rejectedWith(`Slot for ${mediaType} does not exist`);
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
it('should throw and log error when setCustomCodecParameters fails', async () => {
|
|
296
|
+
const error = new Error('codec parameter failure');
|
|
297
|
+
const slot = {
|
|
298
|
+
setCustomCodecParameters: sinon.stub().rejects(error),
|
|
299
|
+
};
|
|
300
|
+
mediaConnection.createSendSlot.returns(slot);
|
|
301
|
+
sendSlotsManager.createSlot(mediaConnection, mediaType);
|
|
302
|
+
|
|
303
|
+
await expect(sendSlotsManager.setCustomCodecParameters(mediaType, codecMimeType, parameters))
|
|
304
|
+
.to.be.rejectedWith('codec parameter failure');
|
|
305
|
+
|
|
306
|
+
assert.called(LoggerProxy.logger.error);
|
|
307
|
+
assert.calledWith(Metrics.sendBehavioralMetric as sinon.SinonStub,
|
|
308
|
+
BEHAVIORAL_METRICS.SET_CUSTOM_CODEC_PARAMETERS_USED,
|
|
309
|
+
{ mediaType, codecMimeType, parameters }
|
|
310
|
+
);
|
|
311
|
+
});
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
describe('markCustomCodecParametersForDeletion', () => {
|
|
315
|
+
let mediaConnection;
|
|
316
|
+
const mediaType = MediaType.AudioMain;
|
|
317
|
+
const codecMimeType = MediaCodecMimeType.OPUS;
|
|
318
|
+
const parameters = ['maxaveragebitrate', 'maxplaybackrate'];
|
|
319
|
+
|
|
320
|
+
beforeEach(() => {
|
|
321
|
+
mediaConnection = {
|
|
322
|
+
createSendSlot: sinon.stub(),
|
|
323
|
+
} as MultistreamRoapMediaConnection;
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
it('should mark custom codec parameters for deletion on the sendSlot for the given mediaType and codec, log info and send metric', async () => {
|
|
327
|
+
const slot = {
|
|
328
|
+
markCustomCodecParametersForDeletion: sinon.stub().resolves(),
|
|
329
|
+
};
|
|
330
|
+
mediaConnection.createSendSlot.returns(slot);
|
|
331
|
+
sendSlotsManager.createSlot(mediaConnection, mediaType);
|
|
332
|
+
|
|
333
|
+
await sendSlotsManager.markCustomCodecParametersForDeletion(mediaType, codecMimeType, parameters);
|
|
238
334
|
|
|
239
|
-
|
|
335
|
+
assert.calledWith(slot.markCustomCodecParametersForDeletion, codecMimeType, parameters);
|
|
336
|
+
assert.called(LoggerProxy.logger.info);
|
|
337
|
+
assert.calledWith(Metrics.sendBehavioralMetric as sinon.SinonStub,
|
|
338
|
+
BEHAVIORAL_METRICS.MARK_CUSTOM_CODEC_PARAMETERS_FOR_DELETION_USED,
|
|
339
|
+
{ mediaType, codecMimeType, parameters }
|
|
340
|
+
);
|
|
240
341
|
});
|
|
241
342
|
|
|
242
|
-
it('should throw an error if a slot for the given mediaType does not exist', (
|
|
243
|
-
sendSlotsManager.
|
|
244
|
-
|
|
245
|
-
done();
|
|
246
|
-
});
|
|
343
|
+
it('should throw an error if a slot for the given mediaType does not exist', async () => {
|
|
344
|
+
await expect(sendSlotsManager.markCustomCodecParametersForDeletion(mediaType, codecMimeType, parameters))
|
|
345
|
+
.to.be.rejectedWith(`Slot for ${mediaType} does not exist`);
|
|
247
346
|
});
|
|
248
347
|
});
|
|
249
348
|
|
|
@@ -54,8 +54,8 @@ describe('plugin-meetings', () => {
|
|
|
54
54
|
webrtcMediaConnection: fakeMediaConnection,
|
|
55
55
|
},
|
|
56
56
|
mediaRequestManagers: {
|
|
57
|
-
audio: {commit: sinon.stub()
|
|
58
|
-
video: {commit: sinon.stub()
|
|
57
|
+
audio: {commit: sinon.stub()},
|
|
58
|
+
video: {commit: sinon.stub()},
|
|
59
59
|
},
|
|
60
60
|
roap: {
|
|
61
61
|
doTurnDiscovery: sinon.stub().resolves({
|
|
@@ -179,26 +179,22 @@ describe('plugin-meetings', () => {
|
|
|
179
179
|
});
|
|
180
180
|
});
|
|
181
181
|
|
|
182
|
-
it('does not
|
|
182
|
+
it('does not re-request media for non-multistream meetings', async () => {
|
|
183
183
|
fakeMeeting.isMultistream = false;
|
|
184
184
|
const rm = new ReconnectionManager(fakeMeeting);
|
|
185
185
|
|
|
186
186
|
await rm.reconnect();
|
|
187
187
|
|
|
188
|
-
assert.notCalled(fakeMeeting.mediaRequestManagers.audio.clearPreviousRequests);
|
|
189
|
-
assert.notCalled(fakeMeeting.mediaRequestManagers.video.clearPreviousRequests);
|
|
190
188
|
assert.notCalled(fakeMeeting.mediaRequestManagers.audio.commit);
|
|
191
189
|
assert.notCalled(fakeMeeting.mediaRequestManagers.video.commit);
|
|
192
190
|
});
|
|
193
191
|
|
|
194
|
-
it('does
|
|
192
|
+
it('does re-request media for multistream meetings', async () => {
|
|
195
193
|
fakeMeeting.isMultistream = true;
|
|
196
194
|
const rm = new ReconnectionManager(fakeMeeting);
|
|
197
195
|
|
|
198
196
|
await rm.reconnect();
|
|
199
197
|
|
|
200
|
-
assert.calledOnce(fakeMeeting.mediaRequestManagers.audio.clearPreviousRequests);
|
|
201
|
-
assert.calledOnce(fakeMeeting.mediaRequestManagers.video.clearPreviousRequests);
|
|
202
198
|
assert.calledOnce(fakeMeeting.mediaRequestManagers.audio.commit);
|
|
203
199
|
assert.calledOnce(fakeMeeting.mediaRequestManagers.video.commit);
|
|
204
200
|
});
|