UNPKG

@twilio/voice-sdk

Version:
484 lines 39.7 kB
"use strict"; var __extends = (this && this.__extends) || (function () { var extendStatics = function (d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; return extendStatics(d, b); }; return function (d, b) { if (typeof b !== "function" && b !== null) throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); var __assign = (this && this.__assign) || function () { __assign = Object.assign || function(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { if (ar || !(i in from)) { if (!ar) ar = Array.prototype.slice.call(from, 0, i); ar[i] = from[i]; } } return to.concat(ar || Array.prototype.slice.call(from)); }; Object.defineProperty(exports, "__esModule", { value: true }); var events_1 = require("events"); var errors_1 = require("./errors"); var mos_1 = require("./rtc/mos"); var stats_1 = require("./rtc/stats"); var util_1 = require("./util"); // How many samples we use when testing metric thresholds var SAMPLE_COUNT_METRICS = 5; // How many samples that need to cross the threshold to // raise or clear a warning. var SAMPLE_COUNT_CLEAR = 0; var SAMPLE_COUNT_RAISE = 3; var SAMPLE_INTERVAL = 1000; var WARNING_TIMEOUT = 5 * 1000; var DEFAULT_THRESHOLDS = { audioInputLevel: { minStandardDeviation: 327.67, sampleCount: 10 }, audioOutputLevel: { minStandardDeviation: 327.67, sampleCount: 10 }, bytesReceived: { clearCount: 2, min: 1, raiseCount: 3, sampleCount: 3 }, bytesSent: { clearCount: 2, min: 1, raiseCount: 3, sampleCount: 3 }, jitter: { max: 30 }, mos: { min: 3 }, packetsLostFraction: [{ max: 1, }, { clearValue: 1, maxAverage: 3, sampleCount: 7, }], rtt: { max: 400 }, }; /** * Count the number of values that cross the max threshold. * @private * @param max - The max allowable value. * @param values - The values to iterate over. * @returns The amount of values in which the stat crossed the threshold. */ function countHigh(max, values) { return values.reduce(function (highCount, value) { return highCount += (value > max) ? 1 : 0; }, 0); } /** * Count the number of values that cross the min threshold. * @private * @param min - The minimum allowable value. * @param values - The values to iterate over. * @returns The amount of values in which the stat crossed the threshold. */ function countLow(min, values) { return values.reduce(function (lowCount, value) { return lowCount += (value < min) ? 1 : 0; }, 0); } /** * Calculate the standard deviation from a list of numbers. * @private * @param values The list of numbers to calculate the standard deviation from. * @returns The standard deviation of a list of numbers. */ function calculateStandardDeviation(values) { if (values.length <= 0) { return null; } var valueAverage = values.reduce(function (partialSum, value) { return partialSum + value; }, 0) / values.length; var diffSquared = values.map(function (value) { return Math.pow(value - valueAverage, 2); }); var stdDev = Math.sqrt(diffSquared.reduce(function (partialSum, value) { return partialSum + value; }, 0) / diffSquared.length); return stdDev; } /** * Flatten a set of numerical sample sets into a single array of samples. * @param sampleSets */ function flattenSamples(sampleSets) { return sampleSets.reduce(function (flat, current) { return __spreadArray(__spreadArray([], flat, true), current, true); }, []); } /** * {@link StatsMonitor} polls a peerConnection via PeerConnection.getStats * and emits warnings when stats cross the specified threshold values. */ var StatsMonitor = /** @class */ (function (_super) { __extends(StatsMonitor, _super); /** * @constructor * @param [options] - Optional settings */ function StatsMonitor(options) { var _this = _super.call(this) || this; /** * A map of warnings with their raised time */ _this._activeWarnings = new Map(); /** * A map of stats with the number of exceeded thresholds */ _this._currentStreaks = new Map(); /** * Keeps track of input volumes in the last second */ _this._inputVolumes = []; /** * Keeps track of output volumes in the last second */ _this._outputVolumes = []; /** * Sample buffer. Saves most recent samples */ _this._sampleBuffer = []; /** * Keeps track of supplemental sample values. * * Currently used for constant audio detection. Contains an array of volume * samples for each sample interval. */ _this._supplementalSampleBuffers = { audioInputLevel: [], audioOutputLevel: [], }; /** * Whether warnings should be enabled */ _this._warningsEnabled = true; options = options || {}; _this._getRTCStats = options.getRTCStats || stats_1.getRTCStats; _this._mos = options.Mos || mos_1.default; _this._peerConnection = options.peerConnection; _this._thresholds = __assign(__assign({}, DEFAULT_THRESHOLDS), options.thresholds); var thresholdSampleCounts = Object.values(_this._thresholds) .map(function (threshold) { return threshold.sampleCount; }) .filter(function (sampleCount) { return !!sampleCount; }); _this._maxSampleCount = Math.max.apply(Math, __spreadArray([SAMPLE_COUNT_METRICS], thresholdSampleCounts, false)); if (_this._peerConnection) { _this.enable(_this._peerConnection); } return _this; } /** * Called when a volume sample is available * @param inputVolume - Input volume level from 0 to 32767 * @param outputVolume - Output volume level from 0 to 32767 */ StatsMonitor.prototype.addVolumes = function (inputVolume, outputVolume) { this._inputVolumes.push(inputVolume); this._outputVolumes.push(outputVolume); }; /** * Stop sampling RTC statistics for this {@link StatsMonitor}. * @returns The current {@link StatsMonitor}. */ StatsMonitor.prototype.disable = function () { if (this._sampleInterval) { clearInterval(this._sampleInterval); delete this._sampleInterval; } return this; }; /** * Disable warnings for this {@link StatsMonitor}. * @returns The current {@link StatsMonitor}. */ StatsMonitor.prototype.disableWarnings = function () { if (this._warningsEnabled) { this._activeWarnings.clear(); } this._warningsEnabled = false; return this; }; /** * Start sampling RTC statistics for this {@link StatsMonitor}. * @param peerConnection - A PeerConnection to monitor. * @returns The current {@link StatsMonitor}. */ StatsMonitor.prototype.enable = function (peerConnection) { if (peerConnection) { if (this._peerConnection && peerConnection !== this._peerConnection) { throw new errors_1.InvalidArgumentError('Attempted to replace an existing PeerConnection in StatsMonitor.enable'); } this._peerConnection = peerConnection; } if (!this._peerConnection) { throw new errors_1.InvalidArgumentError('Can not enable StatsMonitor without a PeerConnection'); } this._sampleInterval = this._sampleInterval || setInterval(this._fetchSample.bind(this), SAMPLE_INTERVAL); return this; }; /** * Enable warnings for this {@link StatsMonitor}. * @returns The current {@link StatsMonitor}. */ StatsMonitor.prototype.enableWarnings = function () { this._warningsEnabled = true; return this; }; /** * Check if there is an active warning for a specific stat and threshold * @param statName - The name of the stat to check * @param thresholdName - The name of the threshold to check * @returns Whether there is an active warning for a specific stat and threshold */ StatsMonitor.prototype.hasActiveWarning = function (statName, thresholdName) { var warningId = "".concat(statName, ":").concat(thresholdName); return !!this._activeWarnings.get(warningId); }; /** * Add a sample to our sample buffer and remove the oldest if we are over the limit. * @param sample - Sample to add */ StatsMonitor.prototype._addSample = function (sample) { var samples = this._sampleBuffer; samples.push(sample); // We store 1 extra sample so that we always have (current, previous) // available for all {sampleBufferSize} threshold validations. if (samples.length > this._maxSampleCount) { samples.splice(0, samples.length - this._maxSampleCount); } }; /** * Clear an active warning. * @param statName - The name of the stat to clear. * @param thresholdName - The name of the threshold to clear * @param [data] - Any relevant sample data. */ StatsMonitor.prototype._clearWarning = function (statName, thresholdName, data) { var warningId = "".concat(statName, ":").concat(thresholdName); var activeWarning = this._activeWarnings.get(warningId); if (!activeWarning || Date.now() - activeWarning.timeRaised < WARNING_TIMEOUT) { return; } this._activeWarnings.delete(warningId); this.emit('warning-cleared', __assign(__assign({}, data), { name: statName, threshold: { name: thresholdName, value: this._thresholds[statName][thresholdName], } })); }; /** * Create a sample object from a stats object using the previous sample, if available. * @param stats - Stats retrieved from getStatistics * @param [previousSample=null] - The previous sample to use to calculate deltas. * @returns A universally-formatted version of RTC stats. */ StatsMonitor.prototype._createSample = function (stats, previousSample) { var previousBytesSent = previousSample && previousSample.totals.bytesSent || 0; var previousBytesReceived = previousSample && previousSample.totals.bytesReceived || 0; var previousPacketsSent = previousSample && previousSample.totals.packetsSent || 0; var previousPacketsReceived = previousSample && previousSample.totals.packetsReceived || 0; var previousPacketsLost = previousSample && previousSample.totals.packetsLost || 0; var currentBytesSent = stats.bytesSent - previousBytesSent; var currentBytesReceived = stats.bytesReceived - previousBytesReceived; var currentPacketsSent = stats.packetsSent - previousPacketsSent; var currentPacketsReceived = stats.packetsReceived - previousPacketsReceived; var currentPacketsLost = stats.packetsLost - previousPacketsLost; var currentInboundPackets = currentPacketsReceived + currentPacketsLost; var currentPacketsLostFraction = (currentInboundPackets > 0) ? (currentPacketsLost / currentInboundPackets) * 100 : 0; var totalInboundPackets = stats.packetsReceived + stats.packetsLost; var totalPacketsLostFraction = (totalInboundPackets > 0) ? (stats.packetsLost / totalInboundPackets) * 100 : 100; var rttValue = (typeof stats.rtt === 'number' || !previousSample) ? stats.rtt : previousSample.rtt; var audioInputLevelValues = this._inputVolumes.splice(0); this._supplementalSampleBuffers.audioInputLevel.push(audioInputLevelValues); var audioOutputLevelValues = this._outputVolumes.splice(0); this._supplementalSampleBuffers.audioOutputLevel.push(audioOutputLevelValues); return { audioInputLevel: Math.round((0, util_1.average)(audioInputLevelValues)), audioOutputLevel: Math.round((0, util_1.average)(audioOutputLevelValues)), bytesReceived: currentBytesReceived, bytesSent: currentBytesSent, codecName: stats.codecName, jitter: stats.jitter, mos: this._mos.calculate(rttValue, stats.jitter, previousSample && currentPacketsLostFraction), packetsLost: currentPacketsLost, packetsLostFraction: currentPacketsLostFraction, packetsReceived: currentPacketsReceived, packetsSent: currentPacketsSent, rtt: rttValue, timestamp: stats.timestamp, totals: { bytesReceived: stats.bytesReceived, bytesSent: stats.bytesSent, packetsLost: stats.packetsLost, packetsLostFraction: totalPacketsLostFraction, packetsReceived: stats.packetsReceived, packetsSent: stats.packetsSent, }, }; }; /** * Get stats from the PeerConnection and add it to our list of samples. */ StatsMonitor.prototype._fetchSample = function () { var _this = this; this._getSample().then(function (sample) { _this._addSample(sample); _this._raiseWarnings(); _this.emit('sample', sample); }).catch(function (error) { _this.disable(); // We only bubble up any errors coming from pc.getStats() // No need to attach a twilioError _this.emit('error', error); }); }; /** * Get stats from the PeerConnection. * @returns A universally-formatted version of RTC stats. */ StatsMonitor.prototype._getSample = function () { var _this = this; return this._getRTCStats(this._peerConnection).then(function (stats) { var previousSample = null; if (_this._sampleBuffer.length) { previousSample = _this._sampleBuffer[_this._sampleBuffer.length - 1]; } return _this._createSample(stats, previousSample); }); }; /** * Raise a warning and log its raised time. * @param statName - The name of the stat to raise. * @param thresholdName - The name of the threshold to raise * @param [data] - Any relevant sample data. */ StatsMonitor.prototype._raiseWarning = function (statName, thresholdName, data) { var warningId = "".concat(statName, ":").concat(thresholdName); if (this._activeWarnings.has(warningId)) { return; } this._activeWarnings.set(warningId, { timeRaised: Date.now() }); var thresholds = this._thresholds[statName]; var thresholdValue; if (Array.isArray(thresholds)) { var foundThreshold = thresholds.find(function (threshold) { return thresholdName in threshold; }); if (foundThreshold) { thresholdValue = foundThreshold[thresholdName]; } } else { thresholdValue = this._thresholds[statName][thresholdName]; } this.emit('warning', __assign(__assign({}, data), { name: statName, threshold: { name: thresholdName, value: thresholdValue, } })); }; /** * Apply our thresholds to our array of RTCStat samples. */ StatsMonitor.prototype._raiseWarnings = function () { var _this = this; if (!this._warningsEnabled) { return; } Object.keys(this._thresholds).forEach(function (name) { return _this._raiseWarningsForStat(name); }); }; /** * Apply thresholds for a given stat name to our array of * RTCStat samples and raise or clear any associated warnings. * @param statName - Name of the stat to compare. */ StatsMonitor.prototype._raiseWarningsForStat = function (statName) { var _this = this; var limits = Array.isArray(this._thresholds[statName]) ? this._thresholds[statName] : [this._thresholds[statName]]; limits.forEach(function (limit) { var samples = _this._sampleBuffer; var clearCount = limit.clearCount || SAMPLE_COUNT_CLEAR; var raiseCount = limit.raiseCount || SAMPLE_COUNT_RAISE; var sampleCount = limit.sampleCount || _this._maxSampleCount; var relevantSamples = samples.slice(-sampleCount); var values = relevantSamples.map(function (sample) { return sample[statName]; }); // (rrowland) If we have a bad or missing value in the set, we don't // have enough information to throw or clear a warning. Bail out. var containsNull = values.some(function (value) { return typeof value === 'undefined' || value === null; }); if (containsNull) { return; } var count; if (typeof limit.max === 'number') { count = countHigh(limit.max, values); if (count >= raiseCount) { _this._raiseWarning(statName, 'max', { values: values, samples: relevantSamples }); } else if (count <= clearCount) { _this._clearWarning(statName, 'max', { values: values, samples: relevantSamples }); } } if (typeof limit.min === 'number') { count = countLow(limit.min, values); if (count >= raiseCount) { _this._raiseWarning(statName, 'min', { values: values, samples: relevantSamples }); } else if (count <= clearCount) { _this._clearWarning(statName, 'min', { values: values, samples: relevantSamples }); } } if (typeof limit.maxDuration === 'number' && samples.length > 1) { relevantSamples = samples.slice(-2); var prevValue = relevantSamples[0][statName]; var curValue = relevantSamples[1][statName]; var prevStreak = _this._currentStreaks.get(statName) || 0; var streak = (prevValue === curValue) ? prevStreak + 1 : 0; _this._currentStreaks.set(statName, streak); if (streak >= limit.maxDuration) { _this._raiseWarning(statName, 'maxDuration', { value: streak }); } else if (streak === 0) { _this._clearWarning(statName, 'maxDuration', { value: prevStreak }); } } if (typeof limit.minStandardDeviation === 'number') { var sampleSets = _this._supplementalSampleBuffers[statName]; if (!sampleSets || sampleSets.length < limit.sampleCount) { return; } if (sampleSets.length > limit.sampleCount) { sampleSets.splice(0, sampleSets.length - limit.sampleCount); } var flatSamples = flattenSamples(sampleSets.slice(-sampleCount)); var stdDev = calculateStandardDeviation(flatSamples); if (typeof stdDev !== 'number') { return; } if (stdDev < limit.minStandardDeviation) { _this._raiseWarning(statName, 'minStandardDeviation', { value: stdDev }); } else { _this._clearWarning(statName, 'minStandardDeviation', { value: stdDev }); } } [ ['maxAverage', function (x, y) { return x > y; }], ['minAverage', function (x, y) { return x < y; }], ].forEach(function (_a) { var thresholdName = _a[0], comparator = _a[1]; if (typeof limit[thresholdName] === 'number' && values.length >= sampleCount) { var avg = (0, util_1.average)(values); if (comparator(avg, limit[thresholdName])) { _this._raiseWarning(statName, thresholdName, { values: values, samples: relevantSamples }); } else if (!comparator(avg, limit.clearValue || limit[thresholdName])) { _this._clearWarning(statName, thresholdName, { values: values, samples: relevantSamples }); } } }); }); }; return StatsMonitor; }(events_1.EventEmitter)); exports.default = StatsMonitor; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3RhdHNNb25pdG9yLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vbGliL3R3aWxpby9zdGF0c01vbml0b3IudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQUFBLGlDQUFzQztBQUN0QyxtQ0FBZ0Q7QUFDaEQsaUNBQTRCO0FBRTVCLHFDQUEwQztBQUUxQywrQkFBaUM7QUFFakMseURBQXlEO0FBQ3pELElBQU0sb0JBQW9CLEdBQUcsQ0FBQyxDQUFDO0FBRS9CLHVEQUF1RDtBQUN2RCw0QkFBNEI7QUFDNUIsSUFBTSxrQkFBa0IsR0FBRyxDQUFDLENBQUM7QUFDN0IsSUFBTSxrQkFBa0IsR0FBRyxDQUFDLENBQUM7QUFFN0IsSUFBTSxlQUFlLEdBQUcsSUFBSSxDQUFDO0FBQzdCLElBQU0sZUFBZSxHQUFHLENBQUMsR0FBRyxJQUFJLENBQUM7QUFFakMsSUFBTSxrQkFBa0IsR0FBa0M7SUFDeEQsZUFBZSxFQUFFLEVBQUUsb0JBQW9CLEVBQUUsTUFBTSxFQUFFLFdBQVcsRUFBRSxFQUFFLEVBQUU7SUFDbEUsZ0JBQWdCLEVBQUUsRUFBRSxvQkFBb0IsRUFBRSxNQUFNLEVBQUUsV0FBVyxFQUFFLEVBQUUsRUFBRTtJQUNuRSxhQUFhLEVBQUUsRUFBRSxVQUFVLEVBQUUsQ0FBQyxFQUFFLEdBQUcsRUFBRSxDQUFDLEVBQUUsVUFBVSxFQUFFLENBQUMsRUFBRSxXQUFXLEVBQUUsQ0FBQyxFQUFFO0lBQ3ZFLFNBQVMsRUFBRSxFQUFFLFVBQVUsRUFBRSxDQUFDLEVBQUUsR0FBRyxFQUFFLENBQUMsRUFBRSxVQUFVLEVBQUUsQ0FBQyxFQUFFLFdBQVcsRUFBRSxDQUFDLEVBQUU7SUFDbkUsTUFBTSxFQUFFLEVBQUUsR0FBRyxFQUFFLEVBQUUsRUFBRTtJQUNuQixHQUFHLEVBQUUsRUFBRSxHQUFHLEVBQUUsQ0FBQyxFQUFFO0lBQ2YsbUJBQW1CLEVBQUUsQ0FBQztZQUNwQixH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