@webex/plugin-meetings 3.0.0-beta.339 → 3.0.0-beta.340
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/dist/breakouts/breakout.js +1 -1
- package/dist/breakouts/index.js +1 -1
- package/dist/interpretation/index.js +1 -1
- package/dist/interpretation/siLanguage.js +1 -1
- package/dist/statsAnalyzer/index.js +58 -6
- package/dist/statsAnalyzer/index.js.map +1 -1
- package/dist/types/statsAnalyzer/index.d.ts +22 -0
- package/dist/webinar/index.js +1 -1
- package/package.json +19 -19
- package/src/statsAnalyzer/index.ts +64 -1
- package/test/unit/spec/stats-analyzer/index.js +53 -0
package/dist/breakouts/index.js
CHANGED
|
@@ -1041,7 +1041,7 @@ var Breakouts = _webexCore.WebexPlugin.extend({
|
|
|
1041
1041
|
this.trigger(_constants.BREAKOUTS.EVENTS.ASK_RETURN_TO_MAIN);
|
|
1042
1042
|
}
|
|
1043
1043
|
},
|
|
1044
|
-
version: "3.0.0-beta.
|
|
1044
|
+
version: "3.0.0-beta.340"
|
|
1045
1045
|
});
|
|
1046
1046
|
var _default = Breakouts;
|
|
1047
1047
|
exports.default = _default;
|
|
@@ -17,6 +17,7 @@ var _inherits2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/
|
|
|
17
17
|
var _possibleConstructorReturn2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/possibleConstructorReturn"));
|
|
18
18
|
var _getPrototypeOf2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/getPrototypeOf"));
|
|
19
19
|
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/defineProperty"));
|
|
20
|
+
var _isEmpty2 = _interopRequireDefault(require("lodash/isEmpty"));
|
|
20
21
|
var _cloneDeep2 = _interopRequireDefault(require("lodash/cloneDeep"));
|
|
21
22
|
var _internalMediaCore = require("@webex/internal-media-core");
|
|
22
23
|
var _eventsScope = _interopRequireDefault(require("../common/events/events-scope"));
|
|
@@ -59,6 +60,8 @@ var emptyReceiver = {
|
|
|
59
60
|
var StatsAnalyzer = /*#__PURE__*/function (_EventsScope) {
|
|
60
61
|
(0, _inherits2.default)(StatsAnalyzer, _EventsScope);
|
|
61
62
|
var _super = _createSuper(StatsAnalyzer);
|
|
63
|
+
// Returns the local IP address for diagnostics. this is the local IP of the interface used for the current media connection a host can have many local Ip Addresses
|
|
64
|
+
|
|
62
65
|
/**
|
|
63
66
|
* Creates a new instance of StatsAnalyzer
|
|
64
67
|
* @constructor
|
|
@@ -91,6 +94,7 @@ var StatsAnalyzer = /*#__PURE__*/function (_EventsScope) {
|
|
|
91
94
|
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "statsResults", void 0);
|
|
92
95
|
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "statsStarted", void 0);
|
|
93
96
|
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "successfulCandidatePair", void 0);
|
|
97
|
+
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "localIpAddress", void 0);
|
|
94
98
|
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "receiveSlotCallback", void 0);
|
|
95
99
|
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "emitStartStopEvents", function (mediaType, previousValue, currentValue, isLocal) {
|
|
96
100
|
if (mediaType !== 'audio' && mediaType !== 'video' && mediaType !== 'share') {
|
|
@@ -125,6 +129,33 @@ var StatsAnalyzer = /*#__PURE__*/function (_EventsScope) {
|
|
|
125
129
|
});
|
|
126
130
|
}
|
|
127
131
|
});
|
|
132
|
+
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "extractAndSetLocalIpAddressInfoForDiagnostics", function (successfulCandidatePairId, candidates) {
|
|
133
|
+
var newIpAddress = '';
|
|
134
|
+
if (successfulCandidatePairId && !(0, _isEmpty2.default)(candidates)) {
|
|
135
|
+
var localCandidate = candidates[successfulCandidatePairId];
|
|
136
|
+
if (localCandidate) {
|
|
137
|
+
if (localCandidate.candidateType === 'host') {
|
|
138
|
+
// if it's a host candidate, use the address property - it will be the local IP
|
|
139
|
+
newIpAddress = "".concat(localCandidate.address);
|
|
140
|
+
} else if (localCandidate.candidateType === 'prflx') {
|
|
141
|
+
// if it's a peer reflexive candidate and we're not using a relay (there is no relayProtocol set)
|
|
142
|
+
// then look at the relatedAddress - it will be the local
|
|
143
|
+
//
|
|
144
|
+
// Firefox doesn't populate the relayProtocol property
|
|
145
|
+
if (!localCandidate.relayProtocol) {
|
|
146
|
+
newIpAddress = "".concat(localCandidate.relatedAddress);
|
|
147
|
+
} else {
|
|
148
|
+
// if it's a peer reflexive candidate and we are using a relay -
|
|
149
|
+
// in that case the relatedAddress will be the IP of the TURN server (Linus),
|
|
150
|
+
// so we can only look at the address, but it might be local IP or public IP,
|
|
151
|
+
// depending on if the user is behind a NAT or not
|
|
152
|
+
newIpAddress = "".concat(localCandidate.address);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
_this.localIpAddress = newIpAddress;
|
|
158
|
+
});
|
|
128
159
|
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "parseCandidate", function (result, type, isSender, isRemote) {
|
|
129
160
|
var _this$successfulCandi;
|
|
130
161
|
if (!result || !result.id) {
|
|
@@ -150,6 +181,11 @@ var StatsAnalyzer = /*#__PURE__*/function (_EventsScope) {
|
|
|
150
181
|
candidateType: result.candidateType,
|
|
151
182
|
ipAddress: result.ip,
|
|
152
183
|
// TODO: add ports
|
|
184
|
+
relatedAddress: result.relatedAddress,
|
|
185
|
+
relatedPort: result.relatedPort,
|
|
186
|
+
relayProtocol: result.relayProtocol,
|
|
187
|
+
protocol: result.protocol,
|
|
188
|
+
address: result.address,
|
|
153
189
|
portNumber: result.port,
|
|
154
190
|
networkType: result.networkType,
|
|
155
191
|
priority: result.priority,
|
|
@@ -175,6 +211,7 @@ var StatsAnalyzer = /*#__PURE__*/function (_EventsScope) {
|
|
|
175
211
|
_this.lastEmittedStartStopEvent = {};
|
|
176
212
|
_this.receiveSlotCallback = receiveSlotCallback;
|
|
177
213
|
_this.successfulCandidatePair = {};
|
|
214
|
+
_this.localIpAddress = '';
|
|
178
215
|
return _this;
|
|
179
216
|
}
|
|
180
217
|
|
|
@@ -325,6 +362,18 @@ var StatsAnalyzer = /*#__PURE__*/function (_EventsScope) {
|
|
|
325
362
|
this.mediaConnection = mediaConnection;
|
|
326
363
|
}
|
|
327
364
|
|
|
365
|
+
/**
|
|
366
|
+
* Returns the local IP address for diagnostics.
|
|
367
|
+
* this is the local IP of the interface used for the current media connection
|
|
368
|
+
* a host can have many local Ip Addresses
|
|
369
|
+
* @returns {string | undefined} The local IP address.
|
|
370
|
+
*/
|
|
371
|
+
}, {
|
|
372
|
+
key: "getLocalIpAddress",
|
|
373
|
+
value: function getLocalIpAddress() {
|
|
374
|
+
return this.localIpAddress;
|
|
375
|
+
}
|
|
376
|
+
|
|
328
377
|
/**
|
|
329
378
|
* Starts the stats analyzer on interval
|
|
330
379
|
*
|
|
@@ -462,9 +511,11 @@ var StatsAnalyzer = /*#__PURE__*/function (_EventsScope) {
|
|
|
462
511
|
}
|
|
463
512
|
});
|
|
464
513
|
if (this.statsResults[type]) {
|
|
514
|
+
var _this$successfulCandi2, _this$statsResults;
|
|
465
515
|
this.statsResults[type].direction = statsItem.currentDirection;
|
|
466
516
|
this.statsResults[type].trackLabel = statsItem.localTrackLabel;
|
|
467
517
|
this.statsResults[type].csi = statsItem.csi;
|
|
518
|
+
this.extractAndSetLocalIpAddressInfoForDiagnostics((_this$successfulCandi2 = this.successfulCandidatePair) === null || _this$successfulCandi2 === void 0 ? void 0 : _this$successfulCandi2.localCandidateId, (_this$statsResults = this.statsResults) === null || _this$statsResults === void 0 ? void 0 : _this$statsResults.candidates);
|
|
468
519
|
// reset the successful candidate pair.
|
|
469
520
|
this.successfulCandidatePair = {};
|
|
470
521
|
}
|
|
@@ -832,13 +883,14 @@ var StatsAnalyzer = /*#__PURE__*/function (_EventsScope) {
|
|
|
832
883
|
}
|
|
833
884
|
|
|
834
885
|
/**
|
|
835
|
-
*
|
|
886
|
+
* extracts the local Ip address from the statsResult object by looking at stats results candidates
|
|
887
|
+
* and matches that ID with the successful candidate pair. It looks at the type of local candidate it is
|
|
888
|
+
* and then extracts the IP address from the relatedAddress or address property based on conditions known in webrtc
|
|
889
|
+
* note, there are known incompatibilities and it is possible for this to set undefined, or for the IP address to be the public IP address
|
|
890
|
+
* for example, firefox does not set the relayProtocol, and if the user is behind a NAT it might be the public IP
|
|
836
891
|
* @private
|
|
837
|
-
* @param {
|
|
838
|
-
* @param {
|
|
839
|
-
* @param {boolean} isSender
|
|
840
|
-
* @param {boolean} isRemote
|
|
841
|
-
*
|
|
892
|
+
* @param {string} successfulCandidatePairId - The ID of the successful candidate pair.
|
|
893
|
+
* @param {Object} candidates - the stats result candidates
|
|
842
894
|
* @returns {void}
|
|
843
895
|
*/
|
|
844
896
|
}, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["EVENTS","MEDIA_QUALITY","LOCAL_MEDIA_STARTED","LOCAL_MEDIA_STOPPED","REMOTE_MEDIA_STARTED","REMOTE_MEDIA_STOPPED","emptySender","trackLabel","maxPacketLossRatio","availableBandwidth","bytesSent","meanRemoteJitter","meanRoundTripTime","emptyReceiver","bytesReceived","meanRtpJitter","StatsAnalyzer","config","receiveSlotCallback","undefined","networkQualityMonitor","statsResults","defaultStats","mediaType","previousValue","currentValue","isLocal","Error","lastEmittedStartStopEvent","lastEmittedEvent","local","remote","newEvent","emit","file","function","type","result","isSender","isRemote","id","successfulCandidatePair","localCandidateId","transport","relayProtocol","toUpperCase","protocol","sendRecvType","STATS","SEND_DIRECTION","RECEIVE_DIRECTION","ipType","REMOTE","LOCAL","candidates","candidateType","ipAddress","ip","portNumber","port","networkType","priority","timestamp","time","connectionType","NETWORK_TYPE","VPN","UNKNOWN","totalRoundTripTime","statsStarted","lastStatsResults","correlationId","mqaSentCount","lastMqaDataSent","forEach","includes","recv","send","status","meetingMediaStatus","newMqa","emptyMqaInterval","audioSender","emptyAudioTransmit","getAudioSenderMqa","audioTransmit","push","audioReceiver","emptyAudioReceive","getAudioReceiverMqa","audioReceive","videoSender","emptyVideoTransmit","getVideoSenderMqa","videoTransmit","videoReceiver","emptyVideoReceive","getVideoReceiverMqa","videoReceive","intervalMetadata","peerReflexiveIP","peripherals","information","_UNKNOWN_","name","MEDIA_DEVICES","SPEAKER","MICROPHONE","CAMERA","intervalNumber","resetStatsResults","data","mediaConnection","getStatsAndParse","then","statsInterval","setInterval","analyzerInterval","sendMqaData","mqaInterval","MQA_INTEVAL","resolve","sendOneLastMqa","clearInterval","getStatsResult","processOutboundRTPResult","processInboundRTPResult","compareSentAndReceived","parseCandidate","parseAudioSource","statsItem","types","DEFAULT_GET_STATS_FILTER","report","state","parseGetStatsResult","direction","currentDirection","localTrackLabel","csi","audioLevel","totalAudioEnergy","getCurrentStatsTotals","keyPrefix","value","filter","key","startsWith","reduce","prev","cur","getPreviousStatsTotals","expected","sendAudio","currentStats","previousStats","totalPacketsSent","LoggerProxy","logger","info","emitStartStopEvents","receiveAudio","currentPacketsReceived","previousPacketsReceived","currentSamplesReceived","previousSamplesReceived","sendVideo","framesEncoded","framesSent","receiveVideo","currentFramesReceived","previousFramesReceived","currentFramesDecoded","previousFramesDecoded","currentFramesDropped","previousFramesDropped","sendShare","getConnectionState","ConnectionState","Failed","trace","getTransceiverStats","transceiverStats","video","receivers","receiver","i","filterAndParseGetStatsResults","audio","screenShareVideo","screenShareAudio","senders","sender","compareLastStatsResult","JSON","parse","sendrecvType","kilobytes","frameWidth","frameHeight","width","height","hugeFramesSent","toFixed","keyFramesEncoded","packetsSent","totalKeyFramesEncoded","totalNackCount","nackCount","totalPliCount","pliCount","totalFirCount","firCount","encoderImplementation","qualityLimitationReason","qualityLimitationResolutionChanges","retransmittedPacketsSent","totalBytesSent","headerBytesSent","retransmittedBytesSent","receiveSlot","ssrc","idAndCsi","framesReceived","bytes","totalBytesReceived","currentPacketsLost","packetsLost","totalPacketsLost","packetsReceived","totalPacketsReceived","currentPacketLossRatio","lastPacketReceivedTimestamp","framesDecoded","keyFramesDecoded","decoderImplementation","fecPacketsDiscarded","fecPacketsReceived","headerBytesReceived","jitter","totalSamplesReceived","totalSamplesDecoded","concealedSamples","currentPacketLoss","totalPacketsLostOnReceiver","packetsLostOnReceiver","roundTripTime","reportsReceived","overAllPacketLossRatio","determineUplinkNetworkQuality","remoteRtpResults","statsAnalyzerCurrentStats","EventsScope"],"sources":["index.ts"],"sourcesContent":["/* eslint-disable prefer-destructuring */\n\nimport {cloneDeep} from 'lodash';\nimport {ConnectionState} from '@webex/internal-media-core';\n\nimport EventsScope from '../common/events/events-scope';\nimport {\n DEFAULT_GET_STATS_FILTER,\n STATS,\n MQA_INTEVAL,\n NETWORK_TYPE,\n MEDIA_DEVICES,\n _UNKNOWN_,\n} from '../constants';\nimport {\n emptyAudioReceive,\n emptyAudioTransmit,\n emptyMqaInterval,\n emptyVideoReceive,\n emptyVideoTransmit,\n} from '../mediaQualityMetrics/config';\nimport LoggerProxy from '../common/logs/logger-proxy';\n\nimport defaultStats from './global';\nimport {\n getAudioSenderMqa,\n getAudioReceiverMqa,\n getVideoSenderMqa,\n getVideoReceiverMqa,\n} from './mqaUtil';\nimport {ReceiveSlot} from '../multistream/receiveSlot';\n\nexport const EVENTS = {\n MEDIA_QUALITY: 'MEDIA_QUALITY',\n LOCAL_MEDIA_STARTED: 'LOCAL_MEDIA_STARTED',\n LOCAL_MEDIA_STOPPED: 'LOCAL_MEDIA_STOPPED',\n REMOTE_MEDIA_STARTED: 'REMOTE_MEDIA_STARTED',\n REMOTE_MEDIA_STOPPED: 'REMOTE_MEDIA_STOPPED',\n};\n\nconst emptySender = {\n trackLabel: '',\n maxPacketLossRatio: 0,\n availableBandwidth: 0,\n bytesSent: 0,\n meanRemoteJitter: [],\n meanRoundTripTime: [],\n};\n\nconst emptyReceiver = {\n availableBandwidth: 0,\n bytesReceived: 0,\n meanRtpJitter: [],\n meanRoundTripTime: [],\n};\n\ntype ReceiveSlotCallback = (csi: number) => ReceiveSlot | undefined;\n\n/**\n * Stats Analyzer class that will emit events based on detected quality\n *\n * @export\n * @class StatsAnalyzer\n * @extends {EventsScope}\n */\nexport class StatsAnalyzer extends EventsScope {\n config: any;\n correlationId: any;\n lastEmittedStartStopEvent: any;\n lastMqaDataSent: any;\n lastStatsResults: any;\n meetingMediaStatus: any;\n mqaInterval: NodeJS.Timeout;\n mqaSentCount: any;\n networkQualityMonitor: any;\n mediaConnection: any;\n statsInterval: NodeJS.Timeout;\n statsResults: any;\n statsStarted: any;\n successfulCandidatePair: any;\n receiveSlotCallback: ReceiveSlotCallback;\n\n /**\n * Creates a new instance of StatsAnalyzer\n * @constructor\n * @public\n * @param {Object} config SDK Configuration Object\n * @param {Function} receiveSlotCallback Callback used to access receive slots.\n * @param {Object} networkQualityMonitor class for assessing network characteristics (jitter, packetLoss, latency)\n * @param {Object} statsResults Default properties for stats\n */\n constructor(\n config: any,\n receiveSlotCallback: ReceiveSlotCallback = () => undefined,\n networkQualityMonitor: object = {},\n statsResults: object = defaultStats\n ) {\n super();\n this.statsStarted = false;\n this.statsResults = statsResults;\n this.lastStatsResults = null;\n this.config = config;\n this.networkQualityMonitor = networkQualityMonitor;\n this.correlationId = config.correlationId;\n this.mqaSentCount = -1;\n this.lastMqaDataSent = {};\n this.lastEmittedStartStopEvent = {};\n this.receiveSlotCallback = receiveSlotCallback;\n this.successfulCandidatePair = {};\n }\n\n /**\n * Resets cumulative stats arrays.\n *\n * @public\n * @memberof StatsAnalyzer\n * @returns {void}\n */\n resetStatsResults() {\n Object.keys(this.statsResults).forEach((mediaType) => {\n if (mediaType.includes('recv')) {\n this.statsResults[mediaType].recv.meanRtpJitter = [];\n }\n\n if (mediaType.includes('send')) {\n this.statsResults[mediaType].send.meanRemoteJitter = [];\n this.statsResults[mediaType].send.meanRoundTripTime = [];\n }\n });\n }\n\n /**\n * sets mediaStatus status for analyzing metrics\n *\n * @public\n * @param {Object} status for the audio and video\n * @memberof StatsAnalyzer\n * @returns {void}\n */\n public updateMediaStatus(status: object) {\n this.meetingMediaStatus = status;\n }\n\n /**\n * captures MQA data from media connection\n *\n * @public\n * @memberof StatsAnalyzer\n * @returns {void}\n */\n sendMqaData() {\n const newMqa = cloneDeep(emptyMqaInterval);\n\n Object.keys(this.statsResults).forEach((mediaType) => {\n if (!this.lastMqaDataSent[mediaType]) {\n this.lastMqaDataSent[mediaType] = {};\n }\n\n if (!this.lastMqaDataSent[mediaType].send && mediaType.includes('-send')) {\n this.lastMqaDataSent[mediaType].send = {};\n }\n\n if (!this.lastMqaDataSent[mediaType].recv && mediaType.includes('-recv')) {\n this.lastMqaDataSent[mediaType].recv = {};\n }\n\n if (mediaType.includes('audio-send') || mediaType.includes('audio-share-send')) {\n const audioSender = cloneDeep(emptyAudioTransmit);\n\n getAudioSenderMqa({\n audioSender,\n statsResults: this.statsResults,\n lastMqaDataSent: this.lastMqaDataSent,\n mediaType,\n });\n newMqa.audioTransmit.push(audioSender);\n\n this.lastMqaDataSent[mediaType].send = cloneDeep(this.statsResults[mediaType].send);\n } else if (mediaType.includes('audio-recv') || mediaType.includes('audio-share-recv')) {\n const audioReceiver = cloneDeep(emptyAudioReceive);\n\n getAudioReceiverMqa({\n audioReceiver,\n statsResults: this.statsResults,\n lastMqaDataSent: this.lastMqaDataSent,\n mediaType,\n });\n newMqa.audioReceive.push(audioReceiver);\n\n this.lastMqaDataSent[mediaType].recv = cloneDeep(this.statsResults[mediaType].recv);\n } else if (mediaType.includes('video-send') || mediaType.includes('video-share-send')) {\n const videoSender = cloneDeep(emptyVideoTransmit);\n\n getVideoSenderMqa({\n videoSender,\n statsResults: this.statsResults,\n lastMqaDataSent: this.lastMqaDataSent,\n mediaType,\n });\n newMqa.videoTransmit.push(videoSender);\n\n this.lastMqaDataSent[mediaType].send = cloneDeep(this.statsResults[mediaType].send);\n } else if (mediaType.includes('video-recv') || mediaType.includes('video-share-recv')) {\n const videoReceiver = cloneDeep(emptyVideoReceive);\n\n getVideoReceiverMqa({\n videoReceiver,\n statsResults: this.statsResults,\n lastMqaDataSent: this.lastMqaDataSent,\n mediaType,\n });\n newMqa.videoReceive.push(videoReceiver);\n\n this.lastMqaDataSent[mediaType].recv = cloneDeep(this.statsResults[mediaType].recv);\n }\n });\n\n newMqa.intervalMetadata.peerReflexiveIP = this.statsResults.connectionType.local.ipAddress;\n\n // Adding peripheral information\n newMqa.intervalMetadata.peripherals.push({information: _UNKNOWN_, name: MEDIA_DEVICES.SPEAKER});\n if (this.statsResults['audio-send']) {\n newMqa.intervalMetadata.peripherals.push({\n information: this.statsResults['audio-send'].trackLabel || _UNKNOWN_,\n name: MEDIA_DEVICES.MICROPHONE,\n });\n }\n if (this.statsResults['video-send']) {\n newMqa.intervalMetadata.peripherals.push({\n information: this.statsResults['video-send'].trackLabel || _UNKNOWN_,\n name: MEDIA_DEVICES.CAMERA,\n });\n }\n\n newMqa.networkType = this.statsResults.connectionType.local.networkType;\n\n this.mqaSentCount += 1;\n\n newMqa.intervalNumber = this.mqaSentCount;\n\n this.resetStatsResults();\n\n this.emit(\n {\n file: 'statsAnalyzer',\n function: 'sendMqaData',\n },\n EVENTS.MEDIA_QUALITY,\n {\n data: newMqa,\n // @ts-ignore\n networkType: newMqa.networkType,\n }\n );\n }\n\n /**\n * updated the media connection when changed\n *\n * @private\n * @memberof StatsAnalyzer\n * @param {RoapMediaConnection} mediaConnection\n * @returns {void}\n */\n updateMediaConnection(mediaConnection: any) {\n this.mediaConnection = mediaConnection;\n }\n\n /**\n * Starts the stats analyzer on interval\n *\n * @public\n * @memberof StatsAnalyzer\n * @param {RoapMediaConnection} mediaConnection\n * @returns {Promise}\n */\n public startAnalyzer(mediaConnection: any) {\n if (!this.statsStarted) {\n this.statsStarted = true;\n this.mediaConnection = mediaConnection;\n\n return this.getStatsAndParse().then(() => {\n this.statsInterval = setInterval(() => {\n this.getStatsAndParse();\n }, this.config.analyzerInterval);\n // Trigger initial fetch\n this.sendMqaData();\n this.mqaInterval = setInterval(() => {\n this.sendMqaData();\n }, MQA_INTEVAL);\n });\n }\n\n return Promise.resolve();\n }\n\n /**\n * Cleans up the analyzer when done\n *\n * @public\n * @memberof StatsAnalyzer\n * @returns {void}\n */\n public stopAnalyzer() {\n const sendOneLastMqa = this.mqaInterval && this.statsInterval;\n\n if (this.statsInterval) {\n clearInterval(this.statsInterval);\n this.statsInterval = undefined;\n }\n\n if (this.mqaInterval) {\n clearInterval(this.mqaInterval);\n this.mqaInterval = undefined;\n }\n\n if (sendOneLastMqa) {\n return this.getStatsAndParse().then(() => {\n this.sendMqaData();\n this.mediaConnection = null;\n });\n }\n\n return Promise.resolve();\n }\n\n /**\n * Parse a single result of get stats\n *\n * @private\n * @param {*} getStatsResult\n * @param {String} type\n * @param {boolean} isSender\n * @returns {void}\n * @memberof StatsAnalyzer\n */\n private parseGetStatsResult(getStatsResult: any, type: string, isSender: boolean) {\n if (!getStatsResult) {\n return;\n }\n\n // Generate empty stats results\n if (!this.statsResults[type]) {\n this.statsResults[type] = {};\n }\n\n if (isSender && !this.statsResults[type].send) {\n this.statsResults[type].send = cloneDeep(emptySender);\n } else if (!isSender && !this.statsResults[type].recv) {\n this.statsResults[type].recv = cloneDeep(emptyReceiver);\n }\n\n switch (getStatsResult.type) {\n case 'outbound-rtp':\n this.processOutboundRTPResult(getStatsResult, type);\n break;\n case 'inbound-rtp':\n this.processInboundRTPResult(getStatsResult, type);\n break;\n case 'remote-inbound-rtp':\n case 'remote-outbound-rtp':\n this.compareSentAndReceived(getStatsResult, type);\n break;\n case 'remotecandidate':\n case 'remote-candidate':\n this.parseCandidate(getStatsResult, type, isSender, true);\n break;\n case 'local-candidate':\n this.parseCandidate(getStatsResult, type, isSender, false);\n break;\n case 'media-source':\n // @ts-ignore\n this.parseAudioSource(getStatsResult, type);\n break;\n default:\n break;\n }\n }\n\n /**\n * Filters the get stats results for types\n * @private\n * @param {Array} statsItem\n * @param {String} type\n * @param {boolean} isSender\n * @returns {void}\n */\n filterAndParseGetStatsResults(statsItem: any, type: string, isSender: boolean) {\n const {types} = DEFAULT_GET_STATS_FILTER;\n\n // get the successful candidate pair before parsing stats.\n statsItem.report.forEach((report) => {\n if (report.type === 'candidate-pair' && report.state === 'succeeded') {\n this.successfulCandidatePair = report;\n }\n });\n\n statsItem.report.forEach((result) => {\n if (types.includes(result.type)) {\n this.parseGetStatsResult(result, type, isSender);\n }\n });\n\n if (this.statsResults[type]) {\n this.statsResults[type].direction = statsItem.currentDirection;\n this.statsResults[type].trackLabel = statsItem.localTrackLabel;\n this.statsResults[type].csi = statsItem.csi;\n // reset the successful candidate pair.\n this.successfulCandidatePair = {};\n }\n }\n\n /**\n * parse the audio\n * @param {String} result\n * @param {boolean} type\n * @returns {void}\n */\n parseAudioSource(result: any, type: any) {\n if (!result) {\n return;\n }\n\n if (type.includes('audio-send')) {\n this.statsResults[type].send.audioLevel = result.audioLevel;\n this.statsResults[type].send.totalAudioEnergy = result.totalAudioEnergy;\n }\n }\n\n /**\n * emits started/stopped events for local/remote media by checking\n * if given values are increasing or not. The previousValue, currentValue\n * params can be any numerical value like number of receive packets or\n * decoded frames, etc.\n *\n * @private\n * @param {string} mediaType\n * @param {number} previousValue - value to compare\n * @param {number} currentValue - value to compare (must be same type of value as previousValue)\n * @param {boolean} isLocal - true if stats are for local media being sent out, false for remote media being received\n * @memberof StatsAnalyzer\n * @returns {void}\n */\n emitStartStopEvents = (\n mediaType: string,\n previousValue: number,\n currentValue: number,\n isLocal: boolean\n ) => {\n if (mediaType !== 'audio' && mediaType !== 'video' && mediaType !== 'share') {\n throw new Error(`Unsupported mediaType: ${mediaType}`);\n }\n\n // eslint-disable-next-line no-param-reassign\n if (previousValue === undefined) previousValue = 0;\n // eslint-disable-next-line no-param-reassign\n if (currentValue === undefined) currentValue = 0;\n\n if (!this.lastEmittedStartStopEvent[mediaType]) {\n this.lastEmittedStartStopEvent[mediaType] = {};\n }\n\n const lastEmittedEvent = isLocal\n ? this.lastEmittedStartStopEvent[mediaType].local\n : this.lastEmittedStartStopEvent[mediaType].remote;\n\n let newEvent;\n\n if (currentValue - previousValue > 0) {\n newEvent = isLocal ? EVENTS.LOCAL_MEDIA_STARTED : EVENTS.REMOTE_MEDIA_STARTED;\n } else if (currentValue === previousValue && currentValue > 0) {\n newEvent = isLocal ? EVENTS.LOCAL_MEDIA_STOPPED : EVENTS.REMOTE_MEDIA_STOPPED;\n }\n\n if (newEvent && lastEmittedEvent !== newEvent) {\n if (isLocal) {\n this.lastEmittedStartStopEvent[mediaType].local = newEvent;\n } else {\n this.lastEmittedStartStopEvent[mediaType].remote = newEvent;\n }\n this.emit(\n {\n file: 'statsAnalyzer/index',\n function: 'compareLastStatsResult',\n },\n newEvent,\n {\n type: mediaType,\n }\n );\n }\n };\n\n /**\n * compares current and previous stats to check if packets are not sent\n *\n * @private\n * @memberof StatsAnalyzer\n * @returns {void}\n */\n private compareLastStatsResult() {\n if (this.lastStatsResults !== null && this.meetingMediaStatus) {\n const getCurrentStatsTotals = (keyPrefix: string, value: string): number =>\n Object.keys(this.statsResults)\n .filter((key) => key.startsWith(keyPrefix))\n .reduce((prev, cur) => prev + (this.statsResults[cur]?.recv[value] || 0), 0);\n\n const getPreviousStatsTotals = (keyPrefix: string, value: string): number =>\n Object.keys(this.statsResults)\n .filter((key) => key.startsWith(keyPrefix))\n .reduce((prev, cur) => prev + (this.lastStatsResults[cur]?.recv[value] || 0), 0);\n\n if (this.meetingMediaStatus.expected.sendAudio && this.lastStatsResults['audio-send']) {\n // compare audio stats sent\n // NOTE: relies on there being only one sender.\n const currentStats = this.statsResults['audio-send'].send;\n const previousStats = this.lastStatsResults['audio-send'].send;\n\n if (\n currentStats.totalPacketsSent === previousStats.totalPacketsSent ||\n currentStats.totalPacketsSent === 0\n ) {\n LoggerProxy.logger.info(\n `StatsAnalyzer:index#compareLastStatsResult --> No audio RTP packets sent`,\n currentStats.totalPacketsSent\n );\n } else {\n if (\n currentStats.totalAudioEnergy === previousStats.totalAudioEnergy ||\n currentStats.totalAudioEnergy === 0\n ) {\n LoggerProxy.logger.info(\n `StatsAnalyzer:index#compareLastStatsResult --> No audio Energy present`,\n currentStats.totalAudioEnergy\n );\n }\n\n if (currentStats.audioLevel === 0) {\n LoggerProxy.logger.info(\n `StatsAnalyzer:index#compareLastStatsResult --> audio level is 0 for the user`\n );\n }\n }\n\n this.emitStartStopEvents(\n 'audio',\n previousStats.totalPacketsSent,\n currentStats.totalPacketsSent,\n true\n );\n }\n\n if (this.meetingMediaStatus.expected.receiveAudio) {\n // compare audio stats received\n const currentPacketsReceived = getCurrentStatsTotals('audio-recv', 'totalPacketsReceived');\n const previousPacketsReceived = getPreviousStatsTotals(\n 'audio-recv',\n 'totalPacketsReceived'\n );\n const currentSamplesReceived = getCurrentStatsTotals('audio-recv', 'totalSamplesReceived');\n const previousSamplesReceived = getPreviousStatsTotals(\n 'audio-recv',\n 'totalSamplesReceived'\n );\n\n if (currentPacketsReceived === previousPacketsReceived || currentPacketsReceived === 0) {\n LoggerProxy.logger.info(\n `StatsAnalyzer:index#compareLastStatsResult --> No audio RTP packets received`,\n currentPacketsReceived\n );\n } else if (\n currentSamplesReceived === previousSamplesReceived ||\n currentSamplesReceived === 0\n ) {\n LoggerProxy.logger.info(\n `StatsAnalyzer:index#compareLastStatsResult --> No audio samples received`,\n currentSamplesReceived\n );\n }\n\n this.emitStartStopEvents('audio', previousPacketsReceived, currentPacketsReceived, false);\n }\n\n if (this.meetingMediaStatus.expected.sendVideo && this.lastStatsResults['video-send']) {\n // compare video stats sent\n const currentStats = this.statsResults['video-send'].send;\n const previousStats = this.lastStatsResults['video-send'].send;\n\n if (\n currentStats.totalPacketsSent === previousStats.totalPacketsSent ||\n currentStats.totalPacketsSent === 0\n ) {\n LoggerProxy.logger.info(\n `StatsAnalyzer:index#compareLastStatsResult --> No video RTP packets sent`,\n currentStats.totalPacketsSent\n );\n } else {\n if (\n currentStats.framesEncoded === previousStats.framesEncoded ||\n currentStats.framesEncoded === 0\n ) {\n LoggerProxy.logger.info(\n `StatsAnalyzer:index#compareLastStatsResult --> No video Frames Encoded`,\n currentStats.framesEncoded\n );\n }\n\n if (\n this.statsResults['video-send'].send.framesSent ===\n this.lastStatsResults['video-send'].send.framesSent ||\n this.statsResults['video-send'].send.framesSent === 0\n ) {\n LoggerProxy.logger.info(\n `StatsAnalyzer:index#compareLastStatsResult --> No video Frames sent`,\n this.statsResults['video-send'].send.framesSent\n );\n }\n }\n\n this.emitStartStopEvents('video', previousStats.framesSent, currentStats.framesSent, true);\n }\n\n if (this.meetingMediaStatus.expected.receiveVideo) {\n // compare video stats received\n const currentPacketsReceived = getCurrentStatsTotals('video-recv', 'totalPacketsReceived');\n const previousPacketsReceived = getPreviousStatsTotals(\n 'video-recv',\n 'totalPacketsReceived'\n );\n const currentFramesReceived = getCurrentStatsTotals('video-recv', 'framesReceived');\n const previousFramesReceived = getPreviousStatsTotals('video-recv', 'framesReceived');\n const currentFramesDecoded = getCurrentStatsTotals('video-recv', 'framesDecoded');\n const previousFramesDecoded = getPreviousStatsTotals('video-recv', 'framesDecoded');\n const currentFramesDropped = getCurrentStatsTotals('video-recv', 'framesDropped');\n const previousFramesDropped = getPreviousStatsTotals('video-recv', 'framesDropped');\n\n if (currentPacketsReceived === previousPacketsReceived || currentPacketsReceived === 0) {\n LoggerProxy.logger.info(\n `StatsAnalyzer:index#compareLastStatsResult --> No video RTP packets received`,\n currentPacketsReceived\n );\n } else {\n if (currentFramesReceived === previousFramesReceived || currentFramesReceived === 0) {\n LoggerProxy.logger.info(\n `StatsAnalyzer:index#compareLastStatsResult --> No video frames received`,\n currentFramesReceived\n );\n }\n\n if (currentFramesDecoded === previousFramesDecoded || currentFramesDecoded === 0) {\n LoggerProxy.logger.info(\n `StatsAnalyzer:index#compareLastStatsResult --> No video frames decoded`,\n currentFramesDecoded\n );\n }\n\n if (currentFramesDropped - previousFramesDropped > 10) {\n LoggerProxy.logger.info(\n `StatsAnalyzer:index#compareLastStatsResult --> video frames are getting dropped`,\n currentFramesDropped - previousFramesDropped\n );\n }\n }\n\n this.emitStartStopEvents('video', previousFramesDecoded, currentFramesDecoded, false);\n }\n\n if (this.meetingMediaStatus.expected.sendShare && this.lastStatsResults['video-share-send']) {\n // compare share stats sent\n\n const currentStats = this.statsResults['video-share-send'].send;\n const previousStats = this.lastStatsResults['video-share-send'].send;\n\n if (\n currentStats.totalPacketsSent === previousStats.totalPacketsSent ||\n currentStats.totalPacketsSent === 0\n ) {\n LoggerProxy.logger.info(\n `StatsAnalyzer:index#compareLastStatsResult --> No share RTP packets sent`,\n currentStats.totalPacketsSent\n );\n } else {\n if (\n currentStats.framesEncoded === previousStats.framesEncoded ||\n currentStats.framesEncoded === 0\n ) {\n LoggerProxy.logger.info(\n `StatsAnalyzer:index#compareLastStatsResult --> No share frames getting encoded`,\n currentStats.framesEncoded\n );\n }\n\n if (\n this.statsResults['video-share-send'].send.framesSent ===\n this.lastStatsResults['video-share-send'].send.framesSent ||\n this.statsResults['video-share-send'].send.framesSent === 0\n ) {\n LoggerProxy.logger.info(\n `StatsAnalyzer:index#compareLastStatsResult --> No share frames sent`,\n this.statsResults['video-share-send'].send.framesSent\n );\n }\n }\n }\n\n if (this.meetingMediaStatus.expected.sendShare) {\n // TODO:need to check receive share value\n // compare share stats received\n const currentPacketsReceived = getCurrentStatsTotals(\n 'video-share-recv',\n 'totalPacketsReceived'\n );\n const previousPacketsReceived = getPreviousStatsTotals(\n 'video-share-recv',\n 'totalPacketsReceived'\n );\n const currentFramesReceived = getCurrentStatsTotals('video-share-recv', 'framesReceived');\n const previousFramesReceived = getPreviousStatsTotals('video-share-recv', 'framesReceived');\n const currentFramesDecoded = getCurrentStatsTotals('video-share-recv', 'framesDecoded');\n const previousFramesDecoded = getPreviousStatsTotals('video-share-recv', 'framesDecoded');\n const currentFramesDropped = getCurrentStatsTotals('video-share-recv', 'framesDropped');\n const previousFramesDropped = getPreviousStatsTotals('video-share-recv', 'framesDropped');\n\n if (currentPacketsReceived === previousPacketsReceived || currentPacketsReceived === 0) {\n LoggerProxy.logger.info(\n `StatsAnalyzer:index#compareLastStatsResult --> No share RTP packets received`,\n currentPacketsReceived\n );\n } else {\n if (currentFramesReceived === previousFramesReceived || currentFramesReceived === 0) {\n LoggerProxy.logger.info(\n `StatsAnalyzer:index#compareLastStatsResult --> No share frames received`,\n currentFramesReceived\n );\n }\n\n if (currentFramesDecoded === previousFramesDecoded || currentFramesDecoded === 0) {\n LoggerProxy.logger.info(\n `StatsAnalyzer:index#compareLastStatsResult --> No share frames decoded`,\n currentFramesDecoded\n );\n }\n\n if (currentFramesDropped - previousFramesDropped > 10) {\n LoggerProxy.logger.info(\n `StatsAnalyzer:index#compareLastStatsResult --> share frames are getting dropped`,\n currentFramesDropped - previousFramesDropped\n );\n }\n }\n\n // we are not calling emitStartStopEvents() for sending or receiving share because sharing is often started and stopped\n // in meetings and this.meetingMediaStatus.expected values can be out of sync with the actual packet flow\n // so we would send \"sharing stopped\" events incorrectly\n }\n }\n }\n\n /**\n * Does a `getStats` on all the transceivers and parses the results\n *\n * @private\n * @memberof StatsAnalyzer\n * @returns {Promise}\n */\n private getStatsAndParse() {\n if (!this.mediaConnection) {\n return Promise.resolve();\n }\n\n if (\n this.mediaConnection &&\n this.mediaConnection.getConnectionState() === ConnectionState.Failed\n ) {\n LoggerProxy.logger.trace(\n 'StatsAnalyzer:index#getStatsAndParse --> media connection is in failed state'\n );\n\n return Promise.resolve();\n }\n\n LoggerProxy.logger.trace('StatsAnalyzer:index#getStatsAndParse --> Collecting Stats');\n\n return this.mediaConnection.getTransceiverStats().then((transceiverStats) => {\n transceiverStats.video.receivers.forEach((receiver, i) =>\n this.filterAndParseGetStatsResults(receiver, `video-recv-${i}`, false)\n );\n transceiverStats.audio.receivers.forEach((receiver, i) =>\n this.filterAndParseGetStatsResults(receiver, `audio-recv-${i}`, false)\n );\n transceiverStats.screenShareVideo.receivers.forEach((receiver, i) =>\n this.filterAndParseGetStatsResults(receiver, `video-share-recv-${i}`, false)\n );\n transceiverStats.screenShareAudio.receivers.forEach((receiver, i) =>\n this.filterAndParseGetStatsResults(receiver, `audio-share-recv-${i}`, false)\n );\n\n transceiverStats.video.senders.forEach((sender, i) => {\n if (i > 0) {\n throw new Error('Stats Analyzer does not support multiple senders.');\n }\n this.filterAndParseGetStatsResults(sender, 'video-send', true);\n });\n transceiverStats.audio.senders.forEach((sender, i) => {\n if (i > 0) {\n throw new Error('Stats Analyzer does not support multiple senders.');\n }\n this.filterAndParseGetStatsResults(sender, 'audio-send', true);\n });\n transceiverStats.screenShareVideo.senders.forEach((sender, i) => {\n if (i > 0) {\n throw new Error('Stats Analyzer does not support multiple senders.');\n }\n this.filterAndParseGetStatsResults(sender, 'video-share-send', true);\n });\n transceiverStats.screenShareAudio.senders.forEach((sender, i) => {\n if (i > 0) {\n throw new Error('Stats Analyzer does not support multiple senders.');\n }\n this.filterAndParseGetStatsResults(sender, 'audio-share-send', true);\n });\n\n this.compareLastStatsResult();\n\n // Save the last results to compare with the current\n // DO Deep copy, for some reason it takes the reference all the time rather then old value set\n this.lastStatsResults = JSON.parse(JSON.stringify(this.statsResults));\n\n LoggerProxy.logger.trace(\n 'StatsAnalyzer:index#getStatsAndParse --> Finished Collecting Stats'\n );\n });\n }\n\n /**\n * Processes OutboundRTP stats result and stores\n * @private\n * @param {*} result\n * @param {*} mediaType\n * @returns {void}\n */\n private processOutboundRTPResult(result: any, mediaType: any) {\n const sendrecvType = STATS.SEND_DIRECTION;\n\n if (result.bytesSent) {\n const kilobytes = 0;\n\n if (result.frameWidth && result.frameHeight) {\n this.statsResults[mediaType][sendrecvType].width = result.frameWidth;\n this.statsResults[mediaType][sendrecvType].height = result.frameHeight;\n this.statsResults[mediaType][sendrecvType].framesSent = result.framesSent;\n this.statsResults[mediaType][sendrecvType].hugeFramesSent = result.hugeFramesSent;\n }\n\n this.statsResults[mediaType][sendrecvType].availableBandwidth = kilobytes.toFixed(1);\n\n this.statsResults[mediaType][sendrecvType].framesEncoded = result.framesEncoded;\n this.statsResults[mediaType][sendrecvType].keyFramesEncoded = result.keyFramesEncoded;\n this.statsResults[mediaType][sendrecvType].packetsSent = result.packetsSent;\n\n // Data saved to send MQA metrics\n\n this.statsResults[mediaType][sendrecvType].totalKeyFramesEncoded = result.keyFramesEncoded;\n this.statsResults[mediaType][sendrecvType].totalNackCount = result.nackCount;\n this.statsResults[mediaType][sendrecvType].totalPliCount = result.pliCount;\n this.statsResults[mediaType][sendrecvType].totalPacketsSent = result.packetsSent;\n this.statsResults[mediaType][sendrecvType].totalFirCount = result.firCount;\n this.statsResults[mediaType][sendrecvType].framesSent = result.framesSent;\n this.statsResults[mediaType][sendrecvType].framesEncoded = result.framesEncoded;\n this.statsResults[mediaType][sendrecvType].encoderImplementation =\n result.encoderImplementation;\n this.statsResults[mediaType][sendrecvType].qualityLimitationReason =\n result.qualityLimitationReason;\n this.statsResults[mediaType][sendrecvType].qualityLimitationResolutionChanges =\n result.qualityLimitationResolutionChanges;\n this.statsResults[mediaType][sendrecvType].retransmittedPacketsSent =\n result.retransmittedPacketsSent;\n this.statsResults[mediaType][sendrecvType].totalBytesSent = result.bytesSent;\n this.statsResults[mediaType][sendrecvType].headerBytesSent = result.headerBytesSent;\n this.statsResults[mediaType][sendrecvType].retransmittedBytesSent =\n result.retransmittedBytesSent;\n }\n }\n\n /**\n * Processes InboundRTP stats result and stores\n * @private\n * @param {*} result\n * @param {*} mediaType\n * @returns {void}\n */\n private processInboundRTPResult(result: any, mediaType: any) {\n const sendrecvType = STATS.RECEIVE_DIRECTION;\n\n if (result.bytesReceived) {\n let kilobytes = 0;\n const receiveSlot = this.receiveSlotCallback(result.ssrc);\n const idAndCsi = receiveSlot\n ? `id: \"${receiveSlot.id || ''}\"${receiveSlot.csi ? ` and csi: ${receiveSlot.csi}` : ''}`\n : '';\n\n if (result.frameWidth && result.frameHeight) {\n this.statsResults[mediaType][sendrecvType].width = result.frameWidth;\n this.statsResults[mediaType][sendrecvType].height = result.frameHeight;\n this.statsResults[mediaType][sendrecvType].framesReceived = result.framesReceived;\n }\n\n const bytes =\n result.bytesReceived - this.statsResults[mediaType][sendrecvType].totalBytesReceived;\n\n kilobytes = bytes / 1024;\n this.statsResults[mediaType][sendrecvType].availableBandwidth = kilobytes.toFixed(1);\n\n let currentPacketsLost =\n result.packetsLost - this.statsResults[mediaType][sendrecvType].totalPacketsLost;\n if (currentPacketsLost < 0) {\n currentPacketsLost = 0;\n }\n\n const currentPacketsReceived =\n result.packetsReceived - this.statsResults[mediaType][sendrecvType].totalPacketsReceived;\n this.statsResults[mediaType][sendrecvType].totalPacketsReceived = result.packetsReceived;\n\n if (currentPacketsReceived === 0) {\n if (receiveSlot) {\n LoggerProxy.logger.info(\n `StatsAnalyzer:index#processInboundRTPResult --> No packets received for receive slot ${idAndCsi}`,\n currentPacketsReceived\n );\n }\n }\n\n // Check the over all packet Lost ratio\n this.statsResults[mediaType][sendrecvType].currentPacketLossRatio =\n currentPacketsLost > 0\n ? currentPacketsLost / (currentPacketsReceived + currentPacketsLost)\n : 0;\n if (this.statsResults[mediaType][sendrecvType].currentPacketLossRatio > 3) {\n LoggerProxy.logger.info(\n `StatsAnalyzer:index#processInboundRTPResult --> Packets getting lost from the receiver with slot ${idAndCsi}`,\n this.statsResults[mediaType][sendrecvType].currentPacketLossRatio\n );\n }\n\n // TODO: check the packet loss value is negative values here\n\n if (result.packetsLost) {\n this.statsResults[mediaType][sendrecvType].totalPacketsLost =\n result.packetsLost > 0 ? result.packetsLost : -result.packetsLost;\n } else {\n this.statsResults[mediaType][sendrecvType].totalPacketsLost = 0;\n }\n\n this.statsResults[mediaType][sendrecvType].lastPacketReceivedTimestamp =\n result.lastPacketReceivedTimestamp;\n\n // From Thin\n this.statsResults[mediaType][sendrecvType].totalNackCount = result.nackCount;\n this.statsResults[mediaType][sendrecvType].totalPliCount = result.pliCount;\n this.statsResults[mediaType][sendrecvType].framesDecoded = result.framesDecoded;\n this.statsResults[mediaType][sendrecvType].keyFramesDecoded = result.keyFramesDecoded;\n\n this.statsResults[mediaType][sendrecvType].decoderImplementation =\n result.decoderImplementation;\n this.statsResults[mediaType][sendrecvType].totalPacketsReceived = result.packetsReceived;\n\n this.statsResults[mediaType][sendrecvType].fecPacketsDiscarded = result.fecPacketsDiscarded;\n this.statsResults[mediaType][sendrecvType].fecPacketsReceived = result.fecPacketsReceived;\n this.statsResults[mediaType][sendrecvType].totalBytesReceived = result.bytesReceived;\n this.statsResults[mediaType][sendrecvType].headerBytesReceived = result.headerBytesReceived;\n\n this.statsResults[mediaType][sendrecvType].meanRtpJitter.push(result.jitter);\n\n // Audio stats\n\n this.statsResults[mediaType][sendrecvType].audioLevel = result.audioLevel;\n this.statsResults[mediaType][sendrecvType].totalAudioEnergy = result.totalAudioEnergy;\n this.statsResults[mediaType][sendrecvType].totalSamplesReceived =\n result.totalSamplesReceived || 0;\n this.statsResults[mediaType][sendrecvType].totalSamplesDecoded =\n result.totalSamplesDecoded || 0;\n this.statsResults[mediaType][sendrecvType].concealedSamples = result.concealedSamples || 0;\n }\n }\n\n /**\n * Processes remote and local candidate result and stores\n * @private\n * @param {*} result\n * @param {*} type\n * @param {boolean} isSender\n * @param {boolean} isRemote\n *\n * @returns {void}\n */\n parseCandidate = (result: any, type: any, isSender: boolean, isRemote: boolean) => {\n if (!result || !result.id) {\n return;\n }\n\n // We only care about the successful local candidate\n if (this.successfulCandidatePair?.localCandidateId !== result.id) {\n return;\n }\n\n let transport;\n if (result.relayProtocol) {\n transport = result.relayProtocol.toUpperCase();\n } else if (result.protocol) {\n transport = result.protocol.toUpperCase();\n }\n\n const sendRecvType = isSender ? STATS.SEND_DIRECTION : STATS.RECEIVE_DIRECTION;\n const ipType = isRemote ? STATS.REMOTE : STATS.LOCAL;\n\n if (!this.statsResults.candidates) {\n this.statsResults.candidates = {};\n }\n\n this.statsResults.candidates[result.id] = {\n candidateType: result.candidateType,\n ipAddress: result.ip, // TODO: add ports\n portNumber: result.port,\n networkType: result.networkType,\n priority: result.priority,\n transport,\n timestamp: result.time,\n id: result.id,\n type: result.type,\n };\n\n this.statsResults.connectionType[ipType].candidateType = result.candidateType;\n this.statsResults.connectionType[ipType].ipAddress = result.ipAddress;\n\n this.statsResults.connectionType[ipType].networkType =\n result.networkType === NETWORK_TYPE.VPN ? NETWORK_TYPE.UNKNOWN : result.networkType;\n this.statsResults.connectionType[ipType].transport = transport;\n\n this.statsResults[type][sendRecvType].totalRoundTripTime = result.totalRoundTripTime;\n };\n\n /**\n *\n * @private\n * @param {*} result\n * @param {*} type\n * @returns {void}\n * @memberof StatsAnalyzer\n */\n compareSentAndReceived(result, type) {\n // Don't compare on transceivers without a sender.\n if (!type || !this.statsResults[type].send) {\n return;\n }\n\n const mediaType = type;\n\n const currentPacketLoss =\n result.packetsLost - this.statsResults[mediaType].send.totalPacketsLostOnReceiver;\n\n this.statsResults[mediaType].send.packetsLostOnReceiver = currentPacketLoss;\n this.statsResults[mediaType].send.totalPacketsLostOnReceiver = result.packetsLost;\n\n this.statsResults[mediaType].send.meanRemoteJitter.push(result.jitter);\n this.statsResults[mediaType].send.meanRoundTripTime.push(result.roundTripTime);\n\n this.statsResults[mediaType].send.timestamp = result.timestamp;\n this.statsResults[mediaType].send.ssrc = result.ssrc;\n this.statsResults[mediaType].send.reportsReceived = result.reportsReceived;\n\n // Total packloss ratio on this video section of the call\n this.statsResults[mediaType].send.overAllPacketLossRatio =\n this.statsResults[mediaType].send.totalPacketsLostOnReceiver > 0\n ? this.statsResults[mediaType].send.totalPacketsLostOnReceiver /\n this.statsResults[mediaType].send.totalPacketsSent\n : 0;\n this.statsResults[mediaType].send.currentPacketLossRatio =\n this.statsResults[mediaType].send.packetsLostOnReceiver > 0\n ? (this.statsResults[mediaType].send.packetsLostOnReceiver * 100) /\n (this.statsResults[mediaType].send.packetsSent +\n this.statsResults[mediaType].send.packetsLostOnReceiver)\n : 0;\n\n if (\n this.statsResults[mediaType].send.maxPacketLossRatio <\n this.statsResults[mediaType].send.currentPacketLossRatio\n ) {\n this.statsResults[mediaType].send.maxPacketLossRatio =\n this.statsResults[mediaType].send.currentPacketLossRatio;\n }\n\n if (result.type === 'remote-inbound-rtp') {\n this.networkQualityMonitor.determineUplinkNetworkQuality({\n mediaType,\n remoteRtpResults: result,\n statsAnalyzerCurrentStats: this.statsResults,\n });\n }\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"names":["EVENTS","MEDIA_QUALITY","LOCAL_MEDIA_STARTED","LOCAL_MEDIA_STOPPED","REMOTE_MEDIA_STARTED","REMOTE_MEDIA_STOPPED","emptySender","trackLabel","maxPacketLossRatio","availableBandwidth","bytesSent","meanRemoteJitter","meanRoundTripTime","emptyReceiver","bytesReceived","meanRtpJitter","StatsAnalyzer","config","receiveSlotCallback","undefined","networkQualityMonitor","statsResults","defaultStats","mediaType","previousValue","currentValue","isLocal","Error","lastEmittedStartStopEvent","lastEmittedEvent","local","remote","newEvent","emit","file","function","type","successfulCandidatePairId","candidates","newIpAddress","localCandidate","candidateType","address","relayProtocol","relatedAddress","localIpAddress","result","isSender","isRemote","id","successfulCandidatePair","localCandidateId","transport","toUpperCase","protocol","sendRecvType","STATS","SEND_DIRECTION","RECEIVE_DIRECTION","ipType","REMOTE","LOCAL","ipAddress","ip","relatedPort","portNumber","port","networkType","priority","timestamp","time","connectionType","NETWORK_TYPE","VPN","UNKNOWN","totalRoundTripTime","statsStarted","lastStatsResults","correlationId","mqaSentCount","lastMqaDataSent","forEach","includes","recv","send","status","meetingMediaStatus","newMqa","emptyMqaInterval","audioSender","emptyAudioTransmit","getAudioSenderMqa","audioTransmit","push","audioReceiver","emptyAudioReceive","getAudioReceiverMqa","audioReceive","videoSender","emptyVideoTransmit","getVideoSenderMqa","videoTransmit","videoReceiver","emptyVideoReceive","getVideoReceiverMqa","videoReceive","intervalMetadata","peerReflexiveIP","peripherals","information","_UNKNOWN_","name","MEDIA_DEVICES","SPEAKER","MICROPHONE","CAMERA","intervalNumber","resetStatsResults","data","mediaConnection","getStatsAndParse","then","statsInterval","setInterval","analyzerInterval","sendMqaData","mqaInterval","MQA_INTEVAL","resolve","sendOneLastMqa","clearInterval","getStatsResult","processOutboundRTPResult","processInboundRTPResult","compareSentAndReceived","parseCandidate","parseAudioSource","statsItem","types","DEFAULT_GET_STATS_FILTER","report","state","parseGetStatsResult","direction","currentDirection","localTrackLabel","csi","extractAndSetLocalIpAddressInfoForDiagnostics","audioLevel","totalAudioEnergy","getCurrentStatsTotals","keyPrefix","value","filter","key","startsWith","reduce","prev","cur","getPreviousStatsTotals","expected","sendAudio","currentStats","previousStats","totalPacketsSent","LoggerProxy","logger","info","emitStartStopEvents","receiveAudio","currentPacketsReceived","previousPacketsReceived","currentSamplesReceived","previousSamplesReceived","sendVideo","framesEncoded","framesSent","receiveVideo","currentFramesReceived","previousFramesReceived","currentFramesDecoded","previousFramesDecoded","currentFramesDropped","previousFramesDropped","sendShare","getConnectionState","ConnectionState","Failed","trace","getTransceiverStats","transceiverStats","video","receivers","receiver","i","filterAndParseGetStatsResults","audio","screenShareVideo","screenShareAudio","senders","sender","compareLastStatsResult","JSON","parse","sendrecvType","kilobytes","frameWidth","frameHeight","width","height","hugeFramesSent","toFixed","keyFramesEncoded","packetsSent","totalKeyFramesEncoded","totalNackCount","nackCount","totalPliCount","pliCount","totalFirCount","firCount","encoderImplementation","qualityLimitationReason","qualityLimitationResolutionChanges","retransmittedPacketsSent","totalBytesSent","headerBytesSent","retransmittedBytesSent","receiveSlot","ssrc","idAndCsi","framesReceived","bytes","totalBytesReceived","currentPacketsLost","packetsLost","totalPacketsLost","packetsReceived","totalPacketsReceived","currentPacketLossRatio","lastPacketReceivedTimestamp","framesDecoded","keyFramesDecoded","decoderImplementation","fecPacketsDiscarded","fecPacketsReceived","headerBytesReceived","jitter","totalSamplesReceived","totalSamplesDecoded","concealedSamples","currentPacketLoss","totalPacketsLostOnReceiver","packetsLostOnReceiver","roundTripTime","reportsReceived","overAllPacketLossRatio","determineUplinkNetworkQuality","remoteRtpResults","statsAnalyzerCurrentStats","EventsScope"],"sources":["index.ts"],"sourcesContent":["/* eslint-disable prefer-destructuring */\n\nimport {cloneDeep, isEmpty} from 'lodash';\nimport {ConnectionState} from '@webex/internal-media-core';\n\nimport EventsScope from '../common/events/events-scope';\nimport {\n DEFAULT_GET_STATS_FILTER,\n STATS,\n MQA_INTEVAL,\n NETWORK_TYPE,\n MEDIA_DEVICES,\n _UNKNOWN_,\n} from '../constants';\nimport {\n emptyAudioReceive,\n emptyAudioTransmit,\n emptyMqaInterval,\n emptyVideoReceive,\n emptyVideoTransmit,\n} from '../mediaQualityMetrics/config';\nimport LoggerProxy from '../common/logs/logger-proxy';\n\nimport defaultStats from './global';\nimport {\n getAudioSenderMqa,\n getAudioReceiverMqa,\n getVideoSenderMqa,\n getVideoReceiverMqa,\n} from './mqaUtil';\nimport {ReceiveSlot} from '../multistream/receiveSlot';\n\nexport const EVENTS = {\n MEDIA_QUALITY: 'MEDIA_QUALITY',\n LOCAL_MEDIA_STARTED: 'LOCAL_MEDIA_STARTED',\n LOCAL_MEDIA_STOPPED: 'LOCAL_MEDIA_STOPPED',\n REMOTE_MEDIA_STARTED: 'REMOTE_MEDIA_STARTED',\n REMOTE_MEDIA_STOPPED: 'REMOTE_MEDIA_STOPPED',\n};\n\nconst emptySender = {\n trackLabel: '',\n maxPacketLossRatio: 0,\n availableBandwidth: 0,\n bytesSent: 0,\n meanRemoteJitter: [],\n meanRoundTripTime: [],\n};\n\nconst emptyReceiver = {\n availableBandwidth: 0,\n bytesReceived: 0,\n meanRtpJitter: [],\n meanRoundTripTime: [],\n};\n\ntype ReceiveSlotCallback = (csi: number) => ReceiveSlot | undefined;\n\n/**\n * Stats Analyzer class that will emit events based on detected quality\n *\n * @export\n * @class StatsAnalyzer\n * @extends {EventsScope}\n */\nexport class StatsAnalyzer extends EventsScope {\n config: any;\n correlationId: any;\n lastEmittedStartStopEvent: any;\n lastMqaDataSent: any;\n lastStatsResults: any;\n meetingMediaStatus: any;\n mqaInterval: NodeJS.Timeout;\n mqaSentCount: any;\n networkQualityMonitor: any;\n mediaConnection: any;\n statsInterval: NodeJS.Timeout;\n statsResults: any;\n statsStarted: any;\n successfulCandidatePair: any;\n localIpAddress: string; // Returns the local IP address for diagnostics. this is the local IP of the interface used for the current media connection a host can have many local Ip Addresses\n receiveSlotCallback: ReceiveSlotCallback;\n\n /**\n * Creates a new instance of StatsAnalyzer\n * @constructor\n * @public\n * @param {Object} config SDK Configuration Object\n * @param {Function} receiveSlotCallback Callback used to access receive slots.\n * @param {Object} networkQualityMonitor class for assessing network characteristics (jitter, packetLoss, latency)\n * @param {Object} statsResults Default properties for stats\n */\n constructor(\n config: any,\n receiveSlotCallback: ReceiveSlotCallback = () => undefined,\n networkQualityMonitor: object = {},\n statsResults: object = defaultStats\n ) {\n super();\n this.statsStarted = false;\n this.statsResults = statsResults;\n this.lastStatsResults = null;\n this.config = config;\n this.networkQualityMonitor = networkQualityMonitor;\n this.correlationId = config.correlationId;\n this.mqaSentCount = -1;\n this.lastMqaDataSent = {};\n this.lastEmittedStartStopEvent = {};\n this.receiveSlotCallback = receiveSlotCallback;\n this.successfulCandidatePair = {};\n this.localIpAddress = '';\n }\n\n /**\n * Resets cumulative stats arrays.\n *\n * @public\n * @memberof StatsAnalyzer\n * @returns {void}\n */\n resetStatsResults() {\n Object.keys(this.statsResults).forEach((mediaType) => {\n if (mediaType.includes('recv')) {\n this.statsResults[mediaType].recv.meanRtpJitter = [];\n }\n\n if (mediaType.includes('send')) {\n this.statsResults[mediaType].send.meanRemoteJitter = [];\n this.statsResults[mediaType].send.meanRoundTripTime = [];\n }\n });\n }\n\n /**\n * sets mediaStatus status for analyzing metrics\n *\n * @public\n * @param {Object} status for the audio and video\n * @memberof StatsAnalyzer\n * @returns {void}\n */\n public updateMediaStatus(status: object) {\n this.meetingMediaStatus = status;\n }\n\n /**\n * captures MQA data from media connection\n *\n * @public\n * @memberof StatsAnalyzer\n * @returns {void}\n */\n sendMqaData() {\n const newMqa = cloneDeep(emptyMqaInterval);\n\n Object.keys(this.statsResults).forEach((mediaType) => {\n if (!this.lastMqaDataSent[mediaType]) {\n this.lastMqaDataSent[mediaType] = {};\n }\n\n if (!this.lastMqaDataSent[mediaType].send && mediaType.includes('-send')) {\n this.lastMqaDataSent[mediaType].send = {};\n }\n\n if (!this.lastMqaDataSent[mediaType].recv && mediaType.includes('-recv')) {\n this.lastMqaDataSent[mediaType].recv = {};\n }\n\n if (mediaType.includes('audio-send') || mediaType.includes('audio-share-send')) {\n const audioSender = cloneDeep(emptyAudioTransmit);\n\n getAudioSenderMqa({\n audioSender,\n statsResults: this.statsResults,\n lastMqaDataSent: this.lastMqaDataSent,\n mediaType,\n });\n newMqa.audioTransmit.push(audioSender);\n\n this.lastMqaDataSent[mediaType].send = cloneDeep(this.statsResults[mediaType].send);\n } else if (mediaType.includes('audio-recv') || mediaType.includes('audio-share-recv')) {\n const audioReceiver = cloneDeep(emptyAudioReceive);\n\n getAudioReceiverMqa({\n audioReceiver,\n statsResults: this.statsResults,\n lastMqaDataSent: this.lastMqaDataSent,\n mediaType,\n });\n newMqa.audioReceive.push(audioReceiver);\n\n this.lastMqaDataSent[mediaType].recv = cloneDeep(this.statsResults[mediaType].recv);\n } else if (mediaType.includes('video-send') || mediaType.includes('video-share-send')) {\n const videoSender = cloneDeep(emptyVideoTransmit);\n\n getVideoSenderMqa({\n videoSender,\n statsResults: this.statsResults,\n lastMqaDataSent: this.lastMqaDataSent,\n mediaType,\n });\n newMqa.videoTransmit.push(videoSender);\n\n this.lastMqaDataSent[mediaType].send = cloneDeep(this.statsResults[mediaType].send);\n } else if (mediaType.includes('video-recv') || mediaType.includes('video-share-recv')) {\n const videoReceiver = cloneDeep(emptyVideoReceive);\n\n getVideoReceiverMqa({\n videoReceiver,\n statsResults: this.statsResults,\n lastMqaDataSent: this.lastMqaDataSent,\n mediaType,\n });\n newMqa.videoReceive.push(videoReceiver);\n\n this.lastMqaDataSent[mediaType].recv = cloneDeep(this.statsResults[mediaType].recv);\n }\n });\n\n newMqa.intervalMetadata.peerReflexiveIP = this.statsResults.connectionType.local.ipAddress;\n\n // Adding peripheral information\n newMqa.intervalMetadata.peripherals.push({information: _UNKNOWN_, name: MEDIA_DEVICES.SPEAKER});\n if (this.statsResults['audio-send']) {\n newMqa.intervalMetadata.peripherals.push({\n information: this.statsResults['audio-send'].trackLabel || _UNKNOWN_,\n name: MEDIA_DEVICES.MICROPHONE,\n });\n }\n if (this.statsResults['video-send']) {\n newMqa.intervalMetadata.peripherals.push({\n information: this.statsResults['video-send'].trackLabel || _UNKNOWN_,\n name: MEDIA_DEVICES.CAMERA,\n });\n }\n\n newMqa.networkType = this.statsResults.connectionType.local.networkType;\n\n this.mqaSentCount += 1;\n\n newMqa.intervalNumber = this.mqaSentCount;\n\n this.resetStatsResults();\n\n this.emit(\n {\n file: 'statsAnalyzer',\n function: 'sendMqaData',\n },\n EVENTS.MEDIA_QUALITY,\n {\n data: newMqa,\n // @ts-ignore\n networkType: newMqa.networkType,\n }\n );\n }\n\n /**\n * updated the media connection when changed\n *\n * @private\n * @memberof StatsAnalyzer\n * @param {RoapMediaConnection} mediaConnection\n * @returns {void}\n */\n updateMediaConnection(mediaConnection: any) {\n this.mediaConnection = mediaConnection;\n }\n\n /**\n * Returns the local IP address for diagnostics.\n * this is the local IP of the interface used for the current media connection\n * a host can have many local Ip Addresses\n * @returns {string | undefined} The local IP address.\n */\n getLocalIpAddress(): string {\n return this.localIpAddress;\n }\n\n /**\n * Starts the stats analyzer on interval\n *\n * @public\n * @memberof StatsAnalyzer\n * @param {RoapMediaConnection} mediaConnection\n * @returns {Promise}\n */\n public startAnalyzer(mediaConnection: any) {\n if (!this.statsStarted) {\n this.statsStarted = true;\n this.mediaConnection = mediaConnection;\n\n return this.getStatsAndParse().then(() => {\n this.statsInterval = setInterval(() => {\n this.getStatsAndParse();\n }, this.config.analyzerInterval);\n // Trigger initial fetch\n this.sendMqaData();\n this.mqaInterval = setInterval(() => {\n this.sendMqaData();\n }, MQA_INTEVAL);\n });\n }\n\n return Promise.resolve();\n }\n\n /**\n * Cleans up the analyzer when done\n *\n * @public\n * @memberof StatsAnalyzer\n * @returns {void}\n */\n public stopAnalyzer() {\n const sendOneLastMqa = this.mqaInterval && this.statsInterval;\n\n if (this.statsInterval) {\n clearInterval(this.statsInterval);\n this.statsInterval = undefined;\n }\n\n if (this.mqaInterval) {\n clearInterval(this.mqaInterval);\n this.mqaInterval = undefined;\n }\n\n if (sendOneLastMqa) {\n return this.getStatsAndParse().then(() => {\n this.sendMqaData();\n this.mediaConnection = null;\n });\n }\n\n return Promise.resolve();\n }\n\n /**\n * Parse a single result of get stats\n *\n * @private\n * @param {*} getStatsResult\n * @param {String} type\n * @param {boolean} isSender\n * @returns {void}\n * @memberof StatsAnalyzer\n */\n private parseGetStatsResult(getStatsResult: any, type: string, isSender: boolean) {\n if (!getStatsResult) {\n return;\n }\n\n // Generate empty stats results\n if (!this.statsResults[type]) {\n this.statsResults[type] = {};\n }\n\n if (isSender && !this.statsResults[type].send) {\n this.statsResults[type].send = cloneDeep(emptySender);\n } else if (!isSender && !this.statsResults[type].recv) {\n this.statsResults[type].recv = cloneDeep(emptyReceiver);\n }\n\n switch (getStatsResult.type) {\n case 'outbound-rtp':\n this.processOutboundRTPResult(getStatsResult, type);\n break;\n case 'inbound-rtp':\n this.processInboundRTPResult(getStatsResult, type);\n break;\n case 'remote-inbound-rtp':\n case 'remote-outbound-rtp':\n this.compareSentAndReceived(getStatsResult, type);\n break;\n case 'remotecandidate':\n case 'remote-candidate':\n this.parseCandidate(getStatsResult, type, isSender, true);\n break;\n case 'local-candidate':\n this.parseCandidate(getStatsResult, type, isSender, false);\n break;\n case 'media-source':\n // @ts-ignore\n this.parseAudioSource(getStatsResult, type);\n break;\n default:\n break;\n }\n }\n\n /**\n * Filters the get stats results for types\n * @private\n * @param {Array} statsItem\n * @param {String} type\n * @param {boolean} isSender\n * @returns {void}\n */\n filterAndParseGetStatsResults(statsItem: any, type: string, isSender: boolean) {\n const {types} = DEFAULT_GET_STATS_FILTER;\n\n // get the successful candidate pair before parsing stats.\n statsItem.report.forEach((report) => {\n if (report.type === 'candidate-pair' && report.state === 'succeeded') {\n this.successfulCandidatePair = report;\n }\n });\n\n statsItem.report.forEach((result) => {\n if (types.includes(result.type)) {\n this.parseGetStatsResult(result, type, isSender);\n }\n });\n\n if (this.statsResults[type]) {\n this.statsResults[type].direction = statsItem.currentDirection;\n this.statsResults[type].trackLabel = statsItem.localTrackLabel;\n this.statsResults[type].csi = statsItem.csi;\n this.extractAndSetLocalIpAddressInfoForDiagnostics(\n this.successfulCandidatePair?.localCandidateId,\n this.statsResults?.candidates\n );\n // reset the successful candidate pair.\n this.successfulCandidatePair = {};\n }\n }\n\n /**\n * parse the audio\n * @param {String} result\n * @param {boolean} type\n * @returns {void}\n */\n parseAudioSource(result: any, type: any) {\n if (!result) {\n return;\n }\n\n if (type.includes('audio-send')) {\n this.statsResults[type].send.audioLevel = result.audioLevel;\n this.statsResults[type].send.totalAudioEnergy = result.totalAudioEnergy;\n }\n }\n\n /**\n * emits started/stopped events for local/remote media by checking\n * if given values are increasing or not. The previousValue, currentValue\n * params can be any numerical value like number of receive packets or\n * decoded frames, etc.\n *\n * @private\n * @param {string} mediaType\n * @param {number} previousValue - value to compare\n * @param {number} currentValue - value to compare (must be same type of value as previousValue)\n * @param {boolean} isLocal - true if stats are for local media being sent out, false for remote media being received\n * @memberof StatsAnalyzer\n * @returns {void}\n */\n emitStartStopEvents = (\n mediaType: string,\n previousValue: number,\n currentValue: number,\n isLocal: boolean\n ) => {\n if (mediaType !== 'audio' && mediaType !== 'video' && mediaType !== 'share') {\n throw new Error(`Unsupported mediaType: ${mediaType}`);\n }\n\n // eslint-disable-next-line no-param-reassign\n if (previousValue === undefined) previousValue = 0;\n // eslint-disable-next-line no-param-reassign\n if (currentValue === undefined) currentValue = 0;\n\n if (!this.lastEmittedStartStopEvent[mediaType]) {\n this.lastEmittedStartStopEvent[mediaType] = {};\n }\n\n const lastEmittedEvent = isLocal\n ? this.lastEmittedStartStopEvent[mediaType].local\n : this.lastEmittedStartStopEvent[mediaType].remote;\n\n let newEvent;\n\n if (currentValue - previousValue > 0) {\n newEvent = isLocal ? EVENTS.LOCAL_MEDIA_STARTED : EVENTS.REMOTE_MEDIA_STARTED;\n } else if (currentValue === previousValue && currentValue > 0) {\n newEvent = isLocal ? EVENTS.LOCAL_MEDIA_STOPPED : EVENTS.REMOTE_MEDIA_STOPPED;\n }\n\n if (newEvent && lastEmittedEvent !== newEvent) {\n if (isLocal) {\n this.lastEmittedStartStopEvent[mediaType].local = newEvent;\n } else {\n this.lastEmittedStartStopEvent[mediaType].remote = newEvent;\n }\n this.emit(\n {\n file: 'statsAnalyzer/index',\n function: 'compareLastStatsResult',\n },\n newEvent,\n {\n type: mediaType,\n }\n );\n }\n };\n\n /**\n * compares current and previous stats to check if packets are not sent\n *\n * @private\n * @memberof StatsAnalyzer\n * @returns {void}\n */\n private compareLastStatsResult() {\n if (this.lastStatsResults !== null && this.meetingMediaStatus) {\n const getCurrentStatsTotals = (keyPrefix: string, value: string): number =>\n Object.keys(this.statsResults)\n .filter((key) => key.startsWith(keyPrefix))\n .reduce((prev, cur) => prev + (this.statsResults[cur]?.recv[value] || 0), 0);\n\n const getPreviousStatsTotals = (keyPrefix: string, value: string): number =>\n Object.keys(this.statsResults)\n .filter((key) => key.startsWith(keyPrefix))\n .reduce((prev, cur) => prev + (this.lastStatsResults[cur]?.recv[value] || 0), 0);\n\n if (this.meetingMediaStatus.expected.sendAudio && this.lastStatsResults['audio-send']) {\n // compare audio stats sent\n // NOTE: relies on there being only one sender.\n const currentStats = this.statsResults['audio-send'].send;\n const previousStats = this.lastStatsResults['audio-send'].send;\n\n if (\n currentStats.totalPacketsSent === previousStats.totalPacketsSent ||\n currentStats.totalPacketsSent === 0\n ) {\n LoggerProxy.logger.info(\n `StatsAnalyzer:index#compareLastStatsResult --> No audio RTP packets sent`,\n currentStats.totalPacketsSent\n );\n } else {\n if (\n currentStats.totalAudioEnergy === previousStats.totalAudioEnergy ||\n currentStats.totalAudioEnergy === 0\n ) {\n LoggerProxy.logger.info(\n `StatsAnalyzer:index#compareLastStatsResult --> No audio Energy present`,\n currentStats.totalAudioEnergy\n );\n }\n\n if (currentStats.audioLevel === 0) {\n LoggerProxy.logger.info(\n `StatsAnalyzer:index#compareLastStatsResult --> audio level is 0 for the user`\n );\n }\n }\n\n this.emitStartStopEvents(\n 'audio',\n previousStats.totalPacketsSent,\n currentStats.totalPacketsSent,\n true\n );\n }\n\n if (this.meetingMediaStatus.expected.receiveAudio) {\n // compare audio stats received\n const currentPacketsReceived = getCurrentStatsTotals('audio-recv', 'totalPacketsReceived');\n const previousPacketsReceived = getPreviousStatsTotals(\n 'audio-recv',\n 'totalPacketsReceived'\n );\n const currentSamplesReceived = getCurrentStatsTotals('audio-recv', 'totalSamplesReceived');\n const previousSamplesReceived = getPreviousStatsTotals(\n 'audio-recv',\n 'totalSamplesReceived'\n );\n\n if (currentPacketsReceived === previousPacketsReceived || currentPacketsReceived === 0) {\n LoggerProxy.logger.info(\n `StatsAnalyzer:index#compareLastStatsResult --> No audio RTP packets received`,\n currentPacketsReceived\n );\n } else if (\n currentSamplesReceived === previousSamplesReceived ||\n currentSamplesReceived === 0\n ) {\n LoggerProxy.logger.info(\n `StatsAnalyzer:index#compareLastStatsResult --> No audio samples received`,\n currentSamplesReceived\n );\n }\n\n this.emitStartStopEvents('audio', previousPacketsReceived, currentPacketsReceived, false);\n }\n\n if (this.meetingMediaStatus.expected.sendVideo && this.lastStatsResults['video-send']) {\n // compare video stats sent\n const currentStats = this.statsResults['video-send'].send;\n const previousStats = this.lastStatsResults['video-send'].send;\n\n if (\n currentStats.totalPacketsSent === previousStats.totalPacketsSent ||\n currentStats.totalPacketsSent === 0\n ) {\n LoggerProxy.logger.info(\n `StatsAnalyzer:index#compareLastStatsResult --> No video RTP packets sent`,\n currentStats.totalPacketsSent\n );\n } else {\n if (\n currentStats.framesEncoded === previousStats.framesEncoded ||\n currentStats.framesEncoded === 0\n ) {\n LoggerProxy.logger.info(\n `StatsAnalyzer:index#compareLastStatsResult --> No video Frames Encoded`,\n currentStats.framesEncoded\n );\n }\n\n if (\n this.statsResults['video-send'].send.framesSent ===\n this.lastStatsResults['video-send'].send.framesSent ||\n this.statsResults['video-send'].send.framesSent === 0\n ) {\n LoggerProxy.logger.info(\n `StatsAnalyzer:index#compareLastStatsResult --> No video Frames sent`,\n this.statsResults['video-send'].send.framesSent\n );\n }\n }\n\n this.emitStartStopEvents('video', previousStats.framesSent, currentStats.framesSent, true);\n }\n\n if (this.meetingMediaStatus.expected.receiveVideo) {\n // compare video stats received\n const currentPacketsReceived = getCurrentStatsTotals('video-recv', 'totalPacketsReceived');\n const previousPacketsReceived = getPreviousStatsTotals(\n 'video-recv',\n 'totalPacketsReceived'\n );\n const currentFramesReceived = getCurrentStatsTotals('video-recv', 'framesReceived');\n const previousFramesReceived = getPreviousStatsTotals('video-recv', 'framesReceived');\n const currentFramesDecoded = getCurrentStatsTotals('video-recv', 'framesDecoded');\n const previousFramesDecoded = getPreviousStatsTotals('video-recv', 'framesDecoded');\n const currentFramesDropped = getCurrentStatsTotals('video-recv', 'framesDropped');\n const previousFramesDropped = getPreviousStatsTotals('video-recv', 'framesDropped');\n\n if (currentPacketsReceived === previousPacketsReceived || currentPacketsReceived === 0) {\n LoggerProxy.logger.info(\n `StatsAnalyzer:index#compareLastStatsResult --> No video RTP packets received`,\n currentPacketsReceived\n );\n } else {\n if (currentFramesReceived === previousFramesReceived || currentFramesReceived === 0) {\n LoggerProxy.logger.info(\n `StatsAnalyzer:index#compareLastStatsResult --> No video frames received`,\n currentFramesReceived\n );\n }\n\n if (currentFramesDecoded === previousFramesDecoded || currentFramesDecoded === 0) {\n LoggerProxy.logger.info(\n `StatsAnalyzer:index#compareLastStatsResult --> No video frames decoded`,\n currentFramesDecoded\n );\n }\n\n if (currentFramesDropped - previousFramesDropped > 10) {\n LoggerProxy.logger.info(\n `StatsAnalyzer:index#compareLastStatsResult --> video frames are getting dropped`,\n currentFramesDropped - previousFramesDropped\n );\n }\n }\n\n this.emitStartStopEvents('video', previousFramesDecoded, currentFramesDecoded, false);\n }\n\n if (this.meetingMediaStatus.expected.sendShare && this.lastStatsResults['video-share-send']) {\n // compare share stats sent\n\n const currentStats = this.statsResults['video-share-send'].send;\n const previousStats = this.lastStatsResults['video-share-send'].send;\n\n if (\n currentStats.totalPacketsSent === previousStats.totalPacketsSent ||\n currentStats.totalPacketsSent === 0\n ) {\n LoggerProxy.logger.info(\n `StatsAnalyzer:index#compareLastStatsResult --> No share RTP packets sent`,\n currentStats.totalPacketsSent\n );\n } else {\n if (\n currentStats.framesEncoded === previousStats.framesEncoded ||\n currentStats.framesEncoded === 0\n ) {\n LoggerProxy.logger.info(\n `StatsAnalyzer:index#compareLastStatsResult --> No share frames getting encoded`,\n currentStats.framesEncoded\n );\n }\n\n if (\n this.statsResults['video-share-send'].send.framesSent ===\n this.lastStatsResults['video-share-send'].send.framesSent ||\n this.statsResults['video-share-send'].send.framesSent === 0\n ) {\n LoggerProxy.logger.info(\n `StatsAnalyzer:index#compareLastStatsResult --> No share frames sent`,\n this.statsResults['video-share-send'].send.framesSent\n );\n }\n }\n }\n\n if (this.meetingMediaStatus.expected.sendShare) {\n // TODO:need to check receive share value\n // compare share stats received\n const currentPacketsReceived = getCurrentStatsTotals(\n 'video-share-recv',\n 'totalPacketsReceived'\n );\n const previousPacketsReceived = getPreviousStatsTotals(\n 'video-share-recv',\n 'totalPacketsReceived'\n );\n const currentFramesReceived = getCurrentStatsTotals('video-share-recv', 'framesReceived');\n const previousFramesReceived = getPreviousStatsTotals('video-share-recv', 'framesReceived');\n const currentFramesDecoded = getCurrentStatsTotals('video-share-recv', 'framesDecoded');\n const previousFramesDecoded = getPreviousStatsTotals('video-share-recv', 'framesDecoded');\n const currentFramesDropped = getCurrentStatsTotals('video-share-recv', 'framesDropped');\n const previousFramesDropped = getPreviousStatsTotals('video-share-recv', 'framesDropped');\n\n if (currentPacketsReceived === previousPacketsReceived || currentPacketsReceived === 0) {\n LoggerProxy.logger.info(\n `StatsAnalyzer:index#compareLastStatsResult --> No share RTP packets received`,\n currentPacketsReceived\n );\n } else {\n if (currentFramesReceived === previousFramesReceived || currentFramesReceived === 0) {\n LoggerProxy.logger.info(\n `StatsAnalyzer:index#compareLastStatsResult --> No share frames received`,\n currentFramesReceived\n );\n }\n\n if (currentFramesDecoded === previousFramesDecoded || currentFramesDecoded === 0) {\n LoggerProxy.logger.info(\n `StatsAnalyzer:index#compareLastStatsResult --> No share frames decoded`,\n currentFramesDecoded\n );\n }\n\n if (currentFramesDropped - previousFramesDropped > 10) {\n LoggerProxy.logger.info(\n `StatsAnalyzer:index#compareLastStatsResult --> share frames are getting dropped`,\n currentFramesDropped - previousFramesDropped\n );\n }\n }\n\n // we are not calling emitStartStopEvents() for sending or receiving share because sharing is often started and stopped\n // in meetings and this.meetingMediaStatus.expected values can be out of sync with the actual packet flow\n // so we would send \"sharing stopped\" events incorrectly\n }\n }\n }\n\n /**\n * Does a `getStats` on all the transceivers and parses the results\n *\n * @private\n * @memberof StatsAnalyzer\n * @returns {Promise}\n */\n private getStatsAndParse() {\n if (!this.mediaConnection) {\n return Promise.resolve();\n }\n\n if (\n this.mediaConnection &&\n this.mediaConnection.getConnectionState() === ConnectionState.Failed\n ) {\n LoggerProxy.logger.trace(\n 'StatsAnalyzer:index#getStatsAndParse --> media connection is in failed state'\n );\n\n return Promise.resolve();\n }\n\n LoggerProxy.logger.trace('StatsAnalyzer:index#getStatsAndParse --> Collecting Stats');\n\n return this.mediaConnection.getTransceiverStats().then((transceiverStats) => {\n transceiverStats.video.receivers.forEach((receiver, i) =>\n this.filterAndParseGetStatsResults(receiver, `video-recv-${i}`, false)\n );\n transceiverStats.audio.receivers.forEach((receiver, i) =>\n this.filterAndParseGetStatsResults(receiver, `audio-recv-${i}`, false)\n );\n transceiverStats.screenShareVideo.receivers.forEach((receiver, i) =>\n this.filterAndParseGetStatsResults(receiver, `video-share-recv-${i}`, false)\n );\n transceiverStats.screenShareAudio.receivers.forEach((receiver, i) =>\n this.filterAndParseGetStatsResults(receiver, `audio-share-recv-${i}`, false)\n );\n\n transceiverStats.video.senders.forEach((sender, i) => {\n if (i > 0) {\n throw new Error('Stats Analyzer does not support multiple senders.');\n }\n this.filterAndParseGetStatsResults(sender, 'video-send', true);\n });\n transceiverStats.audio.senders.forEach((sender, i) => {\n if (i > 0) {\n throw new Error('Stats Analyzer does not support multiple senders.');\n }\n this.filterAndParseGetStatsResults(sender, 'audio-send', true);\n });\n transceiverStats.screenShareVideo.senders.forEach((sender, i) => {\n if (i > 0) {\n throw new Error('Stats Analyzer does not support multiple senders.');\n }\n this.filterAndParseGetStatsResults(sender, 'video-share-send', true);\n });\n transceiverStats.screenShareAudio.senders.forEach((sender, i) => {\n if (i > 0) {\n throw new Error('Stats Analyzer does not support multiple senders.');\n }\n this.filterAndParseGetStatsResults(sender, 'audio-share-send', true);\n });\n\n this.compareLastStatsResult();\n\n // Save the last results to compare with the current\n // DO Deep copy, for some reason it takes the reference all the time rather then old value set\n this.lastStatsResults = JSON.parse(JSON.stringify(this.statsResults));\n\n LoggerProxy.logger.trace(\n 'StatsAnalyzer:index#getStatsAndParse --> Finished Collecting Stats'\n );\n });\n }\n\n /**\n * Processes OutboundRTP stats result and stores\n * @private\n * @param {*} result\n * @param {*} mediaType\n * @returns {void}\n */\n private processOutboundRTPResult(result: any, mediaType: any) {\n const sendrecvType = STATS.SEND_DIRECTION;\n\n if (result.bytesSent) {\n const kilobytes = 0;\n\n if (result.frameWidth && result.frameHeight) {\n this.statsResults[mediaType][sendrecvType].width = result.frameWidth;\n this.statsResults[mediaType][sendrecvType].height = result.frameHeight;\n this.statsResults[mediaType][sendrecvType].framesSent = result.framesSent;\n this.statsResults[mediaType][sendrecvType].hugeFramesSent = result.hugeFramesSent;\n }\n\n this.statsResults[mediaType][sendrecvType].availableBandwidth = kilobytes.toFixed(1);\n\n this.statsResults[mediaType][sendrecvType].framesEncoded = result.framesEncoded;\n this.statsResults[mediaType][sendrecvType].keyFramesEncoded = result.keyFramesEncoded;\n this.statsResults[mediaType][sendrecvType].packetsSent = result.packetsSent;\n\n // Data saved to send MQA metrics\n\n this.statsResults[mediaType][sendrecvType].totalKeyFramesEncoded = result.keyFramesEncoded;\n this.statsResults[mediaType][sendrecvType].totalNackCount = result.nackCount;\n this.statsResults[mediaType][sendrecvType].totalPliCount = result.pliCount;\n this.statsResults[mediaType][sendrecvType].totalPacketsSent = result.packetsSent;\n this.statsResults[mediaType][sendrecvType].totalFirCount = result.firCount;\n this.statsResults[mediaType][sendrecvType].framesSent = result.framesSent;\n this.statsResults[mediaType][sendrecvType].framesEncoded = result.framesEncoded;\n this.statsResults[mediaType][sendrecvType].encoderImplementation =\n result.encoderImplementation;\n this.statsResults[mediaType][sendrecvType].qualityLimitationReason =\n result.qualityLimitationReason;\n this.statsResults[mediaType][sendrecvType].qualityLimitationResolutionChanges =\n result.qualityLimitationResolutionChanges;\n this.statsResults[mediaType][sendrecvType].retransmittedPacketsSent =\n result.retransmittedPacketsSent;\n this.statsResults[mediaType][sendrecvType].totalBytesSent = result.bytesSent;\n this.statsResults[mediaType][sendrecvType].headerBytesSent = result.headerBytesSent;\n this.statsResults[mediaType][sendrecvType].retransmittedBytesSent =\n result.retransmittedBytesSent;\n }\n }\n\n /**\n * Processes InboundRTP stats result and stores\n * @private\n * @param {*} result\n * @param {*} mediaType\n * @returns {void}\n */\n private processInboundRTPResult(result: any, mediaType: any) {\n const sendrecvType = STATS.RECEIVE_DIRECTION;\n\n if (result.bytesReceived) {\n let kilobytes = 0;\n const receiveSlot = this.receiveSlotCallback(result.ssrc);\n const idAndCsi = receiveSlot\n ? `id: \"${receiveSlot.id || ''}\"${receiveSlot.csi ? ` and csi: ${receiveSlot.csi}` : ''}`\n : '';\n\n if (result.frameWidth && result.frameHeight) {\n this.statsResults[mediaType][sendrecvType].width = result.frameWidth;\n this.statsResults[mediaType][sendrecvType].height = result.frameHeight;\n this.statsResults[mediaType][sendrecvType].framesReceived = result.framesReceived;\n }\n\n const bytes =\n result.bytesReceived - this.statsResults[mediaType][sendrecvType].totalBytesReceived;\n\n kilobytes = bytes / 1024;\n this.statsResults[mediaType][sendrecvType].availableBandwidth = kilobytes.toFixed(1);\n\n let currentPacketsLost =\n result.packetsLost - this.statsResults[mediaType][sendrecvType].totalPacketsLost;\n if (currentPacketsLost < 0) {\n currentPacketsLost = 0;\n }\n\n const currentPacketsReceived =\n result.packetsReceived - this.statsResults[mediaType][sendrecvType].totalPacketsReceived;\n this.statsResults[mediaType][sendrecvType].totalPacketsReceived = result.packetsReceived;\n\n if (currentPacketsReceived === 0) {\n if (receiveSlot) {\n LoggerProxy.logger.info(\n `StatsAnalyzer:index#processInboundRTPResult --> No packets received for receive slot ${idAndCsi}`,\n currentPacketsReceived\n );\n }\n }\n\n // Check the over all packet Lost ratio\n this.statsResults[mediaType][sendrecvType].currentPacketLossRatio =\n currentPacketsLost > 0\n ? currentPacketsLost / (currentPacketsReceived + currentPacketsLost)\n : 0;\n if (this.statsResults[mediaType][sendrecvType].currentPacketLossRatio > 3) {\n LoggerProxy.logger.info(\n `StatsAnalyzer:index#processInboundRTPResult --> Packets getting lost from the receiver with slot ${idAndCsi}`,\n this.statsResults[mediaType][sendrecvType].currentPacketLossRatio\n );\n }\n\n // TODO: check the packet loss value is negative values here\n\n if (result.packetsLost) {\n this.statsResults[mediaType][sendrecvType].totalPacketsLost =\n result.packetsLost > 0 ? result.packetsLost : -result.packetsLost;\n } else {\n this.statsResults[mediaType][sendrecvType].totalPacketsLost = 0;\n }\n\n this.statsResults[mediaType][sendrecvType].lastPacketReceivedTimestamp =\n result.lastPacketReceivedTimestamp;\n\n // From Thin\n this.statsResults[mediaType][sendrecvType].totalNackCount = result.nackCount;\n this.statsResults[mediaType][sendrecvType].totalPliCount = result.pliCount;\n this.statsResults[mediaType][sendrecvType].framesDecoded = result.framesDecoded;\n this.statsResults[mediaType][sendrecvType].keyFramesDecoded = result.keyFramesDecoded;\n\n this.statsResults[mediaType][sendrecvType].decoderImplementation =\n result.decoderImplementation;\n this.statsResults[mediaType][sendrecvType].totalPacketsReceived = result.packetsReceived;\n\n this.statsResults[mediaType][sendrecvType].fecPacketsDiscarded = result.fecPacketsDiscarded;\n this.statsResults[mediaType][sendrecvType].fecPacketsReceived = result.fecPacketsReceived;\n this.statsResults[mediaType][sendrecvType].totalBytesReceived = result.bytesReceived;\n this.statsResults[mediaType][sendrecvType].headerBytesReceived = result.headerBytesReceived;\n\n this.statsResults[mediaType][sendrecvType].meanRtpJitter.push(result.jitter);\n\n // Audio stats\n\n this.statsResults[mediaType][sendrecvType].audioLevel = result.audioLevel;\n this.statsResults[mediaType][sendrecvType].totalAudioEnergy = result.totalAudioEnergy;\n this.statsResults[mediaType][sendrecvType].totalSamplesReceived =\n result.totalSamplesReceived || 0;\n this.statsResults[mediaType][sendrecvType].totalSamplesDecoded =\n result.totalSamplesDecoded || 0;\n this.statsResults[mediaType][sendrecvType].concealedSamples = result.concealedSamples || 0;\n }\n }\n\n /**\n * extracts the local Ip address from the statsResult object by looking at stats results candidates\n * and matches that ID with the successful candidate pair. It looks at the type of local candidate it is\n * and then extracts the IP address from the relatedAddress or address property based on conditions known in webrtc\n * note, there are known incompatibilities and it is possible for this to set undefined, or for the IP address to be the public IP address\n * for example, firefox does not set the relayProtocol, and if the user is behind a NAT it might be the public IP\n * @private\n * @param {string} successfulCandidatePairId - The ID of the successful candidate pair.\n * @param {Object} candidates - the stats result candidates\n * @returns {void}\n */\n extractAndSetLocalIpAddressInfoForDiagnostics = (\n successfulCandidatePairId: string,\n candidates: {[key: string]: Record<string, unknown>}\n ) => {\n let newIpAddress = '';\n if (successfulCandidatePairId && !isEmpty(candidates)) {\n const localCandidate = candidates[successfulCandidatePairId];\n if (localCandidate) {\n if (localCandidate.candidateType === 'host') {\n // if it's a host candidate, use the address property - it will be the local IP\n newIpAddress = `${localCandidate.address}`;\n } else if (localCandidate.candidateType === 'prflx') {\n // if it's a peer reflexive candidate and we're not using a relay (there is no relayProtocol set)\n // then look at the relatedAddress - it will be the local\n //\n // Firefox doesn't populate the relayProtocol property\n if (!localCandidate.relayProtocol) {\n newIpAddress = `${localCandidate.relatedAddress}`;\n } else {\n // if it's a peer reflexive candidate and we are using a relay -\n // in that case the relatedAddress will be the IP of the TURN server (Linus),\n // so we can only look at the address, but it might be local IP or public IP,\n // depending on if the user is behind a NAT or not\n newIpAddress = `${localCandidate.address}`;\n }\n }\n }\n }\n this.localIpAddress = newIpAddress;\n };\n\n /**\n * Processes remote and local candidate result and stores\n * @private\n * @param {*} result\n * @param {*} type\n * @param {boolean} isSender\n * @param {boolean} isRemote\n *\n * @returns {void}\n */\n parseCandidate = (result: any, type: any, isSender: boolean, isRemote: boolean) => {\n if (!result || !result.id) {\n return;\n }\n\n // We only care about the successful local candidate\n if (this.successfulCandidatePair?.localCandidateId !== result.id) {\n return;\n }\n\n let transport;\n if (result.relayProtocol) {\n transport = result.relayProtocol.toUpperCase();\n } else if (result.protocol) {\n transport = result.protocol.toUpperCase();\n }\n\n const sendRecvType = isSender ? STATS.SEND_DIRECTION : STATS.RECEIVE_DIRECTION;\n const ipType = isRemote ? STATS.REMOTE : STATS.LOCAL;\n\n if (!this.statsResults.candidates) {\n this.statsResults.candidates = {};\n }\n\n this.statsResults.candidates[result.id] = {\n candidateType: result.candidateType,\n ipAddress: result.ip, // TODO: add ports\n relatedAddress: result.relatedAddress,\n relatedPort: result.relatedPort,\n relayProtocol: result.relayProtocol,\n protocol: result.protocol,\n address: result.address,\n portNumber: result.port,\n networkType: result.networkType,\n priority: result.priority,\n transport,\n timestamp: result.time,\n id: result.id,\n type: result.type,\n };\n\n this.statsResults.connectionType[ipType].candidateType = result.candidateType;\n this.statsResults.connectionType[ipType].ipAddress = result.ipAddress;\n\n this.statsResults.connectionType[ipType].networkType =\n result.networkType === NETWORK_TYPE.VPN ? NETWORK_TYPE.UNKNOWN : result.networkType;\n this.statsResults.connectionType[ipType].transport = transport;\n\n this.statsResults[type][sendRecvType].totalRoundTripTime = result.totalRoundTripTime;\n };\n\n /**\n *\n * @private\n * @param {*} result\n * @param {*} type\n * @returns {void}\n * @memberof StatsAnalyzer\n */\n compareSentAndReceived(result, type) {\n // Don't compare on transceivers without a sender.\n if (!type || !this.statsResults[type].send) {\n return;\n }\n\n const mediaType = type;\n\n const currentPacketLoss =\n result.packetsLost - this.statsResults[mediaType].send.totalPacketsLostOnReceiver;\n\n this.statsResults[mediaType].send.packetsLostOnReceiver = currentPacketLoss;\n this.statsResults[mediaType].send.totalPacketsLostOnReceiver = result.packetsLost;\n\n this.statsResults[mediaType].send.meanRemoteJitter.push(result.jitter);\n this.statsResults[mediaType].send.meanRoundTripTime.push(result.roundTripTime);\n\n this.statsResults[mediaType].send.timestamp = result.timestamp;\n this.statsResults[mediaType].send.ssrc = result.ssrc;\n this.statsResults[mediaType].send.reportsReceived = result.reportsReceived;\n\n // Total packloss ratio on this video section of the call\n this.statsResults[mediaType].send.overAllPacketLossRatio =\n this.statsResults[mediaType].send.totalPacketsLostOnReceiver > 0\n ? this.statsResults[mediaType].send.totalPacketsLostOnReceiver /\n this.statsResults[mediaType].send.totalPacketsSent\n : 0;\n this.statsResults[mediaType].send.currentPacketLossRatio =\n this.statsResults[mediaType].send.packetsLostOnReceiver > 0\n ? (this.statsResults[mediaType].send.packetsLostOnReceiver * 100) /\n (this.statsResults[mediaType].send.packetsSent +\n this.statsResults[mediaType].send.packetsLostOnReceiver)\n : 0;\n\n if (\n this.statsResults[mediaType].send.maxPacketLossRatio <\n this.statsResults[mediaType].send.currentPacketLossRatio\n ) {\n this.statsResults[mediaType].send.maxPacketLossRatio =\n this.statsResults[mediaType].send.currentPacketLossRatio;\n }\n\n if (result.type === 'remote-inbound-rtp') {\n this.networkQualityMonitor.determineUplinkNetworkQuality({\n mediaType,\n remoteRtpResults: result,\n statsAnalyzerCurrentStats: this.statsResults,\n });\n }\n }\n}\n"],"mappings":"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"}
|
|
@@ -31,6 +31,7 @@ export declare class StatsAnalyzer extends EventsScope {
|
|
|
31
31
|
statsResults: any;
|
|
32
32
|
statsStarted: any;
|
|
33
33
|
successfulCandidatePair: any;
|
|
34
|
+
localIpAddress: string;
|
|
34
35
|
receiveSlotCallback: ReceiveSlotCallback;
|
|
35
36
|
/**
|
|
36
37
|
* Creates a new instance of StatsAnalyzer
|
|
@@ -76,6 +77,13 @@ export declare class StatsAnalyzer extends EventsScope {
|
|
|
76
77
|
* @returns {void}
|
|
77
78
|
*/
|
|
78
79
|
updateMediaConnection(mediaConnection: any): void;
|
|
80
|
+
/**
|
|
81
|
+
* Returns the local IP address for diagnostics.
|
|
82
|
+
* this is the local IP of the interface used for the current media connection
|
|
83
|
+
* a host can have many local Ip Addresses
|
|
84
|
+
* @returns {string | undefined} The local IP address.
|
|
85
|
+
*/
|
|
86
|
+
getLocalIpAddress(): string;
|
|
79
87
|
/**
|
|
80
88
|
* Starts the stats analyzer on interval
|
|
81
89
|
*
|
|
@@ -167,6 +175,20 @@ export declare class StatsAnalyzer extends EventsScope {
|
|
|
167
175
|
* @returns {void}
|
|
168
176
|
*/
|
|
169
177
|
private processInboundRTPResult;
|
|
178
|
+
/**
|
|
179
|
+
* extracts the local Ip address from the statsResult object by looking at stats results candidates
|
|
180
|
+
* and matches that ID with the successful candidate pair. It looks at the type of local candidate it is
|
|
181
|
+
* and then extracts the IP address from the relatedAddress or address property based on conditions known in webrtc
|
|
182
|
+
* note, there are known incompatibilities and it is possible for this to set undefined, or for the IP address to be the public IP address
|
|
183
|
+
* for example, firefox does not set the relayProtocol, and if the user is behind a NAT it might be the public IP
|
|
184
|
+
* @private
|
|
185
|
+
* @param {string} successfulCandidatePairId - The ID of the successful candidate pair.
|
|
186
|
+
* @param {Object} candidates - the stats result candidates
|
|
187
|
+
* @returns {void}
|
|
188
|
+
*/
|
|
189
|
+
extractAndSetLocalIpAddressInfoForDiagnostics: (successfulCandidatePairId: string, candidates: {
|
|
190
|
+
[key: string]: Record<string, unknown>;
|
|
191
|
+
}) => void;
|
|
170
192
|
/**
|
|
171
193
|
* Processes remote and local candidate result and stores
|
|
172
194
|
* @private
|
package/dist/webinar/index.js
CHANGED
|
@@ -62,7 +62,7 @@ var Webinar = _webexCore.WebexPlugin.extend({
|
|
|
62
62
|
updateCanManageWebcast: function updateCanManageWebcast(canManageWebcast) {
|
|
63
63
|
this.set('canManageWebcast', canManageWebcast);
|
|
64
64
|
},
|
|
65
|
-
version: "3.0.0-beta.
|
|
65
|
+
version: "3.0.0-beta.340"
|
|
66
66
|
});
|
|
67
67
|
var _default = Webinar;
|
|
68
68
|
exports.default = _default;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webex/plugin-meetings",
|
|
3
|
-
"version": "3.0.0-beta.
|
|
3
|
+
"version": "3.0.0-beta.340",
|
|
4
4
|
"description": "",
|
|
5
5
|
"license": "Cisco EULA (https://www.cisco.com/c/en/us/products/end-user-license-agreement.html)",
|
|
6
6
|
"contributors": [
|
|
@@ -33,12 +33,12 @@
|
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
35
|
"@peculiar/webcrypto": "^1.4.3",
|
|
36
|
-
"@webex/plugin-meetings": "3.0.0-beta.
|
|
37
|
-
"@webex/test-helper-chai": "3.0.0-beta.
|
|
38
|
-
"@webex/test-helper-mocha": "3.0.0-beta.
|
|
39
|
-
"@webex/test-helper-mock-webex": "3.0.0-beta.
|
|
40
|
-
"@webex/test-helper-retry": "3.0.0-beta.
|
|
41
|
-
"@webex/test-helper-test-users": "3.0.0-beta.
|
|
36
|
+
"@webex/plugin-meetings": "3.0.0-beta.340",
|
|
37
|
+
"@webex/test-helper-chai": "3.0.0-beta.340",
|
|
38
|
+
"@webex/test-helper-mocha": "3.0.0-beta.340",
|
|
39
|
+
"@webex/test-helper-mock-webex": "3.0.0-beta.340",
|
|
40
|
+
"@webex/test-helper-retry": "3.0.0-beta.340",
|
|
41
|
+
"@webex/test-helper-test-users": "3.0.0-beta.340",
|
|
42
42
|
"chai": "^4.3.4",
|
|
43
43
|
"chai-as-promised": "^7.1.1",
|
|
44
44
|
"jsdom-global": "3.0.2",
|
|
@@ -47,19 +47,19 @@
|
|
|
47
47
|
"typescript": "^4.7.4"
|
|
48
48
|
},
|
|
49
49
|
"dependencies": {
|
|
50
|
-
"@webex/common": "3.0.0-beta.
|
|
50
|
+
"@webex/common": "3.0.0-beta.340",
|
|
51
51
|
"@webex/internal-media-core": "2.2.4",
|
|
52
|
-
"@webex/internal-plugin-conversation": "3.0.0-beta.
|
|
53
|
-
"@webex/internal-plugin-device": "3.0.0-beta.
|
|
54
|
-
"@webex/internal-plugin-llm": "3.0.0-beta.
|
|
55
|
-
"@webex/internal-plugin-mercury": "3.0.0-beta.
|
|
56
|
-
"@webex/internal-plugin-metrics": "3.0.0-beta.
|
|
57
|
-
"@webex/internal-plugin-support": "3.0.0-beta.
|
|
58
|
-
"@webex/internal-plugin-user": "3.0.0-beta.
|
|
59
|
-
"@webex/media-helpers": "3.0.0-beta.
|
|
60
|
-
"@webex/plugin-people": "3.0.0-beta.
|
|
61
|
-
"@webex/plugin-rooms": "3.0.0-beta.
|
|
62
|
-
"@webex/webex-core": "3.0.0-beta.
|
|
52
|
+
"@webex/internal-plugin-conversation": "3.0.0-beta.340",
|
|
53
|
+
"@webex/internal-plugin-device": "3.0.0-beta.340",
|
|
54
|
+
"@webex/internal-plugin-llm": "3.0.0-beta.340",
|
|
55
|
+
"@webex/internal-plugin-mercury": "3.0.0-beta.340",
|
|
56
|
+
"@webex/internal-plugin-metrics": "3.0.0-beta.340",
|
|
57
|
+
"@webex/internal-plugin-support": "3.0.0-beta.340",
|
|
58
|
+
"@webex/internal-plugin-user": "3.0.0-beta.340",
|
|
59
|
+
"@webex/media-helpers": "3.0.0-beta.340",
|
|
60
|
+
"@webex/plugin-people": "3.0.0-beta.340",
|
|
61
|
+
"@webex/plugin-rooms": "3.0.0-beta.340",
|
|
62
|
+
"@webex/webex-core": "3.0.0-beta.340",
|
|
63
63
|
"ampersand-collection": "^2.0.2",
|
|
64
64
|
"bowser": "^2.11.0",
|
|
65
65
|
"btoa": "^1.2.1",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* eslint-disable prefer-destructuring */
|
|
2
2
|
|
|
3
|
-
import {cloneDeep} from 'lodash';
|
|
3
|
+
import {cloneDeep, isEmpty} from 'lodash';
|
|
4
4
|
import {ConnectionState} from '@webex/internal-media-core';
|
|
5
5
|
|
|
6
6
|
import EventsScope from '../common/events/events-scope';
|
|
@@ -78,6 +78,7 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
78
78
|
statsResults: any;
|
|
79
79
|
statsStarted: any;
|
|
80
80
|
successfulCandidatePair: any;
|
|
81
|
+
localIpAddress: string; // Returns the local IP address for diagnostics. this is the local IP of the interface used for the current media connection a host can have many local Ip Addresses
|
|
81
82
|
receiveSlotCallback: ReceiveSlotCallback;
|
|
82
83
|
|
|
83
84
|
/**
|
|
@@ -107,6 +108,7 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
107
108
|
this.lastEmittedStartStopEvent = {};
|
|
108
109
|
this.receiveSlotCallback = receiveSlotCallback;
|
|
109
110
|
this.successfulCandidatePair = {};
|
|
111
|
+
this.localIpAddress = '';
|
|
110
112
|
}
|
|
111
113
|
|
|
112
114
|
/**
|
|
@@ -266,6 +268,16 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
266
268
|
this.mediaConnection = mediaConnection;
|
|
267
269
|
}
|
|
268
270
|
|
|
271
|
+
/**
|
|
272
|
+
* Returns the local IP address for diagnostics.
|
|
273
|
+
* this is the local IP of the interface used for the current media connection
|
|
274
|
+
* a host can have many local Ip Addresses
|
|
275
|
+
* @returns {string | undefined} The local IP address.
|
|
276
|
+
*/
|
|
277
|
+
getLocalIpAddress(): string {
|
|
278
|
+
return this.localIpAddress;
|
|
279
|
+
}
|
|
280
|
+
|
|
269
281
|
/**
|
|
270
282
|
* Starts the stats analyzer on interval
|
|
271
283
|
*
|
|
@@ -405,6 +417,10 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
405
417
|
this.statsResults[type].direction = statsItem.currentDirection;
|
|
406
418
|
this.statsResults[type].trackLabel = statsItem.localTrackLabel;
|
|
407
419
|
this.statsResults[type].csi = statsItem.csi;
|
|
420
|
+
this.extractAndSetLocalIpAddressInfoForDiagnostics(
|
|
421
|
+
this.successfulCandidatePair?.localCandidateId,
|
|
422
|
+
this.statsResults?.candidates
|
|
423
|
+
);
|
|
408
424
|
// reset the successful candidate pair.
|
|
409
425
|
this.successfulCandidatePair = {};
|
|
410
426
|
}
|
|
@@ -983,6 +999,48 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
983
999
|
}
|
|
984
1000
|
}
|
|
985
1001
|
|
|
1002
|
+
/**
|
|
1003
|
+
* extracts the local Ip address from the statsResult object by looking at stats results candidates
|
|
1004
|
+
* and matches that ID with the successful candidate pair. It looks at the type of local candidate it is
|
|
1005
|
+
* and then extracts the IP address from the relatedAddress or address property based on conditions known in webrtc
|
|
1006
|
+
* note, there are known incompatibilities and it is possible for this to set undefined, or for the IP address to be the public IP address
|
|
1007
|
+
* for example, firefox does not set the relayProtocol, and if the user is behind a NAT it might be the public IP
|
|
1008
|
+
* @private
|
|
1009
|
+
* @param {string} successfulCandidatePairId - The ID of the successful candidate pair.
|
|
1010
|
+
* @param {Object} candidates - the stats result candidates
|
|
1011
|
+
* @returns {void}
|
|
1012
|
+
*/
|
|
1013
|
+
extractAndSetLocalIpAddressInfoForDiagnostics = (
|
|
1014
|
+
successfulCandidatePairId: string,
|
|
1015
|
+
candidates: {[key: string]: Record<string, unknown>}
|
|
1016
|
+
) => {
|
|
1017
|
+
let newIpAddress = '';
|
|
1018
|
+
if (successfulCandidatePairId && !isEmpty(candidates)) {
|
|
1019
|
+
const localCandidate = candidates[successfulCandidatePairId];
|
|
1020
|
+
if (localCandidate) {
|
|
1021
|
+
if (localCandidate.candidateType === 'host') {
|
|
1022
|
+
// if it's a host candidate, use the address property - it will be the local IP
|
|
1023
|
+
newIpAddress = `${localCandidate.address}`;
|
|
1024
|
+
} else if (localCandidate.candidateType === 'prflx') {
|
|
1025
|
+
// if it's a peer reflexive candidate and we're not using a relay (there is no relayProtocol set)
|
|
1026
|
+
// then look at the relatedAddress - it will be the local
|
|
1027
|
+
//
|
|
1028
|
+
// Firefox doesn't populate the relayProtocol property
|
|
1029
|
+
if (!localCandidate.relayProtocol) {
|
|
1030
|
+
newIpAddress = `${localCandidate.relatedAddress}`;
|
|
1031
|
+
} else {
|
|
1032
|
+
// if it's a peer reflexive candidate and we are using a relay -
|
|
1033
|
+
// in that case the relatedAddress will be the IP of the TURN server (Linus),
|
|
1034
|
+
// so we can only look at the address, but it might be local IP or public IP,
|
|
1035
|
+
// depending on if the user is behind a NAT or not
|
|
1036
|
+
newIpAddress = `${localCandidate.address}`;
|
|
1037
|
+
}
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
1041
|
+
this.localIpAddress = newIpAddress;
|
|
1042
|
+
};
|
|
1043
|
+
|
|
986
1044
|
/**
|
|
987
1045
|
* Processes remote and local candidate result and stores
|
|
988
1046
|
* @private
|
|
@@ -1020,6 +1078,11 @@ export class StatsAnalyzer extends EventsScope {
|
|
|
1020
1078
|
this.statsResults.candidates[result.id] = {
|
|
1021
1079
|
candidateType: result.candidateType,
|
|
1022
1080
|
ipAddress: result.ip, // TODO: add ports
|
|
1081
|
+
relatedAddress: result.relatedAddress,
|
|
1082
|
+
relatedPort: result.relatedPort,
|
|
1083
|
+
relayProtocol: result.relayProtocol,
|
|
1084
|
+
protocol: result.protocol,
|
|
1085
|
+
address: result.address,
|
|
1023
1086
|
portNumber: result.port,
|
|
1024
1087
|
networkType: result.networkType,
|
|
1025
1088
|
priority: result.priority,
|
|
@@ -286,6 +286,19 @@ describe('plugin-meetings', () => {
|
|
|
286
286
|
await testUtils.flushPromises();
|
|
287
287
|
};
|
|
288
288
|
|
|
289
|
+
const mergeProperties = (target, properties, keyValue = 'fake-candidate-id', matchKey= 'type', matchValue = 'local-candidate') => {
|
|
290
|
+
for (let key in target) {
|
|
291
|
+
if (target.hasOwnProperty(key)) {
|
|
292
|
+
if (typeof target[key] === 'object') {
|
|
293
|
+
mergeProperties(target[key], properties, keyValue, matchKey, matchValue);
|
|
294
|
+
}
|
|
295
|
+
if (key === 'id' && target[key] === keyValue && target[matchKey] === matchValue) {
|
|
296
|
+
Object.assign(target, properties);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
289
302
|
const progressTime = async () => {
|
|
290
303
|
await clock.tickAsync(initialConfig.analyzerInterval);
|
|
291
304
|
await testUtils.flushPromises();
|
|
@@ -474,6 +487,46 @@ describe('plugin-meetings', () => {
|
|
|
474
487
|
await progressTime();
|
|
475
488
|
assert.strictEqual(mqeData.videoReceive[0].streams[0].common.receivedFrameRate, 25);
|
|
476
489
|
});
|
|
490
|
+
|
|
491
|
+
it('has the correct localIpAddress set when the candidateType is host', async () => {
|
|
492
|
+
await startStatsAnalyzer();
|
|
493
|
+
|
|
494
|
+
await progressTime();
|
|
495
|
+
assert.strictEqual(statsAnalyzer.getLocalIpAddress(), '');
|
|
496
|
+
mergeProperties(fakeStats, {address: 'test', candidateType: 'host'});
|
|
497
|
+
await progressTime();
|
|
498
|
+
assert.strictEqual(statsAnalyzer.getLocalIpAddress(), 'test');
|
|
499
|
+
});
|
|
500
|
+
|
|
501
|
+
it('has the correct localIpAddress set when the candidateType is prflx and relayProtocol is set', async () => {
|
|
502
|
+
await startStatsAnalyzer();
|
|
503
|
+
|
|
504
|
+
await progressTime();
|
|
505
|
+
assert.strictEqual(statsAnalyzer.getLocalIpAddress(), '');
|
|
506
|
+
mergeProperties(fakeStats, {relayProtocol: 'test', address: 'test2', candidateType: 'prflx'});
|
|
507
|
+
await progressTime();
|
|
508
|
+
assert.strictEqual(statsAnalyzer.getLocalIpAddress(), 'test2');
|
|
509
|
+
});
|
|
510
|
+
|
|
511
|
+
it('has the correct localIpAddress set when the candidateType is prflx and relayProtocol is not set', async () => {
|
|
512
|
+
await startStatsAnalyzer();
|
|
513
|
+
|
|
514
|
+
await progressTime();
|
|
515
|
+
assert.strictEqual(statsAnalyzer.getLocalIpAddress(), '');
|
|
516
|
+
mergeProperties(fakeStats, {relatedAddress: 'relatedAddress', address: 'test2', candidateType: 'prflx'});
|
|
517
|
+
await progressTime();
|
|
518
|
+
assert.strictEqual(statsAnalyzer.getLocalIpAddress(), 'relatedAddress');
|
|
519
|
+
});
|
|
520
|
+
|
|
521
|
+
it('has no localIpAddress set when the candidateType is invalid', async () => {
|
|
522
|
+
await startStatsAnalyzer();
|
|
523
|
+
|
|
524
|
+
await progressTime();
|
|
525
|
+
assert.strictEqual(statsAnalyzer.getLocalIpAddress(), '');
|
|
526
|
+
mergeProperties(fakeStats, {candidateType: 'invalid'});
|
|
527
|
+
await progressTime();
|
|
528
|
+
assert.strictEqual(statsAnalyzer.getLocalIpAddress(), '');
|
|
529
|
+
});
|
|
477
530
|
});
|
|
478
531
|
});
|
|
479
532
|
});
|