@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
@@ -12,6 +12,7 @@ describe('plugin-meetings', () => {
12
12
  let meeting;
13
13
  let audio;
14
14
  let video;
15
+ let originalRemoteUpdateAudioVideo;
15
16
 
16
17
  const fakeLocus = {info: 'this is a fake locus'};
17
18
 
@@ -34,10 +35,16 @@ describe('plugin-meetings', () => {
34
35
  audio = createMuteState(AUDIO, meeting, {sendAudio: true});
35
36
  video = createMuteState(VIDEO, meeting, {sendVideo: true});
36
37
 
38
+ originalRemoteUpdateAudioVideo = MeetingUtil.remoteUpdateAudioVideo;
39
+
37
40
  MeetingUtil.remoteUpdateAudioVideo = sinon.stub().resolves(fakeLocus);
38
41
  Media.setLocalTrack = sinon.stub();
39
42
  });
40
43
 
44
+ afterEach(() => {
45
+ MeetingUtil.remoteUpdateAudioVideo = originalRemoteUpdateAudioVideo;
46
+ });
47
+
41
48
  describe('mute state library', () => {
42
49
  it('does not create an audio instance if we are not sending audio', async () => {
43
50
  assert.isNull(createMuteState(AUDIO, meeting, {sendAudio: false}));
@@ -41,7 +41,6 @@ describe('plugin-meetings', () => {
41
41
  meeting.unsetRemoteTracks = sinon.stub();
42
42
  meeting.unsetPeerConnections = sinon.stub();
43
43
  meeting.reconnectionManager = {cleanUp: sinon.stub()};
44
- meeting.roap = {stop: sinon.stub()};
45
44
  meeting.stopKeepAlive = sinon.stub();
46
45
  });
47
46
 
@@ -63,7 +62,6 @@ describe('plugin-meetings', () => {
63
62
  assert.calledOnce(meeting.unsetRemoteTracks);
64
63
  assert.calledOnce(meeting.unsetPeerConnections);
65
64
  assert.calledOnce(meeting.reconnectionManager.cleanUp);
66
- assert.calledOnce(meeting.roap.stop);
67
65
  assert.calledOnce(meeting.stopKeepAlive);
68
66
  });
69
67
  });
@@ -136,6 +134,48 @@ describe('plugin-meetings', () => {
136
134
  });
137
135
  });
138
136
 
137
+ describe('remoteUpdateAudioVideo', () => {
138
+ it('#Should call meetingRequest.remoteAudioVideoToggle with correct parameters (multistream)', async () => {
139
+ const meeting = {
140
+ correlationId: 'correlation id',
141
+ isMultistream: true,
142
+ mediaId: '12345',
143
+ meetingJoinUrl: 'meetingJoinUrl',
144
+ locusUrl: 'locusUrl',
145
+ deviceUrl: 'some device url',
146
+ selfId: 'self id',
147
+ meetingRequest: {remoteAudioVideoToggle: sinon.stub().returns(Promise.resolve({body: {}, headers: {}}))}
148
+ };
149
+
150
+ await MeetingUtil.remoteUpdateAudioVideo(true, false, meeting);
151
+
152
+ assert.calledOnce(meeting.meetingRequest.remoteAudioVideoToggle);
153
+ const parameter = meeting.meetingRequest.remoteAudioVideoToggle.getCall(0).args[0];
154
+
155
+ assert.equal(parameter.locusUrl, 'locusUrl');
156
+ assert.equal(parameter.selfId, 'self id');
157
+ assert.equal(parameter.correlationId, 'correlation id');
158
+ assert.equal(parameter.deviceUrl, 'some device url');
159
+ assert.deepEqual(parameter.localMedias, [{localSdp: '{"audioMuted":true,"videoMuted":false}', mediaId: '12345'}]);
160
+ assert.equal(parameter.preferTranscoding, false);
161
+ });
162
+
163
+ it('#Should call meetingRequest.remoteAudioVideoToggle with preferTranscoding:true for non multistream connections', async () => {
164
+ const meeting = {
165
+ isMultistream: false,
166
+ mediaId: '12345',
167
+ meetingRequest: {remoteAudioVideoToggle: sinon.stub().returns(Promise.resolve({body: {}, headers: {}}))}
168
+ };
169
+
170
+ await MeetingUtil.remoteUpdateAudioVideo(true, false, meeting);
171
+
172
+ assert.calledOnce(meeting.meetingRequest.remoteAudioVideoToggle);
173
+ const parameter = meeting.meetingRequest.remoteAudioVideoToggle.getCall(0).args[0];
174
+
175
+ assert.equal(parameter.preferTranscoding, true);
176
+ });
177
+ });
178
+
139
179
  describe('joinMeeting', () => {
140
180
  it('#Should call `meetingRequest.joinMeeting', async () => {
141
181
  const meeting = {meetingJoinUrl: 'meetingJoinUrl', locusUrl: 'locusUrl', meetingRequest: {joinMeeting: sinon.stub().returns(Promise.resolve({body: {}, headers: {}}))}};
@@ -147,6 +187,25 @@ describe('plugin-meetings', () => {
147
187
  const parameter = meeting.meetingRequest.joinMeeting.getCall(0).args[0];
148
188
 
149
189
  assert.equal(parameter.inviteeAddress, 'meetingJoinUrl');
190
+ assert.equal(parameter.preferTranscoding, true);
191
+ });
192
+
193
+ it('#Should call meetingRequest.joinMeeting with preferTranscoding=false when multistream is enabled', async () => {
194
+ const meeting = {
195
+ isMultistream: true,
196
+ meetingJoinUrl: 'meetingJoinUrl',
197
+ locusUrl: 'locusUrl',
198
+ meetingRequest: {joinMeeting: sinon.stub().returns(Promise.resolve({body: {}, headers: {}}))}
199
+ };
200
+
201
+ MeetingUtil.parseLocusJoin = sinon.stub();
202
+ await MeetingUtil.joinMeeting(meeting, {});
203
+
204
+ assert.calledOnce(meeting.meetingRequest.joinMeeting);
205
+ const parameter = meeting.meetingRequest.joinMeeting.getCall(0).args[0];
206
+
207
+ assert.equal(parameter.inviteeAddress, 'meetingJoinUrl');
208
+ assert.equal(parameter.preferTranscoding, false);
150
209
  });
151
210
 
152
211
  it('#Should fallback sipUrl if meetingJoinUrl does not exists', async () => {
@@ -12,7 +12,6 @@ import uuid from 'uuid';
12
12
  import StaticConfig from '@webex/plugin-meetings/src/common/config';
13
13
  import TriggerProxy from '@webex/plugin-meetings/src/common/events/trigger-proxy';
14
14
  import LoggerConfig from '@webex/plugin-meetings/src/common/logs/logger-config';
15
- import MediaUtil from '@webex/plugin-meetings/src/media/util';
16
15
  import Meeting from '@webex/plugin-meetings/src/meeting';
17
16
  import MeetingUtil from '@webex/plugin-meetings/src/meeting/util';
18
17
  import Meetings from '@webex/plugin-meetings/src/meetings';
@@ -787,7 +786,6 @@ describe('plugin-meetings', () => {
787
786
  });
788
787
  describe('#createMeeting', () => {
789
788
  beforeEach(() => {
790
- MediaUtil.createPeerConnection = sinon.stub().returns(true);
791
789
  webex.internal.device.userId = uuid1;
792
790
  webex.internal.device.url = url1;
793
791
  MeetingCollection.set = sinon.stub().returns(true);
@@ -1024,7 +1022,6 @@ describe('plugin-meetings', () => {
1024
1022
  describe('Public Event Triggers', () => {
1025
1023
  describe('#destroy', () => {
1026
1024
  beforeEach(() => {
1027
- MediaUtil.createPeerConnection = sinon.stub().returns(true);
1028
1025
  MeetingUtil.cleanUp = sinon.stub();
1029
1026
  });
1030
1027
  it('should have #destroy', () => {
@@ -1116,7 +1113,6 @@ describe('plugin-meetings', () => {
1116
1113
  let meeting;
1117
1114
 
1118
1115
  beforeEach(async () => {
1119
- MediaUtil.createPeerConnection = sinon.stub().returns(true);
1120
1116
  webex.internal.device.userId = uuid1;
1121
1117
  webex.internal.device.url = url1;
1122
1118
  MeetingCollection.set = sinon.stub().returns(true);
@@ -10,9 +10,8 @@ import chaiAsPromised from 'chai-as-promised';
10
10
  import {Credentials} from '@webex/webex-core';
11
11
  import Support from '@webex/internal-plugin-support';
12
12
  import MockWebex from '@webex/test-helper-mock-webex';
13
-
14
- import Meetings from '@webex/plugin-meetings';
15
13
  import ParameterError from '@webex/plugin-meetings/src/common/errors/parameter';
14
+ import Meetings from '@webex/plugin-meetings';
16
15
  import Members from '@webex/plugin-meetings/src/members';
17
16
  import MembersUtil from '@webex/plugin-meetings/src/members/util';
18
17
 
@@ -311,5 +310,168 @@ describe('plugin-meetings', () => {
311
310
  await checkValid(resultPromise, spies, requestingMemberId, url1);
312
311
  });
313
312
  });
313
+
314
+ describe('findMemberByCsi()', () => {
315
+ let members;
316
+
317
+ // fake collection that contains all combinations of members data structure (with respect to CSIs)
318
+ const fakeCollection = {
319
+ oneWithoutDevices: {
320
+ participant: {
321
+ }
322
+ },
323
+ oneWithEmptyDevices: {
324
+ participant: {
325
+ devices: [],
326
+ }
327
+ },
328
+ oneWithDevicesWithoutCsis: {
329
+ participant: {
330
+ devices: [
331
+ {
332
+ url: 'https://fakeURL1.com',
333
+ deviceType: 'SIP',
334
+ state: 'JOINED',
335
+ csis: [],
336
+ },
337
+ {
338
+ url: 'dialout:///fakeagain',
339
+ deviceType: 'PROVISIONAL',
340
+ state: 'JOINED',
341
+ }
342
+ ],
343
+ },
344
+ id: 'abc-123-abc-123',
345
+ status: 'IN_MEETING',
346
+ },
347
+ oneWithSomeCsis: {
348
+ participant: {
349
+ devices: [
350
+ {
351
+ url: 'https://fakeURL2.com',
352
+ deviceType: 'SIP',
353
+ state: 'JOINED',
354
+ csis: [1000, 1001, 1002],
355
+ },
356
+ {
357
+ url: 'https://fakeURL3.com',
358
+ deviceType: 'SIP',
359
+ state: 'JOINED',
360
+ csis: [2000, 2001, 2002],
361
+ }
362
+ ]
363
+ }
364
+ }
365
+ };
366
+
367
+ beforeEach(() => {
368
+ members = createMembers({url: url1});
369
+ members.membersCollection.setAll(fakeCollection);
370
+ });
371
+
372
+ it('returns undefined if member not found', () => {
373
+ assert.strictEqual(members.findMemberByCsi(123), undefined);
374
+ });
375
+
376
+ it('returns correct member when CSI matches the first device', () => {
377
+ assert.strictEqual(members.findMemberByCsi(1001), fakeCollection.oneWithSomeCsis);
378
+ });
379
+
380
+ it('returns correct member when CSI matches the second device', () => {
381
+ assert.strictEqual(members.findMemberByCsi(2001), fakeCollection.oneWithSomeCsis);
382
+ });
383
+ });
384
+
385
+ describe('getCsisForMember()', () => {
386
+ let members;
387
+
388
+ // fake collection that contains all combinations of members data structure (with respect to CSIs)
389
+ const fakeCollection = {
390
+ oneWithoutParticipant: {
391
+ id: 'oneWithoutParticipant',
392
+ },
393
+ oneWithoutDevices: {
394
+ id: 'oneWithoutDevices',
395
+ participant: {
396
+ }
397
+ },
398
+ oneWithEmptyDevices: {
399
+ id: 'oneWithEmptyDevices',
400
+ participant: {
401
+ devices: [],
402
+ }
403
+ },
404
+ oneWithDevicesWithoutCsis: {
405
+ id: 'oneWithDevicesWithoutCsis',
406
+ participant: {
407
+ devices: [
408
+ {
409
+ url: 'https://fakeURL1.com',
410
+ deviceType: 'SIP',
411
+ state: 'JOINED',
412
+ mediaSessions: [],
413
+ },
414
+ {
415
+ url: 'dialout:///fakeagain',
416
+ deviceType: 'PROVISIONAL',
417
+ state: 'JOINED',
418
+ }
419
+ ],
420
+ },
421
+ status: 'IN_MEETING',
422
+ },
423
+ oneWithSomeCsis: {
424
+ id: 'oneWithSomeCsis',
425
+ participant: {
426
+ devices: [
427
+ {
428
+ url: 'https://fakeURL2.com',
429
+ deviceType: 'SIP',
430
+ state: 'JOINED',
431
+ mediaSessions: [
432
+ {mediaType: 'audio', mediaContent: 'main', csi: 1000},
433
+ {mediaType: 'video', mediaContent: 'main', csi: 1001},
434
+ {mediaType: 'video', mediaContent: 'content', csi: 1002}
435
+ ],
436
+ },
437
+ {
438
+ url: 'https://fakeURL3.com',
439
+ deviceType: 'SIP',
440
+ state: 'JOINED',
441
+ mediaSessions: [
442
+ {mediaType: 'audio', mediaContent: 'main', csi: 2000},
443
+ {mediaType: 'video', mediaContent: 'main', csi: 2001},
444
+ {mediaType: 'video', mediaContent: 'content', csi: 2002}
445
+ ],
446
+ }
447
+ ]
448
+ }
449
+ }
450
+ };
451
+
452
+ beforeEach(() => {
453
+ members = createMembers({url: url1});
454
+ members.membersCollection.setAll(fakeCollection);
455
+ });
456
+
457
+ it('returns empty array if member not found', () => {
458
+ assert.deepEqual(members.getCsisForMember('wrong id'), []);
459
+ });
460
+
461
+ it('returns empty array if member does not have CSIs', () => {
462
+ assert.deepEqual(members.getCsisForMember('oneWithoutParticipant'), []);
463
+ assert.deepEqual(members.getCsisForMember('oneWithoutDevices'), []);
464
+ assert.deepEqual(members.getCsisForMember('oneWithEmptyDevices'), []);
465
+ assert.deepEqual(members.getCsisForMember('oneWithDevicesWithoutCsis'), []);
466
+ });
467
+
468
+ it('returns empty array if mediaType and mediaContent do not match', () => {
469
+ assert.deepEqual(members.getCsisForMember('oneWithSomeCsis', 'audio', 'content'), []);
470
+ });
471
+
472
+ it('returns correct CSI values when there is a match', () => {
473
+ assert.deepEqual(members.getCsisForMember('oneWithSomeCsis', 'video', 'main'), [1001, 2001]);
474
+ });
475
+ });
314
476
  });
315
477
  });