@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
@@ -1,7 +1,8 @@
1
1
  import {cloneDeep} from 'lodash';
2
+ import {MediaConnection as MC} from '@webex/internal-media-core';
2
3
 
3
4
  import EventsScope from '../common/events/events-scope';
4
- import {DEFAULT_GET_STATS_FILTER, CONNECTION_STATE, STATS, MQA_INTEVAL, NETWORK_TYPE, MEDIA_DEVICES, _UNKNOWN_} from '../constants';
5
+ import {DEFAULT_GET_STATS_FILTER, STATS, MQA_INTEVAL, NETWORK_TYPE, MEDIA_DEVICES, _UNKNOWN_} from '../constants';
5
6
  import mqaData from '../mediaQualityMetrics/config';
6
7
  import LoggerProxy from '../common/logs/logger-proxy';
7
8
 
@@ -175,7 +176,7 @@ export class StatsAnalyzer extends EventsScope {
175
176
  }
176
177
 
177
178
  /**
178
- * captures MQA data from peerconnection
179
+ * captures MQA data from media connection
179
180
  *
180
181
  * @public
181
182
  * @memberof StatsAnalyzer
@@ -232,8 +233,8 @@ export class StatsAnalyzer extends EventsScope {
232
233
  // Adding peripheral information
233
234
  mqaData.intervals[0].intervalMetadata.peripherals = [];
234
235
  mqaData.intervals[0].intervalMetadata.peripherals.push({information: _UNKNOWN_, name: MEDIA_DEVICES.SPEAKER});
235
- mqaData.intervals[0].intervalMetadata.peripherals.push({information: this.peerConnection?.audioTransceiver?.sender?.track?.label || _UNKNOWN_, name: MEDIA_DEVICES.MICROPHONE});
236
- mqaData.intervals[0].intervalMetadata.peripherals.push({information: this.peerConnection?.videoTransceiver?.sender?.track?.label || _UNKNOWN_, name: MEDIA_DEVICES.CAMERA});
236
+ mqaData.intervals[0].intervalMetadata.peripherals.push({information: this.statsResults[STATS.AUDIO_CORRELATE][STATS.SEND_DIRECTION].trackLabel || _UNKNOWN_, name: MEDIA_DEVICES.MICROPHONE});
237
+ mqaData.intervals[0].intervalMetadata.peripherals.push({information: this.statsResults[STATS.VIDEO_CORRELATE][STATS.SEND_DIRECTION].trackLabel || _UNKNOWN_, name: MEDIA_DEVICES.CAMERA});
237
238
 
238
239
 
239
240
  mqaData.networkType = this.statsResults.connectionType.local.networkType;
@@ -263,15 +264,15 @@ export class StatsAnalyzer extends EventsScope {
263
264
  }
264
265
 
265
266
  /**
266
- * updated the peerconnection when changed
267
+ * updated the media connection when changed
267
268
  *
268
269
  * @private
269
- * @memberof updatePeerconnection
270
- * @param {PeerConnection} peerConnection
270
+ * @memberof StatsAnalyzer
271
+ * @param {MC.RoapMediaConnection} mediaConnection
271
272
  * @returns {void}
272
273
  */
273
- updatePeerconnection(peerConnection) {
274
- this.peerConnection = peerConnection;
274
+ updateMediaConnection(mediaConnection) {
275
+ this.mediaConnection = mediaConnection;
275
276
  }
276
277
 
277
278
  /**
@@ -279,13 +280,13 @@ export class StatsAnalyzer extends EventsScope {
279
280
  *
280
281
  * @public
281
282
  * @memberof StatsAnalyzer
282
- * @param {PeerConnection} peerConnection
283
+ * @param {MC.RoapMediaConnection} mediaConnection
283
284
  * @returns {Promise}
284
285
  */
285
- startAnalyzer(peerConnection) {
286
+ startAnalyzer(mediaConnection) {
286
287
  if (!this.statsStarted) {
287
288
  this.statsStarted = true;
288
- this.peerConnection = peerConnection;
289
+ this.mediaConnection = mediaConnection;
289
290
 
290
291
  return this.getStatsAndParse()
291
292
  .then(() => {
@@ -326,10 +327,10 @@ export class StatsAnalyzer extends EventsScope {
326
327
  if (sendOneLastMqa) {
327
328
  return this.getStatsAndParse().then(() => {
328
329
  this.sendMqaData();
329
- this.peerConnection = null;
330
+ this.mediaConnection = null;
330
331
  });
331
332
  }
332
- this.peerConnection = null;
333
+ this.mediaConnection = null;
333
334
 
334
335
  return Promise.resolve();
335
336
  }
@@ -634,53 +635,38 @@ export class StatsAnalyzer extends EventsScope {
634
635
  * @returns {Promise}
635
636
  */
636
637
  getStatsAndParse() {
637
- if (!this.peerConnection) {
638
+ if (!this.mediaConnection) {
638
639
  return Promise.resolve();
639
640
  }
640
641
 
641
- if (this.peerConnection && this.peerConnection.connectionState === CONNECTION_STATE.FAILED) {
642
- LoggerProxy.logger.trace('StatsAnalyzer:index#getStatsAndParse --> PeerConnection is in failed state');
642
+ if (this.mediaConnection && this.mediaConnection.getConnectionState() === MC.ConnectionState.Failed) {
643
+ LoggerProxy.logger.trace('StatsAnalyzer:index#getStatsAndParse --> media connection is in failed state');
643
644
 
644
645
  return Promise.resolve();
645
646
  }
646
647
 
647
648
  LoggerProxy.logger.trace('StatsAnalyzer:index#getStatsAndParse --> Collecting Stats');
648
649
 
649
- return Promise.all([
650
- this.peerConnection.videoTransceiver.sender.getStats().then((res) => {
651
- this.filterAndParseGetStatsResults(res, STATS.VIDEO_CORRELATE, true);
652
- }),
653
-
654
- this.peerConnection.videoTransceiver.receiver.getStats().then((res) => {
655
- this.filterAndParseGetStatsResults(res, STATS.VIDEO_CORRELATE, false);
656
- }),
657
-
658
- this.peerConnection.audioTransceiver.sender.getStats().then((res) => {
659
- this.filterAndParseGetStatsResults(res, STATS.AUDIO_CORRELATE, true);
660
- }),
661
-
662
- this.peerConnection.audioTransceiver.receiver.getStats().then((res) => {
663
- this.filterAndParseGetStatsResults(res, STATS.AUDIO_CORRELATE, false);
664
- }),
665
-
666
- // TODO: add checks for screen share
667
- this.peerConnection.shareTransceiver.sender.getStats().then((res) => {
668
- this.filterAndParseGetStatsResults(res, STATS.SHARE_CORRELATE, true);
669
- }),
650
+ return this.mediaConnection.getTransceiverStats().then((transceiverStats) => {
651
+ this.filterAndParseGetStatsResults(transceiverStats.video.sender, STATS.VIDEO_CORRELATE, true);
652
+ this.filterAndParseGetStatsResults(transceiverStats.video.receiver, STATS.VIDEO_CORRELATE, false);
653
+ this.filterAndParseGetStatsResults(transceiverStats.audio.sender, STATS.AUDIO_CORRELATE, true);
654
+ this.filterAndParseGetStatsResults(transceiverStats.audio.receiver, STATS.AUDIO_CORRELATE, false);
655
+ this.filterAndParseGetStatsResults(transceiverStats.screenShareVideo.sender, STATS.SHARE_CORRELATE, true);
656
+ this.filterAndParseGetStatsResults(transceiverStats.screenShareVideo.receiver, STATS.SHARE_CORRELATE, false);
670
657
 
671
- this.peerConnection.shareTransceiver.receiver.getStats().then((res) => {
672
- this.filterAndParseGetStatsResults(res, STATS.SHARE_CORRELATE, false);
673
- }),
658
+ // updates the current direction of media
659
+ this.statsResults[STATS.AUDIO_CORRELATE].direction = transceiverStats.audio.currentDirection;
660
+ this.statsResults[STATS.VIDEO_CORRELATE].direction = transceiverStats.video.currentDirection;
661
+ this.statsResults[STATS.SHARE_CORRELATE].direction = transceiverStats.screenShareVideo.currentDirection;
674
662
 
675
- ]).then(() => {
676
- this.statsResults[STATS.AUDIO_CORRELATE].direction = this.peerConnection.audioTransceiver.currentDirection;
677
- this.statsResults[STATS.VIDEO_CORRELATE].direction = this.peerConnection.videoTransceiver.currentDirection;
678
- this.statsResults[STATS.SHARE_CORRELATE].direction = this.peerConnection.shareTransceiver.currentDirection;
663
+ this.statsResults[STATS.AUDIO_CORRELATE][STATS.SEND_DIRECTION].trackLabel = transceiverStats.audio.localTrackLabel;
664
+ this.statsResults[STATS.VIDEO_CORRELATE][STATS.SEND_DIRECTION].trackLabel = transceiverStats.video.localTrackLabel;
679
665
 
680
- // Process Stats results every 5 seconds
681
666
  this.compareLastStatsResult();
682
667
 
683
668
  // Save the last results to compare with the current
669
+ // DO Deep copy, for some reason it takes the reference all the time rather then old value set
684
670
  this.lastStatsResults = JSON.parse(JSON.stringify(this.statsResults));
685
671
 
686
672
  LoggerProxy.logger.trace('StatsAnalyzer:index#getStatsAndParse --> Finished Collecting Stats');
@@ -364,7 +364,7 @@ skipInNode(describe)('plugin-meetings', () => {
364
364
  stream: response[0]
365
365
  })
366
366
  .then(() => {
367
- console.log('AUDIO ', alice.meeting.mediaProperties.peerConnection.audioTransceiver.sender.track);
367
+ console.log('AUDIO ', alice.meeting.mediaProperties.audioTrack);
368
368
  assert.equal(alice.meeting.mediaProperties.audioTrack.id, response[0].getAudioTracks()[0].id);
369
369
  assert.equal(alice.meeting.mediaProperties.videoTrack.id, oldVideoTrackId);
370
370
  })),
@@ -0,0 +1,223 @@
1
+ import * as internalMediaModule from '@webex/plugin-meetings/src/media/internal-media-core-wrapper';
2
+ import Media from '@webex/plugin-meetings/src/media/index';
3
+ import {assert} from '@webex/test-helper-chai';
4
+ import sinon from 'sinon';
5
+ import StaticConfig from '@webex/plugin-meetings/src/common/config';
6
+
7
+ describe('createMediaConnection', () => {
8
+ const fakeRoapMediaConnection = {
9
+ id: 'roap media connection',
10
+ };
11
+ const fakeAudioTrack = {
12
+ id: 'audio track',
13
+ };
14
+ const fakeVideoTrack = {
15
+ id: 'video track',
16
+ };
17
+
18
+ afterEach(() => {
19
+ sinon.restore();
20
+ });
21
+
22
+ it('creates a RoapMediaConnection when multistream is disabled', () => {
23
+ const roapMediaConnectionConstructorStub = sinon
24
+ .stub(internalMediaModule, 'RoapMediaConnection')
25
+ .returns(fakeRoapMediaConnection);
26
+
27
+ StaticConfig.set({bandwidth: {audio: 123, video: 456, startBitrate: 999}});
28
+
29
+ const ENABLE_EXTMAP = false;
30
+ const ENABLE_RTX = true;
31
+
32
+ Media.createMediaConnection(
33
+ {
34
+ mediaDirection: {
35
+ sendAudio: true,
36
+ sendVideo: true,
37
+ sendShare: false,
38
+ receiveAudio: true,
39
+ receiveVideo: true,
40
+ receiveShare: true,
41
+ },
42
+ audioTrack: fakeAudioTrack,
43
+ videoTrack: fakeVideoTrack,
44
+ shareTrack: null,
45
+ },
46
+ {
47
+ isMultistream: false,
48
+ remoteQualityLevel: 'HIGH',
49
+ enableRtx: ENABLE_RTX,
50
+ enableExtmap: ENABLE_EXTMAP,
51
+ turnServerInfo: {
52
+ url: 'turn server url',
53
+ username: 'turn username',
54
+ password: 'turn password',
55
+ },
56
+ }
57
+ );
58
+ assert.calledOnce(roapMediaConnectionConstructorStub);
59
+ assert.calledWith(
60
+ roapMediaConnectionConstructorStub,
61
+ {
62
+ iceServers: [
63
+ {
64
+ urls: 'turn server url',
65
+ username: 'turn username',
66
+ credential: 'turn password',
67
+ },
68
+ ],
69
+ skipInactiveTransceivers: false,
70
+ requireH264: true,
71
+ sdpMunging: {
72
+ convertPort9to0: false,
73
+ addContentSlides: true,
74
+ bandwidthLimits: {
75
+ audio: 123,
76
+ video: 456,
77
+ },
78
+ startBitrate: 999,
79
+ periodicKeyframes: 20,
80
+ disableExtmap: !ENABLE_EXTMAP,
81
+ disableRtx: !ENABLE_RTX,
82
+ },
83
+ },
84
+ {
85
+ send: {
86
+ audio: fakeAudioTrack,
87
+ video: fakeVideoTrack,
88
+ screenShareVideo: null,
89
+ },
90
+ receive: {
91
+ audio: true,
92
+ video: true,
93
+ screenShareVideo: true,
94
+ remoteQualityLevel: 'HIGH',
95
+ },
96
+ },
97
+ 'mc'
98
+ );
99
+ });
100
+
101
+ it('creates a MultistreamRoapMediaConnection when multistream is enabled', () => {
102
+ const multistreamRoapMediaConnectionConstructorStub = sinon
103
+ .stub(internalMediaModule, 'MultistreamRoapMediaConnection')
104
+ .returns(fakeRoapMediaConnection);
105
+
106
+ Media.createMediaConnection(
107
+ {},
108
+ {
109
+ isMultistream: true,
110
+ turnServerInfo: {
111
+ url: 'turn server url',
112
+ username: 'turn username',
113
+ password: 'turn password',
114
+ },
115
+ }
116
+ );
117
+ assert.calledOnce(multistreamRoapMediaConnectionConstructorStub);
118
+ assert.calledWith(
119
+ multistreamRoapMediaConnectionConstructorStub,
120
+ {
121
+ iceServers: [
122
+ {
123
+ urls: 'turn server url',
124
+ username: 'turn username',
125
+ credential: 'turn password',
126
+ },
127
+ ],
128
+ },
129
+ 'mc'
130
+ );
131
+ });
132
+
133
+ it('passes empty ICE servers array to MultistreamRoapMediaConnection if turnServerInfo is undefined (multistream enabled)', () => {
134
+ const multistreamRoapMediaConnectionConstructorStub = sinon
135
+ .stub(internalMediaModule, 'MultistreamRoapMediaConnection')
136
+ .returns(fakeRoapMediaConnection);
137
+
138
+ Media.createMediaConnection(
139
+ {},
140
+ {
141
+ isMultistream: true,
142
+ turnServerInfo: undefined,
143
+ }
144
+ );
145
+ assert.calledOnce(multistreamRoapMediaConnectionConstructorStub);
146
+ assert.calledWith(
147
+ multistreamRoapMediaConnectionConstructorStub,
148
+ {
149
+ iceServers: [],
150
+ },
151
+ 'mc'
152
+ );
153
+ });
154
+
155
+ it('passes empty ICE servers array to RoapMediaConnection if turnServerInfo is undefined (multistream disabled)', () => {
156
+ const roapMediaConnectionConstructorStub = sinon
157
+ .stub(internalMediaModule, 'RoapMediaConnection')
158
+ .returns(fakeRoapMediaConnection);
159
+
160
+ StaticConfig.set({bandwidth: {audio: 123, video: 456, startBitrate: 999}});
161
+
162
+ const ENABLE_EXTMAP = false;
163
+ const ENABLE_RTX = true;
164
+
165
+ Media.createMediaConnection(
166
+ {
167
+ mediaDirection: {
168
+ sendAudio: true,
169
+ sendVideo: true,
170
+ sendShare: true,
171
+ receiveAudio: true,
172
+ receiveVideo: true,
173
+ receiveShare: true,
174
+ },
175
+ audioTrack: fakeAudioTrack,
176
+ videoTrack: null,
177
+ shareTrack: fakeVideoTrack,
178
+ },
179
+ {
180
+ isMultistream: false,
181
+ remoteQualityLevel: 'HIGH',
182
+ enableRtx: ENABLE_RTX,
183
+ enableExtmap: ENABLE_EXTMAP,
184
+ turnServerInfo: undefined,
185
+ }
186
+ );
187
+ assert.calledOnce(roapMediaConnectionConstructorStub);
188
+ assert.calledWith(
189
+ roapMediaConnectionConstructorStub,
190
+ {
191
+ iceServers: [],
192
+ skipInactiveTransceivers: false,
193
+ requireH264: true,
194
+ sdpMunging: {
195
+ convertPort9to0: false,
196
+ addContentSlides: true,
197
+ bandwidthLimits: {
198
+ audio: 123,
199
+ video: 456,
200
+ },
201
+ startBitrate: 999,
202
+ periodicKeyframes: 20,
203
+ disableExtmap: !ENABLE_EXTMAP,
204
+ disableRtx: !ENABLE_RTX,
205
+ },
206
+ },
207
+ {
208
+ send: {
209
+ audio: fakeAudioTrack,
210
+ video: null,
211
+ screenShareVideo: fakeVideoTrack,
212
+ },
213
+ receive: {
214
+ audio: true,
215
+ video: true,
216
+ screenShareVideo: true,
217
+ remoteQualityLevel: 'HIGH',
218
+ },
219
+ },
220
+ 'mc'
221
+ );
222
+ });
223
+ });
@@ -1,5 +1,6 @@
1
1
  import {assert} from '@webex/test-helper-chai';
2
2
  import sinon from 'sinon';
3
+ import {MediaConnection as MC} from '@webex/internal-media-core';
3
4
  import MediaProperties from '@webex/plugin-meetings/src/media/properties';
4
5
  import MediaUtil from '@webex/plugin-meetings/src/media/util';
5
6
  import testUtils from '../../../utils/testUtils';
@@ -8,47 +9,39 @@ import {Defer} from '@webex/common';
8
9
 
9
10
  describe('MediaProperties', () => {
10
11
  let mediaProperties;
11
- let mockPc;
12
+ let mockMC;
12
13
  let clock;
13
14
 
14
15
  beforeEach(() => {
15
16
  clock = sinon.useFakeTimers();
16
17
 
17
- mockPc = {
18
+ mockMC = {
18
19
  getStats: sinon.stub().resolves([]),
19
- addEventListener: sinon.stub(),
20
- removeEventListener: sinon.stub(),
21
- iceConnectionState: 'connected',
20
+ on: sinon.stub(),
21
+ off: sinon.stub(),
22
+ getConnectionState: sinon.stub().returns(MC.ConnectionState.Connected),
22
23
  };
23
24
 
24
- sinon.stub(MediaUtil, 'createPeerConnection').returns(mockPc);
25
-
26
25
  mediaProperties = new MediaProperties();
26
+ mediaProperties.setMediaPeerConnection(mockMC);
27
27
  });
28
28
 
29
29
  afterEach(() => {
30
30
  clock.restore();
31
31
  sinon.restore();
32
32
  });
33
- describe('waitForIceConnectedState', () => {
33
+ describe('waitForMediaConnectionConnected', () => {
34
34
  it('resolves immediately if ice state is connected', async () => {
35
- mockPc.iceConnectionState = 'connected';
36
-
37
- await mediaProperties.waitForIceConnectedState();
38
- });
39
- it('resolves immediately if ice state is completed', async () => {
40
- mockPc.iceConnectionState = 'completed';
41
-
42
- await mediaProperties.waitForIceConnectedState();
35
+ await mediaProperties.waitForMediaConnectionConnected();
43
36
  });
44
37
  it('rejects after timeout if ice state does not reach connected/completed', async () => {
45
- mockPc.iceConnectionState = 'connecting';
38
+ mockMC.getConnectionState.returns(MC.ConnectionState.Connecting);
46
39
 
47
40
  let promiseResolved = false;
48
41
  let promiseRejected = false;
49
42
 
50
43
  mediaProperties
51
- .waitForIceConnectedState()
44
+ .waitForMediaConnectionConnected()
52
45
  .then(() => {
53
46
  promiseResolved = true;
54
47
  })
@@ -66,128 +59,126 @@ describe('MediaProperties', () => {
66
59
  assert.equal(promiseRejected, true);
67
60
 
68
61
  // check that listener was registered and removed
69
- assert.calledOnce(mockPc.addEventListener);
70
- assert.equal(mockPc.addEventListener.getCall(0).args[0], 'iceconnectionstatechange');
71
- const listener = mockPc.addEventListener.getCall(0).args[1];
62
+ assert.calledOnce(mockMC.on);
63
+ assert.equal(mockMC.on.getCall(0).args[0], MC.Event.CONNECTION_STATE_CHANGED);
64
+ const listener = mockMC.on.getCall(0).args[1];
72
65
 
73
- assert.calledOnce(mockPc.removeEventListener);
74
- assert.calledWith(mockPc.removeEventListener, 'iceconnectionstatechange', listener);
66
+ assert.calledOnce(mockMC.off);
67
+ assert.calledWith(mockMC.off, MC.Event.CONNECTION_STATE_CHANGED, listener);
75
68
  });
76
69
 
77
- ['connected', 'completed'].forEach((successIceState) =>
78
- it(`resolves when ice state reaches ${successIceState}`, async () => {
79
- mockPc.iceConnectionState = 'connecting';
70
+ it(`resolves when media connection reaches "connected" state`, async () => {
71
+ mockMC.getConnectionState.returns(MC.ConnectionState.Connecting);
80
72
 
81
- const clearTimeoutSpy = sinon.spy(clock, 'clearTimeout');
73
+ const clearTimeoutSpy = sinon.spy(clock, 'clearTimeout');
82
74
 
83
- let promiseResolved = false;
84
- let promiseRejected = false;
75
+ let promiseResolved = false;
76
+ let promiseRejected = false;
85
77
 
86
- mediaProperties
87
- .waitForIceConnectedState()
88
- .then(() => {
89
- promiseResolved = true;
90
- })
91
- .catch(() => {
92
- promiseRejected = true;
93
- });
78
+ mediaProperties
79
+ .waitForMediaConnectionConnected()
80
+ .then(() => {
81
+ promiseResolved = true;
82
+ })
83
+ .catch(() => {
84
+ promiseRejected = true;
85
+ });
94
86
 
95
- assert.equal(promiseResolved, false);
96
- assert.equal(promiseRejected, false);
87
+ assert.equal(promiseResolved, false);
88
+ assert.equal(promiseRejected, false);
97
89
 
98
- // check the right listener was registered
99
- assert.calledOnce(mockPc.addEventListener);
100
- assert.equal(mockPc.addEventListener.getCall(0).args[0], 'iceconnectionstatechange');
101
- const listener = mockPc.addEventListener.getCall(0).args[1];
90
+ // check the right listener was registered
91
+ assert.calledOnce(mockMC.on);
92
+ assert.equal(mockMC.on.getCall(0).args[0], MC.Event.CONNECTION_STATE_CHANGED);
93
+ const listener = mockMC.on.getCall(0).args[1];
102
94
 
103
- // call the listener and pretend we are now connected
104
- mockPc.iceConnectionState = successIceState;
105
- listener();
106
- await testUtils.flushPromises();
95
+ // call the listener and pretend we are now connected
96
+ mockMC.getConnectionState.returns(MC.ConnectionState.Connected);
97
+ listener();
98
+ await testUtils.flushPromises();
107
99
 
108
- assert.equal(promiseResolved, true);
109
- assert.equal(promiseRejected, false);
100
+ assert.equal(promiseResolved, true);
101
+ assert.equal(promiseRejected, false);
110
102
 
111
- // check that listener was removed
112
- assert.calledOnce(mockPc.removeEventListener);
113
- assert.calledWith(mockPc.removeEventListener, 'iceconnectionstatechange', listener);
103
+ // check that listener was removed
104
+ assert.calledOnce(mockMC.off);
105
+ assert.calledWith(mockMC.off, MC.Event.CONNECTION_STATE_CHANGED, listener);
114
106
 
115
- assert.calledOnce(clearTimeoutSpy);
116
- })
117
- );
107
+ assert.calledOnce(clearTimeoutSpy);
108
+ });
118
109
  });
119
110
 
120
111
  describe('getCurrentConnectionType', () => {
121
- it('calls waitForIceConnectedState', async () => {
122
- const spy = sinon.stub(mediaProperties, 'waitForIceConnectedState');
112
+ it('calls waitForMediaConnectionConnected', async () => {
113
+ const spy = sinon.stub(mediaProperties, 'waitForMediaConnectionConnected');
123
114
 
124
115
  await mediaProperties.getCurrentConnectionType();
125
116
 
126
117
  assert.calledOnce(spy);
127
118
  });
128
- it('calls getStats() only after waitForIceConnectedState resolves', async () => {
129
- const waitForIceConnectedStateResult = new Defer();
119
+ it('calls getStats() only after waitForMediaConnectionConnected resolves', async () => {
120
+ const waitForMediaConnectionConnectedResult = new Defer();
130
121
 
131
- const waitForIceConnectedStateStub = sinon
132
- .stub(mediaProperties, 'waitForIceConnectedState')
133
- .returns(waitForIceConnectedStateResult.promise);
122
+ const waitForMediaConnectionConnectedStub = sinon
123
+ .stub(mediaProperties, 'waitForMediaConnectionConnected')
124
+ .returns(waitForMediaConnectionConnectedResult.promise);
134
125
 
135
126
  const result = mediaProperties.getCurrentConnectionType();
136
127
 
137
128
  await testUtils.flushPromises();
138
129
 
139
- assert.called(waitForIceConnectedStateStub);
140
- assert.notCalled(mockPc.getStats);
130
+ assert.called(waitForMediaConnectionConnectedStub);
131
+ assert.notCalled(mockMC.getStats);
141
132
 
142
- waitForIceConnectedStateResult.resolve();
133
+ waitForMediaConnectionConnectedResult.resolve();
143
134
  await testUtils.flushPromises();
144
135
 
145
- assert.called(mockPc.getStats);
136
+ assert.called(mockMC.getStats);
146
137
  await result;
147
138
  });
148
- it('rejects if waitForIceConnectedState rejects', async () => {
149
- const waitForIceConnectedStateResult = new Defer();
139
+ it('rejects if waitForMediaConnectionConnected rejects', async () => {
140
+ const waitForMediaConnectionConnectedResult = new Defer();
150
141
 
151
- const waitForIceConnectedStateStub = sinon
152
- .stub(mediaProperties, 'waitForIceConnectedState')
153
- .returns(waitForIceConnectedStateResult.promise);
142
+ const waitForMediaConnectionConnectedStub = sinon
143
+ .stub(mediaProperties, 'waitForMediaConnectionConnected')
144
+ .returns(waitForMediaConnectionConnectedResult.promise);
154
145
 
155
146
  const result = mediaProperties.getCurrentConnectionType();
156
147
 
157
148
  await testUtils.flushPromises();
158
149
 
159
- assert.called(waitForIceConnectedStateStub);
150
+ assert.called(waitForMediaConnectionConnectedStub);
160
151
 
161
- waitForIceConnectedStateResult.reject(new Error('fake error'));
152
+ waitForMediaConnectionConnectedResult.reject(new Error('fake error'));
162
153
  await testUtils.flushPromises();
163
154
 
164
- assert.notCalled(mockPc.getStats);
155
+ assert.notCalled(mockMC.getStats);
165
156
 
166
157
  await assert.isRejected(result);
167
158
  });
168
159
  it('returns "unknown" if getStats() fails', async () => {
169
- mockPc.getStats.rejects(new Error());
160
+ mockMC.getStats.rejects(new Error());
170
161
 
171
162
  const connectionType = await mediaProperties.getCurrentConnectionType();
172
163
  assert.equal(connectionType, 'unknown');
173
164
  });
174
165
 
175
166
  it('returns "unknown" if getStats() returns no candidate pairs', async () => {
176
- mockPc.getStats.resolves([{type: 'something', id: '1234'}]);
167
+ mockMC.getStats.resolves([{type: 'something', id: '1234'}]);
177
168
 
178
169
  const connectionType = await mediaProperties.getCurrentConnectionType();
179
170
  assert.equal(connectionType, 'unknown');
180
171
  });
181
172
 
182
173
  it('returns "unknown" if getStats() returns no successful candidate pair', async () => {
183
- mockPc.getStats.resolves([{type: 'candidate-pair', id: '1234', state: 'inprogress'}]);
174
+ mockMC.getStats.resolves([{type: 'candidate-pair', id: '1234', state: 'inprogress'}]);
184
175
 
185
176
  const connectionType = await mediaProperties.getCurrentConnectionType();
186
177
  assert.equal(connectionType, 'unknown');
187
178
  });
188
179
 
189
180
  it('returns "unknown" if getStats() returns a successful candidate pair but local candidate is missing', async () => {
190
- mockPc.getStats.resolves([
181
+ mockMC.getStats.resolves([
191
182
  {type: 'candidate-pair', id: '1234', state: 'succeeded', localCandidateId: 'wrong id'},
192
183
  ]);
193
184
 
@@ -196,7 +187,7 @@ describe('MediaProperties', () => {
196
187
  });
197
188
 
198
189
  it('returns "UDP" if getStats() returns a successful candidate pair with udp local candidate', async () => {
199
- mockPc.getStats.resolves([
190
+ mockMC.getStats.resolves([
200
191
  {
201
192
  type: 'candidate-pair',
202
193
  id: 'some candidate pair id',
@@ -212,7 +203,7 @@ describe('MediaProperties', () => {
212
203
  });
213
204
 
214
205
  it('returns "TCP" if getStats() returns a successful candidate pair with tcp local candidate', async () => {
215
- mockPc.getStats.resolves([
206
+ mockMC.getStats.resolves([
216
207
  {
217
208
  type: 'candidate-pair',
218
209
  id: 'some candidate pair id',
@@ -233,7 +224,7 @@ describe('MediaProperties', () => {
233
224
  {relayProtocol: 'udp', expectedConnectionType: 'TURN-UDP'},
234
225
  ].forEach(({relayProtocol, expectedConnectionType}) =>
235
226
  it(`returns "${expectedConnectionType}" if getStats() returns a successful candidate pair with a local candidate with relayProtocol=${relayProtocol}`, async () => {
236
- mockPc.getStats.resolves([
227
+ mockMC.getStats.resolves([
237
228
  {
238
229
  type: 'candidate-pair',
239
230
  id: 'some candidate pair id',
@@ -265,7 +256,7 @@ describe('MediaProperties', () => {
265
256
  // in real life this will never happen and all active candidate pairs will have same transport,
266
257
  // but here we're simulating a situation where they have different transports and just checking
267
258
  // that the code still works and just returns the first one
268
- mockPc.getStats.resolves([
259
+ mockMC.getStats.resolves([
269
260
  {
270
261
  type: 'inbound-rtp',
271
262
  id: 'whatever',