@webex/plugin-meetings 2.19.1 → 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.
- package/README.md +0 -300
- package/dist/constants.js +3 -206
- package/dist/constants.js.map +1 -1
- package/dist/meeting/index.js +352 -489
- package/dist/meeting/index.js.map +1 -1
- package/dist/meeting/util.js +4 -213
- package/dist/meeting/util.js.map +1 -1
- package/dist/meetings/index.js +0 -28
- package/dist/meetings/index.js.map +1 -1
- package/dist/statsAnalyzer/index.js +145 -86
- package/dist/statsAnalyzer/index.js.map +1 -1
- package/package.json +5 -7
- package/src/constants.ts +1 -214
- package/src/meeting/index.js +110 -208
- package/src/meeting/util.js +4 -252
- package/src/meetings/index.js +0 -22
- package/src/statsAnalyzer/index.js +164 -99
- package/test/integration/spec/journey.js +2 -67
- package/test/unit/spec/meeting/index.js +88 -29
- package/test/unit/spec/meeting/utils.js +0 -2
- package/test/unit/spec/stats-analyzer/index.js +209 -1
- package/dist/analyzer/analyzer.js +0 -113
- package/dist/analyzer/analyzer.js.map +0 -1
- package/dist/analyzer/calculator.js +0 -87
- package/dist/analyzer/calculator.js.map +0 -1
- package/dist/metrics/mqa-processor.js +0 -170
- package/dist/metrics/mqa-processor.js.map +0 -1
- package/dist/stats/data.js +0 -93
- package/dist/stats/data.js.map +0 -1
- package/dist/stats/events.js +0 -222
- package/dist/stats/events.js.map +0 -1
- package/dist/stats/filter.js +0 -84
- package/dist/stats/filter.js.map +0 -1
- package/dist/stats/history.js +0 -147
- package/dist/stats/history.js.map +0 -1
- package/dist/stats/index.js +0 -425
- package/dist/stats/index.js.map +0 -1
- package/dist/stats/metrics.js +0 -112
- package/dist/stats/metrics.js.map +0 -1
- package/dist/stats/stats.js +0 -592
- package/dist/stats/stats.js.map +0 -1
- package/dist/stats/stream.js +0 -156
- package/dist/stats/stream.js.map +0 -1
- package/dist/stats/transformer.js +0 -126
- package/dist/stats/transformer.js.map +0 -1
- package/dist/stats/util.js +0 -64
- package/dist/stats/util.js.map +0 -1
- package/src/analyzer/analyzer.js +0 -78
- package/src/analyzer/calculator.js +0 -77
- package/src/metrics/mqa-processor.js +0 -118
- package/src/stats/data.js +0 -56
- package/src/stats/events.js +0 -185
- package/src/stats/filter.js +0 -40
- package/src/stats/history.js +0 -107
- package/src/stats/index.js +0 -320
- package/src/stats/metrics.js +0 -95
- package/src/stats/stats.js +0 -477
- package/src/stats/stream.js +0 -108
- package/src/stats/transformer.js +0 -109
- 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
|
-
|
|
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: {
|
package/src/meeting/index.js
CHANGED
|
@@ -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.
|
|
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
|
-
|
|
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
|
-
|
|
4260
|
+
stopStatsAnalyzer
|
|
4261
|
+
.then(() => {
|
|
4262
|
+
this.statsAnalyzer = null;
|
|
4369
4263
|
|
|
4370
|
-
|
|
4371
|
-
|
|
4372
|
-
|
|
4373
|
-
|
|
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
|
-
|
|
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
|
-
|
|
4393
|
-
|
|
4394
|
-
|
|
4395
|
-
|
|
4396
|
-
|
|
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
|
-
|
|
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
|
|