@webex/plugin-meetings 3.0.0-beta.1 → 3.0.0-beta.2

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.
Files changed (111) hide show
  1. package/dist/common/errors/webex-errors.js +5 -29
  2. package/dist/common/errors/webex-errors.js.map +1 -1
  3. package/dist/constants.js +15 -74
  4. package/dist/constants.js.map +1 -1
  5. package/dist/media/index.js +68 -213
  6. package/dist/media/index.js.map +1 -1
  7. package/dist/media/internal-media-core-wrapper.js +22 -0
  8. package/dist/media/internal-media-core-wrapper.js.map +1 -0
  9. package/dist/media/properties.js +20 -25
  10. package/dist/media/properties.js.map +1 -1
  11. package/dist/media/util.js +0 -27
  12. package/dist/media/util.js.map +1 -1
  13. package/dist/meeting/index.js +694 -432
  14. package/dist/meeting/index.js.map +1 -1
  15. package/dist/meeting/request.js +1 -0
  16. package/dist/meeting/request.js.map +1 -1
  17. package/dist/meeting/util.js +3 -44
  18. package/dist/meeting/util.js.map +1 -1
  19. package/dist/meetings/index.js +64 -5
  20. package/dist/meetings/index.js.map +1 -1
  21. package/dist/meetings/util.js +24 -1
  22. package/dist/meetings/util.js.map +1 -1
  23. package/dist/members/index.js +68 -0
  24. package/dist/members/index.js.map +1 -1
  25. package/dist/multistream/mediaRequestManager.js +132 -0
  26. package/dist/multistream/mediaRequestManager.js.map +1 -0
  27. package/dist/multistream/multistreamMedia.js +116 -0
  28. package/dist/multistream/multistreamMedia.js.map +1 -0
  29. package/dist/multistream/receiveSlot.js +209 -0
  30. package/dist/multistream/receiveSlot.js.map +1 -0
  31. package/dist/multistream/receiveSlotManager.js +195 -0
  32. package/dist/multistream/receiveSlotManager.js.map +1 -0
  33. package/dist/multistream/remoteMedia.js +284 -0
  34. package/dist/multistream/remoteMedia.js.map +1 -0
  35. package/dist/multistream/remoteMediaGroup.js +243 -0
  36. package/dist/multistream/remoteMediaGroup.js.map +1 -0
  37. package/dist/multistream/remoteMediaManager.js +1113 -0
  38. package/dist/multistream/remoteMediaManager.js.map +1 -0
  39. package/dist/reconnection-manager/index.js +109 -130
  40. package/dist/reconnection-manager/index.js.map +1 -1
  41. package/dist/roap/index.js +57 -240
  42. package/dist/roap/index.js.map +1 -1
  43. package/dist/roap/request.js +2 -114
  44. package/dist/roap/request.js.map +1 -1
  45. package/dist/roap/turnDiscovery.js +11 -5
  46. package/dist/roap/turnDiscovery.js.map +1 -1
  47. package/dist/statsAnalyzer/global.js +2 -0
  48. package/dist/statsAnalyzer/global.js.map +1 -1
  49. package/dist/statsAnalyzer/index.js +39 -36
  50. package/dist/statsAnalyzer/index.js.map +1 -1
  51. package/package.json +20 -19
  52. package/src/common/errors/webex-errors.js +0 -18
  53. package/src/constants.ts +139 -180
  54. package/src/media/index.js +60 -194
  55. package/src/media/internal-media-core-wrapper.ts +9 -0
  56. package/src/media/properties.js +19 -25
  57. package/src/media/util.js +0 -22
  58. package/src/meeting/index.js +565 -320
  59. package/src/meeting/request.js +1 -0
  60. package/src/meeting/util.js +3 -46
  61. package/src/meetings/index.js +30 -1
  62. package/src/meetings/util.js +23 -2
  63. package/src/members/index.js +48 -0
  64. package/src/multistream/mediaRequestManager.ts +164 -0
  65. package/src/multistream/multistreamMedia.ts +92 -0
  66. package/src/multistream/receiveSlot.ts +141 -0
  67. package/src/multistream/receiveSlotManager.ts +142 -0
  68. package/src/multistream/remoteMedia.ts +219 -0
  69. package/src/multistream/remoteMediaGroup.ts +224 -0
  70. package/src/multistream/remoteMediaManager.ts +911 -0
  71. package/src/reconnection-manager/index.js +40 -53
  72. package/src/roap/index.js +47 -207
  73. package/src/roap/request.js +1 -72
  74. package/src/roap/turnDiscovery.ts +12 -6
  75. package/src/statsAnalyzer/global.js +2 -0
  76. package/src/statsAnalyzer/index.js +32 -46
  77. package/test/integration/spec/journey.js +1 -1
  78. package/test/unit/spec/media/index.ts +223 -0
  79. package/test/unit/spec/media/properties.ts +73 -82
  80. package/test/unit/spec/meeting/effectsState.js +1 -3
  81. package/test/unit/spec/meeting/index.js +420 -228
  82. package/test/unit/spec/meeting/muteState.js +7 -0
  83. package/test/unit/spec/meeting/utils.js +61 -2
  84. package/test/unit/spec/meetings/index.js +0 -4
  85. package/test/unit/spec/members/index.js +164 -2
  86. package/test/unit/spec/multistream/mediaRequestManager.ts +511 -0
  87. package/test/unit/spec/multistream/receiveSlot.ts +104 -0
  88. package/test/unit/spec/multistream/receiveSlotManager.ts +173 -0
  89. package/test/unit/spec/multistream/remoteMedia.ts +217 -0
  90. package/test/unit/spec/multistream/remoteMediaGroup.ts +396 -0
  91. package/test/unit/spec/multistream/remoteMediaManager.ts +1251 -0
  92. package/test/unit/spec/roap/index.ts +63 -35
  93. package/test/unit/spec/stats-analyzer/index.js +19 -22
  94. package/dist/peer-connection-manager/index.js +0 -794
  95. package/dist/peer-connection-manager/index.js.map +0 -1
  96. package/dist/roap/collection.js +0 -73
  97. package/dist/roap/collection.js.map +0 -1
  98. package/dist/roap/handler.js +0 -337
  99. package/dist/roap/handler.js.map +0 -1
  100. package/dist/roap/state.js +0 -164
  101. package/dist/roap/state.js.map +0 -1
  102. package/dist/roap/util.js +0 -102
  103. package/dist/roap/util.js.map +0 -1
  104. package/src/peer-connection-manager/index.js +0 -723
  105. package/src/roap/collection.js +0 -63
  106. package/src/roap/handler.js +0 -252
  107. package/src/roap/state.js +0 -149
  108. package/src/roap/util.js +0 -93
  109. package/test/unit/spec/peerconnection-manager/index.js +0 -188
  110. package/test/unit/spec/peerconnection-manager/utils.js +0 -48
  111. package/test/unit/spec/roap/util.js +0 -30
@@ -0,0 +1,396 @@
1
+ import EventEmitter from 'events';
2
+
3
+ import {MediaConnection as MC} from '@webex/internal-media-core';
4
+ import {RemoteMediaGroup} from '@webex/plugin-meetings/src/multistream/remoteMediaGroup';
5
+ import {RemoteMedia} from '@webex/plugin-meetings/src/multistream/remoteMedia';
6
+ import {ReceiveSlot} from '@webex/plugin-meetings/src/multistream/receiveSlot';
7
+ import sinon from 'sinon';
8
+ import {assert} from '@webex/test-helper-chai';
9
+
10
+ class FakeSlot extends EventEmitter {
11
+ public mediaType: MC.MediaType;
12
+
13
+ public id: string;
14
+
15
+ constructor(mediaType: MC.MediaType, id: string) {
16
+ super();
17
+ this.mediaType = mediaType;
18
+ this.id = id;
19
+ }
20
+ }
21
+
22
+ describe('RemoteMediaGroup', () => {
23
+ const NUM_SLOTS = 10;
24
+
25
+ let fakeMediaRequestManager;
26
+ let fakeReceiveSlots;
27
+
28
+ let activeSpeakerRequestCounter;
29
+ let receiverSelectedRequestCounter;
30
+
31
+ beforeEach(() => {
32
+ activeSpeakerRequestCounter = 0;
33
+ receiverSelectedRequestCounter = 0;
34
+
35
+ fakeMediaRequestManager = {
36
+ addRequest: sinon.stub().callsFake((mediaRequest) => {
37
+ if (mediaRequest.policyInfo.policy === 'active-speaker') {
38
+ activeSpeakerRequestCounter += 1;
39
+
40
+ return `fake active speaker request ${activeSpeakerRequestCounter}`;
41
+ }
42
+ receiverSelectedRequestCounter += 1;
43
+
44
+ return `fake receiver selected request ${receiverSelectedRequestCounter}`;
45
+ }),
46
+ cancelRequest: sinon.stub(),
47
+ commit: sinon.stub(),
48
+ };
49
+
50
+ fakeReceiveSlots = Array(NUM_SLOTS)
51
+ .fill(null)
52
+ .map((_, index) => new FakeSlot(MC.MediaType.VideoMain, `fake receive slot ${index}`));
53
+ });
54
+
55
+ const getLastActiveSpeakerRequestId = () =>
56
+ `fake active speaker request ${activeSpeakerRequestCounter}`;
57
+
58
+ const resetHistory = () => {
59
+ fakeMediaRequestManager.addRequest.resetHistory();
60
+ fakeMediaRequestManager.cancelRequest.resetHistory();
61
+ fakeMediaRequestManager.commit.resetHistory();
62
+ };
63
+
64
+ describe('constructor', () => {
65
+ it('creates a list or RemoteMedia objects and sends the active speaker media request', () => {
66
+ const group = new RemoteMediaGroup(fakeMediaRequestManager, fakeReceiveSlots, 211, true, {
67
+ resolution: 'medium',
68
+ preferLiveVideo: true,
69
+ });
70
+
71
+ assert.strictEqual(group.getRemoteMedia().length, NUM_SLOTS);
72
+ assert.strictEqual(group.getRemoteMedia('all').length, NUM_SLOTS);
73
+ assert.strictEqual(group.getRemoteMedia('unpinned').length, NUM_SLOTS);
74
+ assert.strictEqual(group.getRemoteMedia('pinned').length, 0);
75
+
76
+ assert.strictEqual(
77
+ group.getRemoteMedia('all').every((item) => item instanceof RemoteMedia),
78
+ true
79
+ );
80
+
81
+ assert.calledOnce(fakeMediaRequestManager.addRequest);
82
+ assert.calledWith(
83
+ fakeMediaRequestManager.addRequest,
84
+ sinon.match({
85
+ policyInfo: sinon.match({
86
+ policy: 'active-speaker',
87
+ priority: 211,
88
+ }),
89
+ receiveSlots: fakeReceiveSlots,
90
+ codecInfo: sinon.match({
91
+ codec: 'h264',
92
+ maxFs: 3600,
93
+ }),
94
+ }),
95
+ true
96
+ );
97
+ });
98
+ });
99
+
100
+ describe('pinning', () => {
101
+ it('works as expected', () => {
102
+ const PINNED_INDEX = 2;
103
+ const PINNED_INDEX2 = 0;
104
+ const CSI = 11111;
105
+ const CSI2 = 12345;
106
+
107
+ const group = new RemoteMediaGroup(fakeMediaRequestManager, fakeReceiveSlots, 255, true, {
108
+ resolution: 'medium',
109
+ preferLiveVideo: true,
110
+ });
111
+
112
+ // initially nothing should be pinned
113
+ assert.strictEqual(group.getRemoteMedia().length, NUM_SLOTS); // by default should return 'all'
114
+ assert.strictEqual(group.getRemoteMedia('all').length, NUM_SLOTS);
115
+ assert.strictEqual(group.getRemoteMedia('unpinned').length, NUM_SLOTS);
116
+ assert.strictEqual(group.getRemoteMedia('pinned').length, 0);
117
+
118
+ // take one instance of remote media from the group
119
+ const remoteMedia = group.getRemoteMedia('all')[PINNED_INDEX];
120
+
121
+ resetHistory();
122
+
123
+ // pin it
124
+ group.pin(remoteMedia, CSI);
125
+
126
+ assert.strictEqual(group.getRemoteMedia().length, NUM_SLOTS); // by default should return 'all'
127
+ assert.strictEqual(group.getRemoteMedia('all').length, NUM_SLOTS);
128
+ assert.strictEqual(group.getRemoteMedia('unpinned').length, NUM_SLOTS - 1);
129
+ assert.strictEqual(group.getRemoteMedia('pinned').length, 1);
130
+
131
+ assert.strictEqual(group.isPinned(remoteMedia), true);
132
+
133
+ // now check that correct media requests were sent...
134
+
135
+ const expectedReceiverSelectedSlots = [fakeReceiveSlots[PINNED_INDEX]];
136
+ const expectedActiveSpeakerReceiveSlots = fakeReceiveSlots.filter(
137
+ (_, idx) => idx !== PINNED_INDEX
138
+ );
139
+
140
+ // the previous active speaker media request for the group should have been cancelled
141
+ assert.calledOnce(fakeMediaRequestManager.cancelRequest);
142
+ assert.calledWith(fakeMediaRequestManager.cancelRequest, 'fake active speaker request 1');
143
+ // a new one should be sent for active speaker and for receiver selected
144
+ assert.calledTwice(fakeMediaRequestManager.addRequest);
145
+ assert.calledWith(
146
+ fakeMediaRequestManager.addRequest,
147
+ sinon.match({
148
+ policyInfo: sinon.match({
149
+ policy: 'active-speaker',
150
+ priority: 255,
151
+ }),
152
+ receiveSlots: expectedActiveSpeakerReceiveSlots,
153
+ codecInfo: sinon.match({
154
+ codec: 'h264',
155
+ maxFs: 3600,
156
+ }),
157
+ })
158
+ );
159
+ assert.calledWith(
160
+ fakeMediaRequestManager.addRequest,
161
+ sinon.match({
162
+ policyInfo: sinon.match({
163
+ policy: 'receiver-selected',
164
+ csi: CSI,
165
+ }),
166
+ receiveSlots: expectedReceiverSelectedSlots,
167
+ codecInfo: sinon.match({
168
+ codec: 'h264',
169
+ maxFs: 3600,
170
+ }),
171
+ })
172
+ );
173
+
174
+ resetHistory();
175
+
176
+ // pin another video
177
+ const remoteMedia2 = group.getRemoteMedia('all')[PINNED_INDEX2];
178
+
179
+ group.pin(remoteMedia2, CSI2);
180
+
181
+ assert.strictEqual(group.getRemoteMedia().length, NUM_SLOTS);
182
+ assert.strictEqual(group.getRemoteMedia('all').length, NUM_SLOTS);
183
+ assert.strictEqual(group.getRemoteMedia('unpinned').length, NUM_SLOTS - 2);
184
+ assert.strictEqual(group.getRemoteMedia('pinned').length, 2);
185
+
186
+ assert.strictEqual(group.isPinned(remoteMedia2), true);
187
+
188
+ // now check that correct media requests were sent...
189
+ const expectedReceiverSelectedSlots2 = [fakeReceiveSlots[PINNED_INDEX2]];
190
+ const expectedActiveSpeakerReceiveSlots2 = fakeReceiveSlots.filter(
191
+ (_, idx) => idx !== PINNED_INDEX && idx !== PINNED_INDEX2
192
+ );
193
+
194
+ // the previous active speaker media request for the group should have been cancelled
195
+ assert.calledOnce(fakeMediaRequestManager.cancelRequest);
196
+ assert.calledWith(fakeMediaRequestManager.cancelRequest, 'fake active speaker request 2');
197
+ // a new one should be sent for active speaker and for receiver selected
198
+ assert.calledTwice(fakeMediaRequestManager.addRequest);
199
+ assert.calledWith(
200
+ fakeMediaRequestManager.addRequest,
201
+ sinon.match({
202
+ policyInfo: sinon.match({
203
+ policy: 'active-speaker',
204
+ priority: 255,
205
+ }),
206
+ receiveSlots: expectedActiveSpeakerReceiveSlots2,
207
+ codecInfo: sinon.match({
208
+ codec: 'h264',
209
+ maxFs: 3600,
210
+ }),
211
+ })
212
+ );
213
+ assert.calledWith(
214
+ fakeMediaRequestManager.addRequest,
215
+ sinon.match({
216
+ policyInfo: sinon.match({
217
+ policy: 'receiver-selected',
218
+ csi: CSI2,
219
+ }),
220
+ receiveSlots: expectedReceiverSelectedSlots2,
221
+ codecInfo: sinon.match({
222
+ codec: 'h264',
223
+ maxFs: 3600,
224
+ }),
225
+ })
226
+ );
227
+
228
+ resetHistory();
229
+
230
+ // now unpin the video pane that was first pinned
231
+ group.unpin(remoteMedia);
232
+
233
+ // one pane should still remain pinned
234
+ assert.strictEqual(group.getRemoteMedia().length, NUM_SLOTS);
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
+ assert.strictEqual(group.isPinned(remoteMedia), false);
240
+
241
+ // the previous requests for the group and the individual remote media should have been cancelled
242
+ assert.calledTwice(fakeMediaRequestManager.cancelRequest);
243
+ assert.calledWith(fakeMediaRequestManager.cancelRequest, 'fake active speaker request 3');
244
+ assert.calledWith(fakeMediaRequestManager.cancelRequest, 'fake receiver selected request 1');
245
+
246
+ // a new one should be sent for active speaker
247
+ assert.calledOnce(fakeMediaRequestManager.addRequest);
248
+ assert.calledWith(
249
+ fakeMediaRequestManager.addRequest,
250
+ sinon.match({
251
+ policyInfo: sinon.match({
252
+ policy: 'active-speaker',
253
+ priority: 255,
254
+ }),
255
+ codecInfo: sinon.match({
256
+ codec: 'h264',
257
+ maxFs: 3600,
258
+ }),
259
+ })
260
+ );
261
+ // checking that the receiveSlots array passed in to addRequest() has the right length without
262
+ // being strict on the order of elements in it:
263
+ const receiveSlotsArg = fakeMediaRequestManager.addRequest.getCall(0).args[0].receiveSlots;
264
+
265
+ assert.strictEqual(receiveSlotsArg.length, fakeReceiveSlots.length - 1);
266
+ });
267
+
268
+ it('works as expected when pin() is called on already pinned RemoteMedia', () => {
269
+ const PINNED_INDEX = 4;
270
+
271
+ const group = new RemoteMediaGroup(fakeMediaRequestManager, fakeReceiveSlots, 255, true, {
272
+ resolution: 'medium',
273
+ preferLiveVideo: true,
274
+ });
275
+
276
+ // take one instance of remote media from the group
277
+ const remoteMedia = group.getRemoteMedia('all')[PINNED_INDEX];
278
+
279
+ resetHistory();
280
+
281
+ // pin it
282
+ group.pin(remoteMedia, 1234);
283
+
284
+ resetHistory();
285
+ // normally this would result in the underlying receive slot csi to be updated, because we're using fake
286
+ // receive slots, we have to do that manually:
287
+ fakeReceiveSlots[PINNED_INDEX].csi = 1234;
288
+
289
+ // pin again to same CSI
290
+ group.pin(remoteMedia, 1234);
291
+
292
+ assert.notCalled(fakeMediaRequestManager.addRequest);
293
+ assert.notCalled(fakeMediaRequestManager.cancelRequest);
294
+ assert.notCalled(fakeMediaRequestManager.commit);
295
+
296
+ // again, this time without even specifying the csi
297
+ group.pin(remoteMedia);
298
+
299
+ assert.notCalled(fakeMediaRequestManager.addRequest);
300
+ assert.notCalled(fakeMediaRequestManager.cancelRequest);
301
+ assert.notCalled(fakeMediaRequestManager.commit);
302
+
303
+ // pin it again but to a different CSI
304
+ group.pin(remoteMedia, 2345);
305
+
306
+ // it should trigger a new receiver selected media request
307
+ assert.calledOnce(fakeMediaRequestManager.addRequest);
308
+ assert.calledWith(
309
+ fakeMediaRequestManager.addRequest,
310
+ sinon.match({
311
+ policyInfo: sinon.match({
312
+ policy: 'receiver-selected',
313
+ csi: 2345,
314
+ }),
315
+ receiveSlots: [fakeReceiveSlots[PINNED_INDEX]],
316
+ codecInfo: sinon.match({
317
+ codec: 'h264',
318
+ maxFs: 3600,
319
+ }),
320
+ })
321
+ );
322
+ });
323
+ });
324
+
325
+ describe('stop()', () => {
326
+ it('stops all RemoteMedia in the group', () => {
327
+ const group = new RemoteMediaGroup(fakeMediaRequestManager, fakeReceiveSlots, 255, true, {
328
+ resolution: 'medium',
329
+ preferLiveVideo: true,
330
+ });
331
+ const stopStubs: any[] = [];
332
+
333
+ group.getRemoteMedia('all').forEach((remoteMedia) => {
334
+ stopStubs.push(sinon.stub(remoteMedia, 'stop'));
335
+ });
336
+
337
+ // pin a few remote media instances
338
+ group.pin(group.getRemoteMedia('unpinned')[2], 12345);
339
+ group.pin(group.getRemoteMedia('unpinned')[1], 12345);
340
+ group.pin(group.getRemoteMedia('unpinned')[0], 12345);
341
+
342
+ assert.strictEqual(group.getRemoteMedia('unpinned').length, NUM_SLOTS - 3);
343
+ assert.strictEqual(group.getRemoteMedia('pinned').length, 3);
344
+
345
+ resetHistory();
346
+
347
+ group.stop(true);
348
+
349
+ // check that all remote media (including pinned ones) have been stopped
350
+ stopStubs.forEach((stub) => {
351
+ assert.calledOnce(stub);
352
+ assert.calledWith(stub, false);
353
+ });
354
+
355
+ // and that we've cancelled the media request for this group
356
+ assert.calledOnce(fakeMediaRequestManager.cancelRequest);
357
+ assert.calledWith(fakeMediaRequestManager.cancelRequest, getLastActiveSpeakerRequestId());
358
+ assert.calledOnce(fakeMediaRequestManager.commit);
359
+ });
360
+ });
361
+
362
+ describe('includes()', () => {
363
+ it('checks if a given RemoteMedia belongs to the group', () => {
364
+ const group = new RemoteMediaGroup(fakeMediaRequestManager, fakeReceiveSlots, 255, true, {
365
+ resolution: 'medium',
366
+ preferLiveVideo: true,
367
+ });
368
+
369
+ const unpinnedRemoteMediaFromGroup = group.getRemoteMedia('all')[0];
370
+ const otherRemoteMedia = new RemoteMedia(
371
+ new FakeSlot(MC.MediaType.VideoMain, 'other slot') as unknown as ReceiveSlot,
372
+ fakeMediaRequestManager
373
+ );
374
+
375
+ group.pin(group.getRemoteMedia('all')[1], 12345);
376
+ const pinnedRemoteMedia = group.getRemoteMedia('pinned')[0];
377
+
378
+ // by default includes() uses 'all' filter
379
+ assert.strictEqual(group.includes(unpinnedRemoteMediaFromGroup), true);
380
+ assert.strictEqual(group.includes(otherRemoteMedia), false);
381
+ assert.strictEqual(group.includes(pinnedRemoteMedia), true);
382
+
383
+ assert.strictEqual(group.includes(unpinnedRemoteMediaFromGroup, 'all'), true);
384
+ assert.strictEqual(group.includes(otherRemoteMedia, 'all'), false);
385
+ assert.strictEqual(group.includes(pinnedRemoteMedia, 'all'), true);
386
+
387
+ assert.strictEqual(group.includes(unpinnedRemoteMediaFromGroup, 'pinned'), false);
388
+ assert.strictEqual(group.includes(otherRemoteMedia, 'pinned'), false);
389
+ assert.strictEqual(group.includes(pinnedRemoteMedia, 'pinned'), true);
390
+
391
+ assert.strictEqual(group.includes(unpinnedRemoteMediaFromGroup, 'unpinned'), true);
392
+ assert.strictEqual(group.includes(pinnedRemoteMedia, 'unpinned'), false);
393
+ assert.strictEqual(group.includes(otherRemoteMedia, 'unpinned'), false);
394
+ });
395
+ });
396
+ });