@webex/plugin-meetings 3.0.0-beta.147 → 3.0.0-beta.149
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/annotation/index.js +0 -2
- package/dist/annotation/index.js.map +1 -1
- package/dist/breakouts/breakout.js +1 -1
- package/dist/breakouts/index.js +1 -1
- package/dist/meeting/index.js +18 -6
- package/dist/meeting/index.js.map +1 -1
- package/dist/multistream/remoteMediaGroup.js +52 -3
- package/dist/multistream/remoteMediaGroup.js.map +1 -1
- package/dist/multistream/remoteMediaManager.js +21 -0
- package/dist/multistream/remoteMediaManager.js.map +1 -1
- package/dist/types/meeting/index.d.ts +7 -0
- package/dist/types/multistream/remoteMediaGroup.d.ts +0 -1
- package/dist/types/multistream/remoteMediaManager.d.ts +10 -0
- package/package.json +19 -19
- package/src/annotation/index.ts +0 -2
- package/src/meeting/index.ts +24 -11
- package/src/multistream/remoteMediaGroup.ts +52 -0
- package/src/multistream/remoteMediaManager.ts +18 -0
- package/test/unit/spec/annotation/index.ts +4 -4
- package/test/unit/spec/meeting/index.js +44 -23
- package/test/unit/spec/multistream/remoteMediaGroup.ts +219 -0
- package/test/unit/spec/multistream/remoteMediaManager.ts +88 -1
package/src/meeting/index.ts
CHANGED
|
@@ -2200,6 +2200,29 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
2200
2200
|
});
|
|
2201
2201
|
}
|
|
2202
2202
|
|
|
2203
|
+
/**
|
|
2204
|
+
* Trigger annotation info update event
|
|
2205
|
+
@returns {undefined}
|
|
2206
|
+
@param {object} contentShare
|
|
2207
|
+
@param {object} previousContentShare
|
|
2208
|
+
*/
|
|
2209
|
+
private triggerAnnotationInfoEvent(contentShare, previousContentShare) {
|
|
2210
|
+
if (
|
|
2211
|
+
contentShare?.annotation &&
|
|
2212
|
+
!isEqual(contentShare?.annotation, previousContentShare?.annotation)
|
|
2213
|
+
) {
|
|
2214
|
+
Trigger.trigger(
|
|
2215
|
+
this,
|
|
2216
|
+
{
|
|
2217
|
+
file: 'meeting/index',
|
|
2218
|
+
function: 'triggerAnnotationInfoEvent',
|
|
2219
|
+
},
|
|
2220
|
+
EVENT_TRIGGERS.MEETING_UPDATE_ANNOTATION_INFO,
|
|
2221
|
+
contentShare.annotation
|
|
2222
|
+
);
|
|
2223
|
+
}
|
|
2224
|
+
}
|
|
2225
|
+
|
|
2203
2226
|
/**
|
|
2204
2227
|
* Set up the locus info media shares listener
|
|
2205
2228
|
* update content and whiteboard sharing id value for members, and updates the member
|
|
@@ -2215,17 +2238,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
2215
2238
|
const previousContentShare = payload.previous?.content;
|
|
2216
2239
|
const previousWhiteboardShare = payload.previous?.whiteboard;
|
|
2217
2240
|
|
|
2218
|
-
|
|
2219
|
-
Trigger.trigger(
|
|
2220
|
-
this,
|
|
2221
|
-
{
|
|
2222
|
-
file: 'meetings/index',
|
|
2223
|
-
function: 'remoteShare',
|
|
2224
|
-
},
|
|
2225
|
-
EVENT_TRIGGERS.MEETING_UPDATE_ANNOTATION_INFO,
|
|
2226
|
-
contentShare.annotation
|
|
2227
|
-
);
|
|
2228
|
-
}
|
|
2241
|
+
this.triggerAnnotationInfoEvent(contentShare, previousContentShare);
|
|
2229
2242
|
|
|
2230
2243
|
if (
|
|
2231
2244
|
contentShare.beneficiaryId === previousContentShare?.beneficiaryId &&
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/* eslint-disable valid-jsdoc */
|
|
2
2
|
/* eslint-disable require-jsdoc */
|
|
3
3
|
/* eslint-disable import/prefer-default-export */
|
|
4
|
+
import {forEach} from 'lodash';
|
|
4
5
|
import LoggerProxy from '../common/logs/logger-proxy';
|
|
5
6
|
|
|
6
7
|
import {getMaxFs, RemoteMedia, RemoteVideoResolution} from './remoteMedia';
|
|
@@ -66,6 +67,53 @@ export class RemoteMediaGroup {
|
|
|
66
67
|
return [...this.unpinnedRemoteMedia, ...this.pinnedRemoteMedia];
|
|
67
68
|
}
|
|
68
69
|
|
|
70
|
+
/**
|
|
71
|
+
* Sets CSIs for multiple RemoteMedia instances belonging to this RemoteMediaGroup.
|
|
72
|
+
* For each entry in the remoteMediaCsis array:
|
|
73
|
+
* - if csi is specified, the RemoteMedia instance is pinned to that CSI
|
|
74
|
+
* - if csi is undefined, the RemoteMedia instance is unpinned
|
|
75
|
+
* @internal
|
|
76
|
+
*/
|
|
77
|
+
public setActiveSpeakerCsis(
|
|
78
|
+
remoteMediaCsis: {remoteMedia: RemoteMedia; csi?: number}[],
|
|
79
|
+
commit = true
|
|
80
|
+
): void {
|
|
81
|
+
forEach(remoteMediaCsis, ({csi, remoteMedia}) => {
|
|
82
|
+
if (csi) {
|
|
83
|
+
if (!(this.pinnedRemoteMedia.indexOf(remoteMedia) >= 0)) {
|
|
84
|
+
const unpinId = this.unpinnedRemoteMedia.indexOf(remoteMedia);
|
|
85
|
+
if (unpinId >= 0) {
|
|
86
|
+
this.unpinnedRemoteMedia.splice(unpinId, 1);
|
|
87
|
+
this.pinnedRemoteMedia.push(remoteMedia);
|
|
88
|
+
} else {
|
|
89
|
+
throw new Error(
|
|
90
|
+
`failed to pin a remote media object ${remoteMedia.id}, because it is not found in this remote media group`
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
remoteMedia.sendMediaRequest(csi, false);
|
|
95
|
+
} else {
|
|
96
|
+
if (!(this.unpinnedRemoteMedia.indexOf(remoteMedia) >= 0)) {
|
|
97
|
+
const pinId = this.pinnedRemoteMedia.indexOf(remoteMedia);
|
|
98
|
+
if (pinId >= 0) {
|
|
99
|
+
this.pinnedRemoteMedia.splice(pinId, 1);
|
|
100
|
+
this.unpinnedRemoteMedia.push(remoteMedia);
|
|
101
|
+
} else {
|
|
102
|
+
throw new Error(
|
|
103
|
+
`failed to unpin a remote media object ${remoteMedia.id}, because it is not found in this remote media group`
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
remoteMedia.cancelMediaRequest(false);
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
this.cancelActiveSpeakerMediaRequest(false);
|
|
111
|
+
this.sendActiveSpeakerMediaRequest(false);
|
|
112
|
+
if (commit) {
|
|
113
|
+
this.mediaRequestManager.commit();
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
69
117
|
/**
|
|
70
118
|
* Pins a specific remote media instance to a specfic CSI, so the media will
|
|
71
119
|
* no longer come from active speaker, but from that CSI.
|
|
@@ -151,6 +199,10 @@ export class RemoteMediaGroup {
|
|
|
151
199
|
throw new Error(`remote media object ${remoteMedia.id} not found in the group`);
|
|
152
200
|
}
|
|
153
201
|
|
|
202
|
+
/**
|
|
203
|
+
* setPreferLiveVideo - sets preferLiveVideo to true/false
|
|
204
|
+
* @internal
|
|
205
|
+
*/
|
|
154
206
|
public setPreferLiveVideo(preferLiveVideo: boolean, commit: boolean) {
|
|
155
207
|
if (this.options.preferLiveVideo !== preferLiveVideo) {
|
|
156
208
|
this.options.preferLiveVideo = preferLiveVideo;
|
|
@@ -505,6 +505,24 @@ export class RemoteMediaManager extends EventsScope {
|
|
|
505
505
|
this.mediaRequestManagers.video.commit();
|
|
506
506
|
}
|
|
507
507
|
|
|
508
|
+
/**
|
|
509
|
+
* Sets CSIs for multiple RemoteMedia instances belonging to RemoteMediaGroup.
|
|
510
|
+
* For each entry in the remoteMediaCsis array:
|
|
511
|
+
* - if csi is specified, the RemoteMedia instance is pinned to that CSI
|
|
512
|
+
* - if csi is undefined, the RemoteMedia instance is unpinned
|
|
513
|
+
*/
|
|
514
|
+
public setActiveSpeakerCsis(remoteMediaCsis: {remoteMedia: RemoteMedia; csi?: number}[]) {
|
|
515
|
+
Object.values(this.media.video.activeSpeakerGroups).forEach((remoteMediaGroup) => {
|
|
516
|
+
const groupRemoteMediaCsis = remoteMediaCsis.filter(({remoteMedia}) =>
|
|
517
|
+
remoteMediaGroup.includes(remoteMedia)
|
|
518
|
+
);
|
|
519
|
+
if (groupRemoteMediaCsis.length > 0) {
|
|
520
|
+
remoteMediaGroup.setActiveSpeakerCsis(groupRemoteMediaCsis, false);
|
|
521
|
+
}
|
|
522
|
+
});
|
|
523
|
+
this.mediaRequestManagers.video.commit();
|
|
524
|
+
}
|
|
525
|
+
|
|
508
526
|
/**
|
|
509
527
|
* Creates the audio slots
|
|
510
528
|
*/
|
|
@@ -189,7 +189,7 @@ describe('live-annotation', () => {
|
|
|
189
189
|
});
|
|
190
190
|
|
|
191
191
|
|
|
192
|
-
describe('encrypt/decrypt Content
|
|
192
|
+
describe('encrypt/decrypt Content', () => {
|
|
193
193
|
beforeEach(async () => {
|
|
194
194
|
annotationService.webex.internal.encryption.encryptText = sinon.stub().returns(Promise.resolve('RETURN_VALUE'));
|
|
195
195
|
annotationService.webex.internal.encryption.decryptText = sinon.stub().returns(Promise.resolve('RETURN_VALUE'));
|
|
@@ -201,7 +201,7 @@ describe('live-annotation', () => {
|
|
|
201
201
|
assert.equal(result, 'RETURN_VALUE')
|
|
202
202
|
});
|
|
203
203
|
|
|
204
|
-
it('decryptContent
|
|
204
|
+
it('decryptContent', async() => {
|
|
205
205
|
const result = await annotationService.decryptContent("decryptionKeyUrl", "content");
|
|
206
206
|
assert.calledOnceWithExactly(webex.internal.encryption.decryptText, "decryptionKeyUrl", "content");
|
|
207
207
|
assert.equal(result, 'RETURN_VALUE')
|
|
@@ -232,7 +232,7 @@ describe('live-annotation', () => {
|
|
|
232
232
|
});
|
|
233
233
|
|
|
234
234
|
|
|
235
|
-
it('works on
|
|
235
|
+
it('works on publish Stroke Data', async () => {
|
|
236
236
|
const strokeData = {
|
|
237
237
|
content: {
|
|
238
238
|
"contentsBuffer": [{
|
|
@@ -343,7 +343,7 @@ describe('live-annotation', () => {
|
|
|
343
343
|
});
|
|
344
344
|
});
|
|
345
345
|
|
|
346
|
-
describe('change annotation
|
|
346
|
+
describe('change annotation info by presenter', () => {
|
|
347
347
|
it('makes change annotation options as expected', async() => {
|
|
348
348
|
const options = { annotationInfo:{
|
|
349
349
|
version: '1',
|
|
@@ -5489,6 +5489,50 @@ describe('plugin-meetings', () => {
|
|
|
5489
5489
|
});
|
|
5490
5490
|
});
|
|
5491
5491
|
describe('share scenarios', () => {
|
|
5492
|
+
|
|
5493
|
+
describe('triggerAnnotationInfoEvent', () => {
|
|
5494
|
+
it('check triggerAnnotationInfoEvent event', () => {
|
|
5495
|
+
|
|
5496
|
+
TriggerProxy.trigger.reset();
|
|
5497
|
+
const annotationInfo = {version: '1', policy: 'Approval'};
|
|
5498
|
+
meeting.triggerAnnotationInfoEvent({annotation:annotationInfo},{});
|
|
5499
|
+
|
|
5500
|
+
assert.calledWith(
|
|
5501
|
+
TriggerProxy.trigger,
|
|
5502
|
+
meeting,
|
|
5503
|
+
{
|
|
5504
|
+
file: 'meeting/index',
|
|
5505
|
+
function: 'triggerAnnotationInfoEvent',
|
|
5506
|
+
},
|
|
5507
|
+
'meeting:updateAnnotationInfo',
|
|
5508
|
+
annotationInfo
|
|
5509
|
+
);
|
|
5510
|
+
|
|
5511
|
+
TriggerProxy.trigger.reset();
|
|
5512
|
+
meeting.triggerAnnotationInfoEvent({annotation:annotationInfo},{annotation:annotationInfo});
|
|
5513
|
+
assert.notCalled(TriggerProxy.trigger);
|
|
5514
|
+
|
|
5515
|
+
TriggerProxy.trigger.reset();
|
|
5516
|
+
const annotationInfoUpdated = {version: '1', policy: 'AnnotationNotAllowed'};
|
|
5517
|
+
meeting.triggerAnnotationInfoEvent({annotation:annotationInfoUpdated},{annotation:annotationInfo});
|
|
5518
|
+
assert.calledWith(
|
|
5519
|
+
TriggerProxy.trigger,
|
|
5520
|
+
meeting,
|
|
5521
|
+
{
|
|
5522
|
+
file: 'meeting/index',
|
|
5523
|
+
function: 'triggerAnnotationInfoEvent',
|
|
5524
|
+
},
|
|
5525
|
+
'meeting:updateAnnotationInfo',
|
|
5526
|
+
annotationInfoUpdated
|
|
5527
|
+
);
|
|
5528
|
+
|
|
5529
|
+
TriggerProxy.trigger.reset();
|
|
5530
|
+
meeting.triggerAnnotationInfoEvent(null,{annotation:annotationInfoUpdated});
|
|
5531
|
+
assert.notCalled(TriggerProxy.trigger);
|
|
5532
|
+
|
|
5533
|
+
});
|
|
5534
|
+
});
|
|
5535
|
+
|
|
5492
5536
|
describe('setUpLocusMediaSharesListener', () => {
|
|
5493
5537
|
beforeEach(() => {
|
|
5494
5538
|
meeting.selfId = '9528d952-e4de-46cf-8157-fd4823b98377';
|
|
@@ -6179,29 +6223,6 @@ describe('plugin-meetings', () => {
|
|
|
6179
6223
|
payloadTestHelper([data1, data2, data3]);
|
|
6180
6224
|
});
|
|
6181
6225
|
});
|
|
6182
|
-
|
|
6183
|
-
describe('annotation policy', () => {
|
|
6184
|
-
|
|
6185
|
-
it('Scenario #1: blank annotation', () => {
|
|
6186
|
-
const data1 = generateData(blankPayload, true, true, USER_IDS.ME);
|
|
6187
|
-
const data2 = generateData(data1.payload, false, true, USER_IDS.ME);
|
|
6188
|
-
const data3 = generateData(data2.payload, true, true, USER_IDS.ME);
|
|
6189
|
-
const data4 = generateData(data3.payload, false, true, USER_IDS.ME);
|
|
6190
|
-
|
|
6191
|
-
payloadTestHelper([data1, data2, data3, data4]);
|
|
6192
|
-
});
|
|
6193
|
-
|
|
6194
|
-
it('Scenario #2: annotation', () => {
|
|
6195
|
-
const annotationInfo = {version: '1', policy: 'Approval'};
|
|
6196
|
-
const data1 = generateData(blankPayload, true, true, USER_IDS.ME, annotationInfo);
|
|
6197
|
-
const data2 = generateData(data1.payload, false, true, USER_IDS.ME);
|
|
6198
|
-
const data3 = generateData(data2.payload, true, true, USER_IDS.ME);
|
|
6199
|
-
const data4 = generateData(data3.payload, false, true, USER_IDS.ME);
|
|
6200
|
-
|
|
6201
|
-
payloadTestHelper([data1, data2, data3, data4]);
|
|
6202
|
-
});
|
|
6203
|
-
});
|
|
6204
|
-
|
|
6205
6226
|
describe('Desktop A --> Desktop B', () => {
|
|
6206
6227
|
it('Scenario #1: you share desktop A and then share desktop B', () => {
|
|
6207
6228
|
const data1 = generateData(blankPayload, true, true, USER_IDS.ME);
|
|
@@ -144,6 +144,225 @@ describe('RemoteMediaGroup', () => {
|
|
|
144
144
|
|
|
145
145
|
});
|
|
146
146
|
|
|
147
|
+
describe('setActiveSpeakerCsis', () => {
|
|
148
|
+
it('checks when there is a csi and remote media is not in pinned array', () => {
|
|
149
|
+
const PINNED_INDEX = 2;
|
|
150
|
+
const CSI = 11111;
|
|
151
|
+
|
|
152
|
+
const group = new RemoteMediaGroup(fakeMediaRequestManager, fakeReceiveSlots, 255, true, {
|
|
153
|
+
resolution: 'medium',
|
|
154
|
+
preferLiveVideo: true,
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
// initially nothing should be pinned
|
|
158
|
+
assert.strictEqual(group.getRemoteMedia().length, NUM_SLOTS); // by default should return 'all'
|
|
159
|
+
assert.strictEqual(group.getRemoteMedia('all').length, NUM_SLOTS);
|
|
160
|
+
assert.strictEqual(group.getRemoteMedia('unpinned').length, NUM_SLOTS);
|
|
161
|
+
assert.strictEqual(group.getRemoteMedia('pinned').length, 0);
|
|
162
|
+
|
|
163
|
+
const remoteMedia = group.getRemoteMedia('all')[PINNED_INDEX];
|
|
164
|
+
|
|
165
|
+
resetHistory();
|
|
166
|
+
|
|
167
|
+
group.setActiveSpeakerCsis([{remoteMedia, csi: CSI}], false);
|
|
168
|
+
|
|
169
|
+
assert.strictEqual(group.getRemoteMedia().length, NUM_SLOTS); // by default should return 'all'
|
|
170
|
+
assert.strictEqual(group.getRemoteMedia('all').length, NUM_SLOTS);
|
|
171
|
+
assert.strictEqual(group.getRemoteMedia('unpinned').length, NUM_SLOTS - 1);
|
|
172
|
+
assert.strictEqual(group.getRemoteMedia('pinned').length, 1);
|
|
173
|
+
|
|
174
|
+
assert.strictEqual(group.isPinned(remoteMedia), true);
|
|
175
|
+
// now check that correct media requests were sent...
|
|
176
|
+
|
|
177
|
+
const expectedReceiverSelectedSlots = [fakeReceiveSlots[PINNED_INDEX]];
|
|
178
|
+
const expectedActiveSpeakerReceiveSlots = fakeReceiveSlots.filter(
|
|
179
|
+
(_, idx) => idx !== PINNED_INDEX
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
// the previous active speaker media request for the group should have been cancelled
|
|
183
|
+
assert.calledOnce(fakeMediaRequestManager.cancelRequest);
|
|
184
|
+
assert.calledWith(fakeMediaRequestManager.cancelRequest, 'fake active speaker request 1');
|
|
185
|
+
// a new one should be sent for active speaker and for receiver selected
|
|
186
|
+
assert.calledTwice(fakeMediaRequestManager.addRequest);
|
|
187
|
+
assert.calledWith(
|
|
188
|
+
fakeMediaRequestManager.addRequest,
|
|
189
|
+
sinon.match({
|
|
190
|
+
policyInfo: sinon.match({
|
|
191
|
+
policy: 'active-speaker',
|
|
192
|
+
priority: 255,
|
|
193
|
+
}),
|
|
194
|
+
receiveSlots: expectedActiveSpeakerReceiveSlots,
|
|
195
|
+
codecInfo: sinon.match({
|
|
196
|
+
codec: 'h264',
|
|
197
|
+
maxFs: 3600,
|
|
198
|
+
}),
|
|
199
|
+
})
|
|
200
|
+
);
|
|
201
|
+
assert.calledWith(
|
|
202
|
+
fakeMediaRequestManager.addRequest,
|
|
203
|
+
sinon.match({
|
|
204
|
+
policyInfo: sinon.match({
|
|
205
|
+
policy: 'receiver-selected',
|
|
206
|
+
csi: CSI,
|
|
207
|
+
}),
|
|
208
|
+
receiveSlots: expectedReceiverSelectedSlots,
|
|
209
|
+
codecInfo: sinon.match({
|
|
210
|
+
codec: 'h264',
|
|
211
|
+
maxFs: 3600,
|
|
212
|
+
}),
|
|
213
|
+
})
|
|
214
|
+
);
|
|
215
|
+
assert.notCalled(fakeMediaRequestManager.commit);
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
it('checks when there is csi and remoteMedia is in pinned array', () => {
|
|
219
|
+
const PINNED_INDEX = 4;
|
|
220
|
+
|
|
221
|
+
const group = new RemoteMediaGroup(fakeMediaRequestManager, fakeReceiveSlots, 255, true, {
|
|
222
|
+
resolution: 'medium',
|
|
223
|
+
preferLiveVideo: true,
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
// take one instance of remote media from the group
|
|
227
|
+
const remoteMedia = group.getRemoteMedia('all')[PINNED_INDEX];
|
|
228
|
+
|
|
229
|
+
resetHistory();
|
|
230
|
+
|
|
231
|
+
// pin it so that it is in pinned array
|
|
232
|
+
group.setActiveSpeakerCsis([{remoteMedia, csi: 1234}], false);
|
|
233
|
+
|
|
234
|
+
assert.strictEqual(group.getRemoteMedia().length, NUM_SLOTS); // by default should return 'all'
|
|
235
|
+
assert.strictEqual(group.getRemoteMedia('all').length, NUM_SLOTS);
|
|
236
|
+
assert.strictEqual(group.getRemoteMedia('unpinned').length, NUM_SLOTS - 1);
|
|
237
|
+
assert.strictEqual(group.getRemoteMedia('pinned').length, 1);
|
|
238
|
+
|
|
239
|
+
resetHistory();
|
|
240
|
+
// normally this would result in the underlying receive slot csi to be updated, because we're using fake
|
|
241
|
+
// receive slots, we have to do that manually:
|
|
242
|
+
fakeReceiveSlots[PINNED_INDEX].csi = 1234;
|
|
243
|
+
const expectedReceiverSelectedSlots = [fakeReceiveSlots[PINNED_INDEX]];
|
|
244
|
+
|
|
245
|
+
// pin again to same CSI
|
|
246
|
+
group.setActiveSpeakerCsis([{remoteMedia, csi: 1234}], false);
|
|
247
|
+
|
|
248
|
+
assert.strictEqual(group.getRemoteMedia().length, NUM_SLOTS); // by default should return 'all'
|
|
249
|
+
assert.strictEqual(group.getRemoteMedia('all').length, NUM_SLOTS);
|
|
250
|
+
assert.strictEqual(group.getRemoteMedia('unpinned').length, NUM_SLOTS - 1);
|
|
251
|
+
assert.strictEqual(group.getRemoteMedia('pinned').length, 1);
|
|
252
|
+
|
|
253
|
+
assert.strictEqual(group.isPinned(remoteMedia), true);
|
|
254
|
+
|
|
255
|
+
assert.calledTwice(fakeMediaRequestManager.cancelRequest);
|
|
256
|
+
assert.calledWith(fakeMediaRequestManager.cancelRequest, 'fake receiver selected request 1');
|
|
257
|
+
|
|
258
|
+
assert.calledWith(
|
|
259
|
+
fakeMediaRequestManager.addRequest,
|
|
260
|
+
sinon.match({
|
|
261
|
+
policyInfo: sinon.match({
|
|
262
|
+
policy: 'receiver-selected',
|
|
263
|
+
csi: 1234,
|
|
264
|
+
}),
|
|
265
|
+
receiveSlots: expectedReceiverSelectedSlots,
|
|
266
|
+
codecInfo: sinon.match({
|
|
267
|
+
codec: 'h264',
|
|
268
|
+
maxFs: 3600,
|
|
269
|
+
}),
|
|
270
|
+
})
|
|
271
|
+
);
|
|
272
|
+
assert.notCalled(fakeMediaRequestManager.commit);
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
it('checks setActiveSpeakerCsis with array of remoteMedia to pin and unpin', () => {
|
|
276
|
+
const PINNED_INDEX = 2;
|
|
277
|
+
const PINNED_INDEX2 = 0;
|
|
278
|
+
const CSI = 11111;
|
|
279
|
+
const CSI2 = 12345;
|
|
280
|
+
|
|
281
|
+
const group = new RemoteMediaGroup(fakeMediaRequestManager, fakeReceiveSlots, 255, true, {
|
|
282
|
+
resolution: 'medium',
|
|
283
|
+
preferLiveVideo: true,
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
// initially nothing should be pinned
|
|
287
|
+
assert.strictEqual(group.getRemoteMedia().length, NUM_SLOTS); // by default should return 'all'
|
|
288
|
+
assert.strictEqual(group.getRemoteMedia('all').length, NUM_SLOTS);
|
|
289
|
+
assert.strictEqual(group.getRemoteMedia('unpinned').length, NUM_SLOTS);
|
|
290
|
+
assert.strictEqual(group.getRemoteMedia('pinned').length, 0);
|
|
291
|
+
|
|
292
|
+
const remoteMedia = group.getRemoteMedia('all')[PINNED_INDEX];
|
|
293
|
+
|
|
294
|
+
const remoteMedia2 = group.getRemoteMedia('all')[PINNED_INDEX2];
|
|
295
|
+
|
|
296
|
+
const remoteMedisCsis = [{remoteMedia, csi: CSI}, {remoteMedia: remoteMedia2, csi: CSI2}];
|
|
297
|
+
|
|
298
|
+
group.setActiveSpeakerCsis(remoteMedisCsis, false);
|
|
299
|
+
|
|
300
|
+
assert.strictEqual(group.getRemoteMedia().length, NUM_SLOTS);
|
|
301
|
+
assert.strictEqual(group.getRemoteMedia('all').length, NUM_SLOTS);
|
|
302
|
+
assert.strictEqual(group.getRemoteMedia('unpinned').length, NUM_SLOTS - 2);
|
|
303
|
+
assert.strictEqual(group.getRemoteMedia('pinned').length, 2);
|
|
304
|
+
|
|
305
|
+
assert.strictEqual(group.isPinned(remoteMedia), true);
|
|
306
|
+
assert.strictEqual(group.isPinned(remoteMedia2), true);
|
|
307
|
+
|
|
308
|
+
resetHistory();
|
|
309
|
+
|
|
310
|
+
group.setActiveSpeakerCsis([{remoteMedia}], false);
|
|
311
|
+
|
|
312
|
+
// one pane should still remain pinned
|
|
313
|
+
assert.strictEqual(group.getRemoteMedia().length, NUM_SLOTS);
|
|
314
|
+
assert.strictEqual(group.getRemoteMedia('all').length, NUM_SLOTS);
|
|
315
|
+
assert.strictEqual(group.getRemoteMedia('unpinned').length, NUM_SLOTS - 1);
|
|
316
|
+
assert.strictEqual(group.getRemoteMedia('pinned').length, 1);
|
|
317
|
+
assert.strictEqual(group.isPinned(remoteMedia), false);
|
|
318
|
+
assert.strictEqual(group.isPinned(remoteMedia2), true);
|
|
319
|
+
|
|
320
|
+
assert.calledTwice(fakeMediaRequestManager.cancelRequest);
|
|
321
|
+
assert.calledWith(fakeMediaRequestManager.cancelRequest, 'fake receiver selected request 1');
|
|
322
|
+
assert.notCalled(fakeMediaRequestManager.commit);
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
it('check commit is only called once', () => {
|
|
326
|
+
const PINNED_INDEX = 2;
|
|
327
|
+
const PINNED_INDEX2 = 0;
|
|
328
|
+
const CSI = 11111;
|
|
329
|
+
const CSI2 = 12345;
|
|
330
|
+
|
|
331
|
+
const group = new RemoteMediaGroup(fakeMediaRequestManager, fakeReceiveSlots, 255, true, {
|
|
332
|
+
resolution: 'medium',
|
|
333
|
+
preferLiveVideo: true,
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
const remoteMedia = group.getRemoteMedia('all')[PINNED_INDEX];
|
|
337
|
+
|
|
338
|
+
resetHistory();
|
|
339
|
+
|
|
340
|
+
const remoteMedia2 = group.getRemoteMedia('all')[PINNED_INDEX2];
|
|
341
|
+
|
|
342
|
+
const remoteMedisCsis = [{remoteMedia, csi: CSI}, {remoteMedia: remoteMedia2, csi: CSI2}, {remoteMedia}];
|
|
343
|
+
|
|
344
|
+
group.setActiveSpeakerCsis(remoteMedisCsis, true);
|
|
345
|
+
|
|
346
|
+
assert.calledOnce(fakeMediaRequestManager.commit);
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
it('throws when remoteMedia id is not in unpinned and pinned array - csi is there', () => {
|
|
350
|
+
const group = new RemoteMediaGroup(fakeMediaRequestManager, fakeReceiveSlots, 255, true, {
|
|
351
|
+
resolution: 'medium',
|
|
352
|
+
preferLiveVideo: true,
|
|
353
|
+
});
|
|
354
|
+
assert.throws(() => group.setActiveSpeakerCsis([{remoteMedia: {id: 'r1'} as any, csi: 123}], false), 'failed to pin a remote media object r1, because it is not found in this remote media group');
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
it('throws when remoteMedia id is not in unpinned and pinned array - csi is not there', () => {
|
|
358
|
+
const group = new RemoteMediaGroup(fakeMediaRequestManager, fakeReceiveSlots, 255, true, {
|
|
359
|
+
resolution: 'medium',
|
|
360
|
+
preferLiveVideo: true,
|
|
361
|
+
});
|
|
362
|
+
assert.throws(() => group.setActiveSpeakerCsis([{remoteMedia: {id: 'r1'} as any}], false), 'failed to unpin a remote media object r1, because it is not found in this remote media group');
|
|
363
|
+
});
|
|
364
|
+
});
|
|
365
|
+
|
|
147
366
|
describe('pinning', () => {
|
|
148
367
|
it('works as expected', () => {
|
|
149
368
|
const PINNED_INDEX = 2;
|
|
@@ -679,7 +679,6 @@ describe('RemoteMediaManager', () => {
|
|
|
679
679
|
);
|
|
680
680
|
|
|
681
681
|
remoteMediaManager.on(Event.VideoLayoutChanged, (layoutInfo: VideoLayoutChangedEventData) => {
|
|
682
|
-
console.log(layoutInfo.activeSpeakerVideoPanes);
|
|
683
682
|
Object.values(layoutInfo.activeSpeakerVideoPanes).forEach((group) => stubs.push(sinon.stub(group, 'setPreferLiveVideo')));
|
|
684
683
|
});
|
|
685
684
|
|
|
@@ -1725,6 +1724,94 @@ describe('RemoteMediaManager', () => {
|
|
|
1725
1724
|
});
|
|
1726
1725
|
});
|
|
1727
1726
|
|
|
1727
|
+
describe('setActiveSpeakerCsis', () => {
|
|
1728
|
+
it('calls setActiveSpeakerCsis on the correct remote media group', async () => {
|
|
1729
|
+
let currentLayoutInfo: VideoLayoutChangedEventData | null = null;
|
|
1730
|
+
let setCsisStub;
|
|
1731
|
+
|
|
1732
|
+
remoteMediaManager.on(Event.VideoLayoutChanged, (layoutInfo: VideoLayoutChangedEventData) => {
|
|
1733
|
+
currentLayoutInfo = layoutInfo;
|
|
1734
|
+
setCsisStub = sinon.stub(layoutInfo.activeSpeakerVideoPanes.main, 'setActiveSpeakerCsis');
|
|
1735
|
+
});
|
|
1736
|
+
|
|
1737
|
+
await remoteMediaManager.start();
|
|
1738
|
+
resetHistory();
|
|
1739
|
+
|
|
1740
|
+
assert.isNotNull(currentLayoutInfo);
|
|
1741
|
+
|
|
1742
|
+
if (currentLayoutInfo) {
|
|
1743
|
+
const remoteVideo = currentLayoutInfo.activeSpeakerVideoPanes.main.getRemoteMedia()[0];
|
|
1744
|
+
|
|
1745
|
+
remoteMediaManager.setActiveSpeakerCsis([{remoteMedia: remoteVideo}]);
|
|
1746
|
+
|
|
1747
|
+
assert.calledOnce(setCsisStub);
|
|
1748
|
+
assert.calledWith(setCsisStub, [{remoteMedia: remoteVideo}], false);
|
|
1749
|
+
assert.calledOnce(fakeMediaRequestManagers.video.commit);
|
|
1750
|
+
}
|
|
1751
|
+
});
|
|
1752
|
+
|
|
1753
|
+
it('does not call setActiveSpeakerCsis on the incorrect media group', async () => {
|
|
1754
|
+
let currentLayoutInfo: VideoLayoutChangedEventData | null = null;
|
|
1755
|
+
let setCsisStub;
|
|
1756
|
+
|
|
1757
|
+
remoteMediaManager.on(Event.VideoLayoutChanged, (layoutInfo: VideoLayoutChangedEventData) => {
|
|
1758
|
+
currentLayoutInfo = layoutInfo;
|
|
1759
|
+
setCsisStub = sinon.stub(layoutInfo.activeSpeakerVideoPanes.main, 'setActiveSpeakerCsis');
|
|
1760
|
+
});
|
|
1761
|
+
|
|
1762
|
+
await remoteMediaManager.start();
|
|
1763
|
+
resetHistory();
|
|
1764
|
+
|
|
1765
|
+
assert.isNotNull(currentLayoutInfo);
|
|
1766
|
+
|
|
1767
|
+
if (currentLayoutInfo) {
|
|
1768
|
+
remoteMediaManager.setActiveSpeakerCsis([{remoteMedia: {}}]);
|
|
1769
|
+
|
|
1770
|
+
assert.notCalled(setCsisStub);
|
|
1771
|
+
assert.calledOnce(fakeMediaRequestManagers.video.commit);
|
|
1772
|
+
}
|
|
1773
|
+
});
|
|
1774
|
+
|
|
1775
|
+
it('checking when there is more than one group', async () => {
|
|
1776
|
+
let currentLayoutInfo: VideoLayoutChangedEventData | null = null;
|
|
1777
|
+
const config = cloneDeep(DefaultTestConfiguration);
|
|
1778
|
+
let stubs = [];
|
|
1779
|
+
|
|
1780
|
+
config.video.initialLayoutId = 'OnePlusFive';
|
|
1781
|
+
|
|
1782
|
+
remoteMediaManager = new RemoteMediaManager(
|
|
1783
|
+
fakeReceiveSlotManager,
|
|
1784
|
+
fakeMediaRequestManagers,
|
|
1785
|
+
config
|
|
1786
|
+
);
|
|
1787
|
+
|
|
1788
|
+
remoteMediaManager.on(Event.VideoLayoutChanged, (layoutInfo: VideoLayoutChangedEventData) => {
|
|
1789
|
+
currentLayoutInfo = layoutInfo;
|
|
1790
|
+
Object.values(layoutInfo.activeSpeakerVideoPanes).forEach((group) => stubs.push(sinon.stub(group, 'setActiveSpeakerCsis')));
|
|
1791
|
+
});
|
|
1792
|
+
|
|
1793
|
+
await remoteMediaManager.start();
|
|
1794
|
+
resetHistory();
|
|
1795
|
+
|
|
1796
|
+
assert.isNotNull(currentLayoutInfo);
|
|
1797
|
+
|
|
1798
|
+
if (currentLayoutInfo) {
|
|
1799
|
+
|
|
1800
|
+
const remoteMedia1 = currentLayoutInfo.activeSpeakerVideoPanes.mainBigOne.getRemoteMedia()[0];
|
|
1801
|
+
const remoteMedia2 = currentLayoutInfo.activeSpeakerVideoPanes.secondarySetOfSmallPanes.getRemoteMedia()[0];
|
|
1802
|
+
|
|
1803
|
+
const remoteMediaCsis = [{remoteMedia: remoteMedia1}, {remoteMedia: remoteMedia2}];
|
|
1804
|
+
|
|
1805
|
+
remoteMediaManager.setActiveSpeakerCsis([{remoteMedia: remoteMedia1}, {remoteMedia: remoteMedia2}]);
|
|
1806
|
+
|
|
1807
|
+
stubs.forEach((stub, index) => {
|
|
1808
|
+
assert.calledWith(stub, [remoteMediaCsis[index]], false)
|
|
1809
|
+
});
|
|
1810
|
+
assert.calledOnce(fakeMediaRequestManagers.video.commit);
|
|
1811
|
+
}
|
|
1812
|
+
});
|
|
1813
|
+
});
|
|
1814
|
+
|
|
1728
1815
|
describe('pinActiveSpeakerVideoPane() and isPinned()', () => {
|
|
1729
1816
|
it('throws if called on a pane not belonging to an active speaker group', async () => {
|
|
1730
1817
|
let currentLayoutInfo: VideoLayoutChangedEventData | null = null;
|