@webex/plugin-meetings 2.18.0 → 2.19.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 (64) hide show
  1. package/README.md +0 -300
  2. package/dist/constants.js +3 -206
  3. package/dist/constants.js.map +1 -1
  4. package/dist/meeting/effectsState.js +1 -2
  5. package/dist/meeting/effectsState.js.map +1 -1
  6. package/dist/meeting/index.js +366 -496
  7. package/dist/meeting/index.js.map +1 -1
  8. package/dist/meeting/util.js +4 -213
  9. package/dist/meeting/util.js.map +1 -1
  10. package/dist/meetings/index.js +0 -28
  11. package/dist/meetings/index.js.map +1 -1
  12. package/dist/statsAnalyzer/index.js +145 -86
  13. package/dist/statsAnalyzer/index.js.map +1 -1
  14. package/package.json +6 -8
  15. package/src/constants.ts +1 -214
  16. package/src/meeting/effectsState.js +1 -2
  17. package/src/meeting/index.js +120 -213
  18. package/src/meeting/util.js +4 -252
  19. package/src/meetings/index.js +0 -22
  20. package/src/statsAnalyzer/index.js +164 -99
  21. package/test/integration/spec/journey.js +2 -67
  22. package/test/unit/spec/meeting/effectsState.js +2 -1
  23. package/test/unit/spec/meeting/index.js +88 -29
  24. package/test/unit/spec/meeting/utils.js +0 -2
  25. package/test/unit/spec/stats-analyzer/index.js +209 -1
  26. package/dist/analyzer/analyzer.js +0 -113
  27. package/dist/analyzer/analyzer.js.map +0 -1
  28. package/dist/analyzer/calculator.js +0 -87
  29. package/dist/analyzer/calculator.js.map +0 -1
  30. package/dist/metrics/mqa-processor.js +0 -170
  31. package/dist/metrics/mqa-processor.js.map +0 -1
  32. package/dist/stats/data.js +0 -93
  33. package/dist/stats/data.js.map +0 -1
  34. package/dist/stats/events.js +0 -222
  35. package/dist/stats/events.js.map +0 -1
  36. package/dist/stats/filter.js +0 -84
  37. package/dist/stats/filter.js.map +0 -1
  38. package/dist/stats/history.js +0 -147
  39. package/dist/stats/history.js.map +0 -1
  40. package/dist/stats/index.js +0 -425
  41. package/dist/stats/index.js.map +0 -1
  42. package/dist/stats/metrics.js +0 -112
  43. package/dist/stats/metrics.js.map +0 -1
  44. package/dist/stats/stats.js +0 -592
  45. package/dist/stats/stats.js.map +0 -1
  46. package/dist/stats/stream.js +0 -156
  47. package/dist/stats/stream.js.map +0 -1
  48. package/dist/stats/transformer.js +0 -126
  49. package/dist/stats/transformer.js.map +0 -1
  50. package/dist/stats/util.js +0 -64
  51. package/dist/stats/util.js.map +0 -1
  52. package/src/analyzer/analyzer.js +0 -78
  53. package/src/analyzer/calculator.js +0 -77
  54. package/src/metrics/mqa-processor.js +0 -118
  55. package/src/stats/data.js +0 -56
  56. package/src/stats/events.js +0 -185
  57. package/src/stats/filter.js +0 -40
  58. package/src/stats/history.js +0 -107
  59. package/src/stats/index.js +0 -320
  60. package/src/stats/metrics.js +0 -95
  61. package/src/stats/stats.js +0 -477
  62. package/src/stats/stream.js +0 -108
  63. package/src/stats/transformer.js +0 -109
  64. package/src/stats/util.js +0 -44
package/src/constants.ts CHANGED
@@ -207,103 +207,6 @@ export const DIALER_REGEX = {
207
207
  // eslint-disable-next-line max-len
208
208
  export const IPV4_REGEX = /^(?:[0-9]{1,3}\.){3}[0-9]{1,3}|.local/g;
209
209
 
210
- export const DEFAULT_TRANSFORM_REGEX = {
211
- rtpOutVideo: {
212
- regex: new RegExp('^(?:RTC)?[Oo]utbound_?[Rr][Tt][Pp]_?[Vv]ideo')
213
- },
214
- rtpOutAudio: {
215
- regex: new RegExp('^(?:RTC)?[Oo]utbound_?[Rr][Tt][Pp]_?[Aa]udio')
216
- },
217
- rtpInVideo: {
218
- regex: new RegExp('^(?:RTC)?[Ii]nbound_?[Rr][Tt][Pp]_?[Vv]ideo')
219
- },
220
- rtpInAudio: {
221
- regex: new RegExp('^(?:RTC)?[Ii]nbound_?[Rr][Tt][Pp]_?[Aa]udio')
222
- },
223
- rtpRemoteInboundAudio: {
224
- regex: new RegExp('^(?:RTC)?[Rr]emote[Ii]nbound_?[Rr][Tt][Pp]_?[Aa]udio[sS]tream_?.*')
225
- },
226
- rtpRemoteOutboundAudio: {
227
- regex: new RegExp('^(?:RTC)?[Rr]emote[Oo]utbound_?[Rr][Tt][Pp]_?[Aa]udio[sS]tream_?.*')
228
- },
229
- rtpRemoteInboundVideo: {
230
- regex: new RegExp('^(?:RTC)?[Rr]emote[Ii]nbound_?[Rr][Tt][Pp]_?[Vv]ideo[sS]tream_?.*')
231
- },
232
- rtpRemoteOutboundVideo: {
233
- regex: new RegExp('^(?:RTC)?[Rr]emote[Oo]utbound_?[Rr][Tt][Pp]_?[Vv]ideo[sS]tream_?.*')
234
- },
235
- rtcOutAudio: {
236
- regex: new RegExp('^RTCMediaStreamTrack_(?:local|sender)_'),
237
- profiler: {
238
- type: 'kind',
239
- value: 'audio'
240
- }
241
- },
242
- rtcOutVideo: {
243
- regex: new RegExp('^RTCMediaStreamTrack_(?:local|sender)_'),
244
- profiler: {
245
- type: 'kind',
246
- value: 'video'
247
- }
248
- },
249
- rtcInAudio: {
250
- regex: new RegExp('^RTCMediaStreamTrack_(?:remote|receiver)_'),
251
- profiler: {
252
- type: 'kind',
253
- value: 'audio'
254
- }
255
- },
256
- rtcInVideo: {
257
- regex: new RegExp('^RTCMediaStreamTrack_(?:remote|receiver)_'),
258
- profiler: {
259
- type: 'kind',
260
- value: 'video'
261
- }
262
- },
263
- rtcTransAudio: {
264
- regex: new RegExp('^RTCTransport_[Aa]udio')
265
- },
266
- rtcTransVideo: {
267
- regex: new RegExp('^RTCTransport_[Vv]ideo')
268
- },
269
- rtcCandidatePair: {
270
- regex: new RegExp('^RTCIceCandidatePair_'),
271
- decider: 'nominated',
272
- selector: true,
273
- profiler: {
274
- type: 'type',
275
- value: 'candidate-pair'
276
- }
277
- }
278
- };
279
-
280
- // we need a specific firefox transform regular expression map
281
- // to convert firefox values to a consistent getStats type mapping
282
- // as with chrome/edge/safari
283
- export const DEFAULT_FF_TRANSFORM_REGEX = {
284
- rtpOutVideo: {
285
- regex: new RegExp('^outbound-rtp_video_.*')
286
- },
287
- rtpOutAudio: {
288
- regex: new RegExp('^outbound-rtp_audio_.*')
289
- },
290
- rtpInVideo: {
291
- regex: new RegExp('^inbound-rtp_video_.*')
292
- },
293
- rtpInAudio: {
294
- regex: new RegExp('^inbound-rtp_audio_.*')
295
- },
296
- rtcCandidatePair: {
297
- regex: new RegExp('^candidate-pair.*'),
298
- decider: 'nominated',
299
- selector: true,
300
- profiler: {
301
- type: 'type',
302
- value: 'candidate-pair'
303
- }
304
- }
305
- };
306
-
307
210
  export const VALID_EMAIL_ADDRESS = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
308
211
  export const VALID_PMR_ADDRESS = /([a-z0-9][-a-z0-9, '.']{0,62})@([a-z0-9][-a-z0-9, '.']{0,62})\.webex\.com/i;
309
212
  export const VALID_PMR_LINK = /(https:\/\/)?([a-z0-9][-a-z0-9, '.']{0,62})\.webex\.com\/(meet|join)\/([a-z0-9][-a-z0-9, '.']{0,62})\/?/i;
@@ -320,16 +223,6 @@ export const API = {
320
223
  LOCUS: 'locus'
321
224
  };
322
225
 
323
- export const ANALYSIS_CHECKS = {
324
- INCREASING: 'increasing',
325
- DECREASING: 'decreasing',
326
- CONSTANT: 'constant'
327
- };
328
-
329
- export const ANALYSIS_STATS = {
330
- DEFAULT_KEYS: [{key: 'bytesSent', check: 'increasing', prop: 'rtpOutAudio'}, {key: 'bytesReceived', check: 'increasing', prop: 'rtpInAudio'}]
331
- };
332
-
333
226
  export const CALENDAR_EVENTS = {
334
227
  CREATE: 'event:calendar.meeting.create',
335
228
  UPDATE: 'event:calendar.meeting.update',
@@ -341,46 +234,7 @@ export const CALENDAR_EVENTS = {
341
234
  export const DEFAULT_GET_STATS_FILTER = {
342
235
  types: ['track', 'transport', 'candidate-pair', 'outbound-rtp', 'outboundrtp', 'inbound-rtp', 'inboundrtp', 'remote-inbound-rtp', 'remote-outbound-rtp', 'remote-candidate', 'local-candidate', 'media-source']
343
236
  };
344
- export const DEFAULT_EVENT_VIDEO_SEND_KEYS = ['rtpOutVideo', 'rtcOutVideo', 'rtcTransVideo'];
345
- export const DEFAULT_EVENT_AUDIO_SEND_KEYS = ['rtpOutAudio', 'rtcOutAudio', 'rtcTransAudio'];
346
- export const DEFAULT_EVENT_VIDEO_RECEIVE_KEYS = ['rtpInVideo', 'rtcInVideo', 'rtcTransVideo'];
347
- export const DEFAULT_EVENT_AUDIO_RECEIVE_KEYS = ['rtpInAudio', 'rtcInAudio', 'rtcTransAudio'];
348
- export const DEFAULT_TRANSFORM_KEYS = [
349
- 'rtpOutVideo',
350
- 'rtpOutAudio',
351
- 'rtpInVideo',
352
- 'rtpInAudio',
353
- 'rtcOutAudio',
354
- 'rtcOutVideo',
355
- 'rtcInAudio',
356
- 'rtcInVideo',
357
- 'rtcTransAudio',
358
- 'rtcTransVideo',
359
- 'rtcCandidatePairAudio',
360
- 'rtcCandidatePairVideo'
361
- ];
362
- // remove unneccessary data that stays constant during a call
363
- export const DEFAULT_OMISSION_DATA_KEYS = [
364
- 'id',
365
- 'timestamp',
366
- 'type',
367
- 'ssrc',
368
- 'isRemote',
369
- 'mediaType',
370
- 'kind',
371
- 'trackId',
372
- 'transportId',
373
- 'codecId',
374
- 'mediaSourceId',
375
- 'trackIdentifier'
376
- ];
377
-
378
- export const DELTAEVENT = {
379
- GT: 'GT',
380
- CF: 'CF',
381
- EQ: 'EQ',
382
- LT: 'LT'
383
- };
237
+
384
238
 
385
239
  export const RECORDING_STATE = {
386
240
  RECORDING: 'recording',
@@ -444,7 +298,6 @@ export const EVENT_TRIGGERS = {
444
298
  MEETING_LOG_UPLOAD_FAILURE: 'meeting:logUpload:failure',
445
299
  MEETING_ACTIONS_UPDATE: 'meeting:actionsUpdate',
446
300
  MEETING_STATE_CHANGE: 'meeting:stateChange',
447
- MEETING_HIGH_PACKETLOSS: 'meeting:highPacketLoss',
448
301
  MEETING_MEETING_CONTAINER_UPDATE: 'meeting:meetingContainer:update',
449
302
  MEDIA_QUALITY: 'media:quality',
450
303
  MEETINGS_NETWORK_DISCONNECTED: 'network:disconnected',
@@ -519,10 +372,6 @@ export const MEDIA_STATE = {
519
372
  inactive: 'inactive'
520
373
  };
521
374
 
522
- export const EVENT_STATS_MAP = {
523
- BYTES_SENT: 'bytesSent',
524
- BYTES_RECEIVED: 'bytesReceived'
525
- };
526
375
  export const ERROR_DICTIONARY = {
527
376
  PARAMETER: {
528
377
  NAME: 'ParameterError',
@@ -1007,78 +856,16 @@ export const STATS = {
1007
856
  AUDIO_CORRELATE: 'audio',
1008
857
  VIDEO_CORRELATE: 'video',
1009
858
  SHARE_CORRELATE: 'share',
1010
- AUDIO_SENDER_ID: 'audioSender',
1011
- AUDIO_RECEIVER_ID: 'audioReceiver',
1012
- VIDEO_SENDER_ID: 'videoSender',
1013
- VIDEO_RECEIVER_ID: 'videoReceiver',
1014
- SHARE_SENDER_ID: 'shareSender',
1015
- SHARE_RECEIVER_ID: 'shareReceiver',
1016
859
  SEND_DIRECTION: 'send',
1017
860
  RECEIVE_DIRECTION: 'recv',
1018
- END: 'end',
1019
861
  REMOTE: 'remote',
1020
862
  LOCAL: 'local',
1021
- DATA: 'data',
1022
- CLOSED: 'closed',
1023
- SENDER: 'sender',
1024
- RECEIVER: 'receiver',
1025
- SENDERS: 'senders',
1026
- RECEIVERS: 'receivers',
1027
- CONFIG: {
1028
- senders: {
1029
- audio: {
1030
- transceiver: 'audioTransceiver',
1031
- child: 'sender',
1032
- parent: 'mediaProperties',
1033
- peerConnection: 'peerConnection',
1034
- name: 'audioSender'
1035
- },
1036
- video: {
1037
- transceiver: 'videoTransceiver',
1038
- child: 'sender',
1039
- parent: 'mediaProperties',
1040
- peerConnection: 'peerConnection',
1041
- name: 'videoSender'
1042
- },
1043
- share: {
1044
- transceiver: 'shareTransceiver',
1045
- child: 'sender',
1046
- parent: 'mediaProperties',
1047
- peerConnection: 'peerConnection',
1048
- name: 'shareSender'
1049
- }
1050
- },
1051
- receivers: {
1052
- audio: {
1053
- transceiver: 'audioTransceiver',
1054
- child: 'receiver',
1055
- parent: 'mediaProperties',
1056
- peerConnection: 'peerConnection',
1057
- name: 'audioReceiver'
1058
- },
1059
- video: {
1060
- transceiver: 'videoTransceiver',
1061
- child: 'receiver',
1062
- parent: 'mediaProperties',
1063
- peerConnection: 'peerConnection',
1064
- name: 'videoReceiver'
1065
- },
1066
- share: {
1067
- transceiver: 'shareTransceiver',
1068
- child: 'receiver',
1069
- parent: 'mediaProperties',
1070
- peerConnection: 'peerConnection',
1071
- name: 'shareReceiver'
1072
- }
1073
- }
1074
- }
1075
863
  };
1076
864
 
1077
865
  export const MQA_STATS = {
1078
866
  MQA_SIZE: 120, // MQA is done on 60 second intervals by server def, add a buffer for missed events
1079
867
  CA_TYPE: 'MQA',
1080
868
  DEFAULT_IP: '0.0.0.0',
1081
- DATA_PLACEMENTS: [STATS.AUDIO_SENDER_ID, STATS.AUDIO_RECEIVER_ID, STATS.VIDEO_SENDER_ID, STATS.VIDEO_RECEIVER_ID, STATS.SHARE_SENDER_ID, STATS.SHARE_RECEIVER_ID],
1082
869
  DEFAULT_SHARE_SENDER_STATS: {
1083
870
  common: {
1084
871
  common: {
@@ -109,8 +109,7 @@ class EffectsState {
109
109
  await meeting.updateAudio({
110
110
  sendAudio: true,
111
111
  receiveAudio: meeting.mediaProperties.mediaDirection.receiveAudio,
112
- stream: audioStream,
113
- bnrEnabled: bnr.enabled
112
+ stream: audioStream
114
113
  });
115
114
 
116
115
  LoggerProxy.logger.info('Meeting:effectState#enableBNR. Updated meeting audio with bnr enabled track');
@@ -7,7 +7,7 @@ import {
7
7
  MeetingNotActiveError, createMeetingsError, UserInLobbyError,
8
8
  NoMediaEstablishedYetError, UserNotJoinedError, InvalidSdpError
9
9
  } from '../common/errors/webex-errors';
10
- import StatsAnalyzer from '../statsAnalyzer';
10
+ import {StatsAnalyzer, EVENTS as StatsAnalyzerEvents} from '../statsAnalyzer';
11
11
  import NetworkQualityMonitor from '../networkQualityMonitor';
12
12
  import LoggerProxy from '../common/logs/logger-proxy';
13
13
  import Trigger from '../common/events/trigger-proxy';
@@ -26,9 +26,6 @@ import MeetingRequest from '../meeting/request';
26
26
  import Members from '../members/index';
27
27
  import MeetingUtil from '../meeting/util';
28
28
  import MediaUtil from '../media/util';
29
- import WebRTCStats from '../stats/index';
30
- import StatsMetrics from '../stats/metrics';
31
- import StatsUtil from '../stats/util';
32
29
  import Transcription from '../transcription';
33
30
  import PasswordError from '../common/errors/password-error';
34
31
  import CaptchaError from '../common/errors/captcha-error';
@@ -70,7 +67,6 @@ import {
70
67
  SENDRECV,
71
68
  SHARE_STATUS,
72
69
  SHARE_STOPPED_REASON,
73
- STATS,
74
70
  VIDEO_RESOLUTIONS,
75
71
  VIDEO,
76
72
  BNR_STATUS,
@@ -80,7 +76,6 @@ import BEHAVIORAL_METRICS from '../metrics/constants';
80
76
  import ParameterError from '../common/errors/parameter';
81
77
  import MediaError from '../common/errors/media';
82
78
  import {MeetingInfoV2PasswordError, MeetingInfoV2CaptchaError} from '../meeting-info/meeting-info-v2';
83
- import MQAProcessor from '../metrics/mqa-processor';
84
79
  import BrowserDetection from '../common/browser-detection';
85
80
  import RoapCollection from '../roap/collection';
86
81
 
@@ -548,21 +543,6 @@ export default class Meeting extends StatelessWebexPlugin {
548
543
  * @memberof Meeting
549
544
  */
550
545
  this.meetingFiniteStateMachine = MeetingStateMachine.create(this);
551
- /**
552
- * @instance
553
- * @type {WebRTCStats}
554
- * @public
555
- * @memberof Meeting
556
- */
557
- this.stats = null;
558
- /**
559
- * @instance
560
- * @type {WebRTCStats}
561
- * @readonly
562
- * @private
563
- * @memberof Meeting
564
- */
565
- this.internalStats = null;
566
546
  /**
567
547
  * @instance
568
548
  * @type {String}
@@ -789,14 +769,6 @@ export default class Meeting extends StatelessWebexPlugin {
789
769
  * @memberof Meeting
790
770
  */
791
771
  this.dialOutUrl = '';
792
- /**
793
- * @instance
794
- * @type {MediaMetrics}
795
- * @readonly
796
- * @private
797
- * @memberof Meeting
798
- */
799
- this.mediaQualityMetrics = null;
800
772
  /**
801
773
  * @instance
802
774
  * @type {StatsAnalyzer}
@@ -811,14 +783,6 @@ export default class Meeting extends StatelessWebexPlugin {
811
783
  * @memberof Meeting
812
784
  */
813
785
  this.networkQualityMonitor = null;
814
- /**
815
- * @instance
816
- * @type {MQAProcessor}
817
- * @readonly
818
- * @private
819
- * @memberof Meeting
820
- */
821
- this.mqaProcessor = null;
822
786
  /**
823
787
  * @instance
824
788
  * @type {String}
@@ -2231,129 +2195,6 @@ export default class Meeting extends StatelessWebexPlugin {
2231
2195
  return this.members;
2232
2196
  }
2233
2197
 
2234
- /**
2235
- * If this gets turned off mid meeting after attaching media, it will shut down the MQA metrics and they will
2236
- * not restart unless the start function is called again
2237
- * @returns {undefined}
2238
- * @public
2239
- * @memberof Meeting
2240
- */
2241
- cleanMQAInterval() {
2242
- if (this.mqaInterval) {
2243
- clearInterval(this.mqaInterval);
2244
- }
2245
- }
2246
-
2247
- /**
2248
- * Automatically publishes media metrics data and on a config interval
2249
- * uses the existing getStats data filter, so no new streams are created
2250
- * but it is separate than history, so history will not be available
2251
- * @returns {MediaMetrics}
2252
- * @public
2253
- * @memberof Meeting
2254
- */
2255
- startMediaQualityMetrics() {
2256
- const automaticMetrics = new StatsMetrics({config: this.config});
2257
-
2258
- const stats = this.getStats(automaticMetrics.initialize(), true);
2259
-
2260
- this.mediaQualityMetrics = automaticMetrics;
2261
-
2262
- this.mediaQualityMetrics.setStats(stats);
2263
-
2264
- this.mqaInterval = setInterval(() => this.processMQAData(), this.config.metrics.mqaMetricsInterval);
2265
-
2266
- return automaticMetrics;
2267
- }
2268
-
2269
- /**
2270
- * @private
2271
- * @returns {undefined}
2272
- * @memberof Meeting
2273
- */
2274
- processMQAData() {
2275
- if (!this.mqaProcessor) {
2276
- this.mqaProcessor = new MQAProcessor();
2277
- }
2278
-
2279
- MQA_STATS.DATA_PLACEMENTS.forEach((key) => {
2280
- if (this.mediaQualityMetrics && this.mediaQualityMetrics.stats) {
2281
- let sendRecvData;
2282
-
2283
- if (key.toLowerCase().endsWith(STATS.SENDER)) {
2284
- sendRecvData = this.mediaQualityMetrics.stats.getSender(key);
2285
- }
2286
- else if (key.toLowerCase().endsWith(STATS.RECEIVER)) {
2287
- sendRecvData = this.mediaQualityMetrics.stats.getReceiver(key);
2288
- }
2289
- let mqa;
2290
-
2291
- if (sendRecvData) {
2292
- mqa = sendRecvData.getMQA();
2293
- }
2294
- if (mqa) {
2295
- const interval = mqa.getSlice(this.config.metrics.mqaMetricsInterval / 1000); // milliseconds -> second based intervals
2296
-
2297
- this.mqaProcessor.process(key, interval);
2298
- }
2299
- }
2300
- });
2301
- }
2302
-
2303
- /**
2304
- * Reference to the stats builder object
2305
- * @param {Object} options - see #createStats
2306
- * @param {Boolean} override - override the previous getStats
2307
- * @returns {WebRTCStats}
2308
- * @public
2309
- * @memberof Meeting
2310
- */
2311
- getStats(options, override) {
2312
- if (!this.stats) {
2313
- return this.createStats(options);
2314
- }
2315
- if (override) {
2316
- if (this.stats) {
2317
- LoggerProxy.logger.log('Meeting:index#getStats --> Overriding the previous stats object without destroying first can result in memory leaks.');
2318
- }
2319
-
2320
- return this.createStats(options);
2321
- }
2322
-
2323
- return this.stats;
2324
- }
2325
-
2326
- /**
2327
- * write the stats builder object and assign to meeting property
2328
- * @param {Object} options
2329
- * @returns {WebRTCStats}
2330
- * @public
2331
- * @memberof Meeting
2332
- */
2333
- createStats(options = {}) {
2334
- StatsUtil.generateOptions(options, STATS.CONFIG, this);
2335
-
2336
- options.config = STATS.CONFIG;
2337
-
2338
- this.stats = new WebRTCStats(this.attrs, this.options, options);
2339
-
2340
- return this.stats;
2341
- }
2342
-
2343
- /**
2344
- * if you have started a stats instance, here's how you can stop it
2345
- * @returns {undefined}
2346
- * @public
2347
- * @memberof Meeting
2348
- */
2349
- stopStats() {
2350
- if (this.stats) {
2351
- this.stats.destroySenders();
2352
- this.stats.destroyReceivers();
2353
- this.stats = null;
2354
- }
2355
- }
2356
-
2357
2198
  /**
2358
2199
  * Truthy when a meeting has an audio connection established
2359
2200
  * @returns {Boolean} true if meeting audio is connected otherwise false
@@ -4160,6 +4001,75 @@ export default class Meeting extends StatelessWebexPlugin {
4160
4001
  */
4161
4002
  getDevices = () => Media.getDevices();
4162
4003
 
4004
+ /**
4005
+ * Registers for all required StatsAnalyzer events
4006
+ * @private
4007
+ * @returns {void}
4008
+ * @memberof Meetings
4009
+ */
4010
+ setupStatsAnalyzerEventHandlers = () => {
4011
+ this.statsAnalyzer.on(StatsAnalyzerEvents.MEDIA_QUALITY, (options) => {
4012
+ // TODO: might have to send the same event to the developer
4013
+ // Add ip address info if geo hint is present
4014
+ options.data.intervalMetadata.peerReflexiveIP = this.webex.meetings.geoHintInfo?.clientAddress || options.data.intervalMetadata.peerReflexiveIP || MQA_STATS.DEFAULT_IP;
4015
+ Metrics.postEvent({event: eventType.MEDIA_QUALITY, meeting: this, data: {intervalData: options.data, networkType: options.networkType}});
4016
+ });
4017
+ this.statsAnalyzer.on(StatsAnalyzerEvents.LOCAL_MEDIA_STARTED, (data) => {
4018
+ Trigger.trigger(
4019
+ this,
4020
+ {
4021
+ file: 'meeting/index',
4022
+ function: 'addMedia'
4023
+ },
4024
+ EVENT_TRIGGERS.MEETING_MEDIA_LOCAL_STARTED,
4025
+ data
4026
+ );
4027
+ Metrics.postEvent({
4028
+ event: eventType.SENDING_MEDIA_START,
4029
+ meeting: this,
4030
+ data: {
4031
+ mediaType: data.type
4032
+ }
4033
+ });
4034
+ });
4035
+ this.statsAnalyzer.on(StatsAnalyzerEvents.LOCAL_MEDIA_STOPPED, (data) => {
4036
+ Metrics.postEvent({
4037
+ event: eventType.SENDING_MEDIA_STOP,
4038
+ meeting: this,
4039
+ data: {
4040
+ mediaType: data.type
4041
+ }
4042
+ });
4043
+ });
4044
+ this.statsAnalyzer.on(StatsAnalyzerEvents.REMOTE_MEDIA_STARTED, (data) => {
4045
+ Trigger.trigger(
4046
+ this,
4047
+ {
4048
+ file: 'meeting/index',
4049
+ function: 'addMedia'
4050
+ },
4051
+ EVENT_TRIGGERS.MEETING_MEDIA_REMOTE_STARTED,
4052
+ data
4053
+ );
4054
+ Metrics.postEvent({
4055
+ event: eventType.RECEIVING_MEDIA_START,
4056
+ meeting: this,
4057
+ data: {
4058
+ mediaType: data.type
4059
+ }
4060
+ });
4061
+ });
4062
+ this.statsAnalyzer.on(StatsAnalyzerEvents.REMOTE_MEDIA_STOPPED, (data) => {
4063
+ Metrics.postEvent({
4064
+ event: eventType.RECEIVING_MEDIA_STOP,
4065
+ meeting: this,
4066
+ data: {
4067
+ mediaType: data.type
4068
+ }
4069
+ });
4070
+ });
4071
+ };
4072
+
4163
4073
  /**
4164
4074
  * Specify joining via audio (option: pstn), video, screenshare
4165
4075
  * @param {Object} options A configurable options object for joining a meeting
@@ -4238,22 +4148,11 @@ export default class Meeting extends StatelessWebexPlugin {
4238
4148
  LoggerProxy.logger.info(`${LOG_HEADER} PeerConnection Received from attachMedia `);
4239
4149
 
4240
4150
  this.setRemoteStream(peerConnection);
4241
- MeetingUtil.startInternalStats(this);
4242
-
4243
- if (this.config.metrics.autoSendMQA) {
4244
- this.startMediaQualityMetrics();
4245
- }
4246
-
4247
4151
  if (this.config.stats.enableStatsAnalyzer) {
4248
4152
  // TODO: ** Dont re create StatsAnalyzer on reconnect or rejoin
4249
4153
  this.networkQualityMonitor = new NetworkQualityMonitor(this.config.stats);
4250
4154
  this.statsAnalyzer = new StatsAnalyzer(this.config.stats, this.networkQualityMonitor);
4251
- this.statsAnalyzer.on(EVENT_TRIGGERS.MEDIA_QUALITY, (options) => {
4252
- // TODO: might have to send the same event to the developer
4253
- // Add ip address info if geo hint is present
4254
- options.data.intervalMetadata.peerReflexiveIP = this.webex.meetings.geoHintInfo?.clientAddress || options.data.intervalMetadata.peerReflexiveIP || MQA_STATS.DEFAULT_IP;
4255
- Metrics.postEvent({event: eventType.MEDIA_QUALITY, meeting: this, data: {intervalData: options.data, networkType: options.networkType}});
4256
- });
4155
+ this.setupStatsAnalyzerEventHandlers();
4257
4156
  this.networkQualityMonitor.on(EVENT_TRIGGERS.NETWORK_QUALITY, this.sendNetworkQualityEvent.bind(this));
4258
4157
  }
4259
4158
  })
@@ -4356,47 +4255,50 @@ export default class Meeting extends StatelessWebexPlugin {
4356
4255
  }))
4357
4256
  .catch((error) => {
4358
4257
  // Clean up stats analyzer, peer connection, and turn off listeners
4359
- if (this.statsAnalyzer) {
4360
- this.statsAnalyzer.stopAnalyzer();
4361
- this.statsAnalyzer = null;
4362
- }
4363
- if (this.mediaProperties.peerConnection) {
4364
- this.closePeerConnections();
4365
- this.unsetPeerConnections();
4366
- }
4258
+ const stopStatsAnalyzer = (this.statsAnalyzer) ? this.statsAnalyzer.stopAnalyzer() : Promise.resolve();
4367
4259
 
4368
- LoggerProxy.logger.error(`${LOG_HEADER} Error adding media failed to initiate PC and send request, `, error);
4260
+ stopStatsAnalyzer
4261
+ .then(() => {
4262
+ this.statsAnalyzer = null;
4369
4263
 
4370
- Metrics.sendBehavioralMetric(
4371
- BEHAVIORAL_METRICS.ADD_MEDIA_FAILURE,
4372
- {
4373
- correlation_id: this.correlationId,
4374
- locus_id: this.locusUrl.split('/').pop(),
4375
- reason: error.message,
4376
- stack: error.stack,
4377
- code: error.code
4378
- }
4379
- );
4264
+ if (this.mediaProperties.peerConnection) {
4265
+ this.closePeerConnections();
4266
+ this.unsetPeerConnections();
4267
+ }
4380
4268
 
4381
- // Upload logs on error while adding media
4382
- Trigger.trigger(
4383
- this,
4384
- {
4385
- file: 'meeting/index',
4386
- function: 'addMedia'
4387
- },
4388
- EVENTS.REQUEST_UPLOAD_LOGS,
4389
- this
4390
- );
4269
+ LoggerProxy.logger.error(`${LOG_HEADER} Error adding media failed to initiate PC and send request, `, error);
4391
4270
 
4392
- // If addMedia failes for not establishing connection then
4393
- // leave the meeting with reson connection failed as meeting anyways will end
4394
- // and cannot be connected unless network condition is checked for firewall
4395
- if (error.code === InvalidSdpError.CODE) {
4396
- this.leave({reason: MEETING_REMOVED_REASON.MEETING_CONNECTION_FAILED});
4397
- }
4271
+ Metrics.sendBehavioralMetric(
4272
+ BEHAVIORAL_METRICS.ADD_MEDIA_FAILURE,
4273
+ {
4274
+ correlation_id: this.correlationId,
4275
+ locus_id: this.locusUrl.split('/').pop(),
4276
+ reason: error.message,
4277
+ stack: error.stack,
4278
+ code: error.code
4279
+ }
4280
+ );
4398
4281
 
4399
- throw error;
4282
+ // Upload logs on error while adding media
4283
+ Trigger.trigger(
4284
+ this,
4285
+ {
4286
+ file: 'meeting/index',
4287
+ function: 'addMedia'
4288
+ },
4289
+ EVENTS.REQUEST_UPLOAD_LOGS,
4290
+ this
4291
+ );
4292
+
4293
+ // If addMedia failes for not establishing connection then
4294
+ // leave the meeting with reson connection failed as meeting anyways will end
4295
+ // and cannot be connected unless network condition is checked for firewall
4296
+ if (error.code === InvalidSdpError.CODE) {
4297
+ this.leave({reason: MEETING_REMOVED_REASON.MEETING_CONNECTION_FAILED});
4298
+ }
4299
+
4300
+ throw error;
4301
+ });
4400
4302
  });
4401
4303
  }
4402
4304
 
@@ -4575,8 +4477,9 @@ export default class Meeting extends StatelessWebexPlugin {
4575
4477
  return this.enqueueMediaUpdate(MEDIA_UPDATE_TYPE.AUDIO, options);
4576
4478
  }
4577
4479
  const {
4578
- sendAudio, receiveAudio, stream, bnrEnabled
4480
+ sendAudio, receiveAudio, stream
4579
4481
  } = options;
4482
+
4580
4483
  const {audioTransceiver} = this.mediaProperties.peerConnection;
4581
4484
  let track = MeetingUtil.getTrack(stream).audioTrack;
4582
4485
 
@@ -4584,10 +4487,14 @@ export default class Meeting extends StatelessWebexPlugin {
4584
4487
  return Promise.reject(new ParameterError('Pass sendAudio and receiveAudio parameter'));
4585
4488
  }
4586
4489
 
4587
- if (sendAudio && !this.isAudioMuted() && (bnrEnabled === BNR_STATUS.ENABLED || bnrEnabled === BNR_STATUS.SHOULD_ENABLE)) {
4588
- LoggerProxy.logger.info('Meeting:index#updateAudio. Calling WebRTC enable bnr method');
4589
- track = await this.internal_enableBNR(track);
4590
- LoggerProxy.logger.info('Meeting:index#updateAudio. WebRTC enable bnr request completed');
4490
+ if (this.effects && this.effects.state) {
4491
+ const bnrEnabled = this.effects.state.bnr.enabled;
4492
+
4493
+ if (sendAudio && !this.isAudioMuted() && (bnrEnabled === BNR_STATUS.ENABLED || bnrEnabled === BNR_STATUS.SHOULD_ENABLE)) {
4494
+ LoggerProxy.logger.info('Meeting:index#updateAudio. Calling WebRTC enable bnr method');
4495
+ track = await this.internal_enableBNR(track);
4496
+ LoggerProxy.logger.info('Meeting:index#updateAudio. WebRTC enable bnr request completed');
4497
+ }
4591
4498
  }
4592
4499
 
4593
4500
  return MeetingUtil.validateOptions({sendAudio, localStream: stream})