@twilio/voice-sdk
Version:
Twilio's JavaScript Voice SDK
485 lines • 39 kB
JavaScript
"use strict";
/**
* @packageDocumentation
* @module Voice
* @internalapi
*/
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) {
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 __spreadArrays = (this && this.__spreadArrays) || function () {
for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;
for (var r = Array(s), k = 0, i = 0; i < il; i++)
for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)
r[k] = a[j];
return r;
};
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 __spreadArrays(flat, current); }, []);
}
/**
* {@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, __spreadArrays([SAMPLE_COUNT_METRICS], thresholdSampleCounts));
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 = statName + ":" + 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 = statName + ":" + 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(util_1.average(audioInputLevelValues)),
audioOutputLevel: Math.round(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 = statName + ":" + 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 = 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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3RhdHNNb25pdG9yLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vbGliL3R3aWxpby9zdGF0c01vbml0b3IudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBOzs7O0dBSUc7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQUVILGlDQUFzQztBQUN0QyxtQ0FBZ0Q7QUFDaEQsaUNBQTRCO0FBRTVCLHFDQUEwQztBQUUxQywrQkFBaUM7QUFFakMseURBQXlEO0FBQ3pELElBQU0sb0JBQW9CLEdBQUcsQ0FBQyxDQUFDO0FBRS9CLHVEQUF1RDtBQUN2RCw0QkFBNEI7QUFDNUIsSUFBTSxrQkFBa0IsR0FBRyxDQUFDLENBQUM7QUFDN0IsSUFBTSxrQkFBa0IsR0FBRyxDQUFDLENBQUM7QUFFN0IsSUFBTSxlQUFlLEdBQUcsSUFBSSxDQUFDO0FBQzdCLElBQU0sZUFBZSxHQUFHLENBQUMsR0FBRyxJQUFJLENBQUM7QUFFakMsSUFBTSxrQkFBa0IsR0FBa0M7SUFDeEQsZUFBZSxFQUFFLEVBQUUsb0JBQW9CLEVBQUUsTUFBTSxFQUFFLFdBQVcsRUFBRSxFQUFFLEVBQUU7SUFDbEUsZ0JBQWdCLEVBQUUsRUFBRSxvQkFBb0IsRUFBRSxNQUFNLEVBQUUsV0FBVyxFQUFFLEVBQUUsRUFBRTtJQUNuRSxhQUFhLEVBQUUsRUFBRSxVQUFVLEVBQUUsQ0FBQyxFQUFFLEdBQUcsRUFBRSxDQUFDLEVBQUUsVUFBVSxFQUFFLENBQUMsRUFBRSxXQUFXLEVBQUUsQ0FBQyxFQUFFO0lBQ3ZFLFNBQVMsRUFBRSxFQUFFLFVBQVUsRUFBRSxDQUFDLEVBQUUsR0FBRyxFQUFFLENBQUMsRUFBRSxVQUFVLEVBQUUsQ0FBQyxFQUFFLFdBQVcsRUFBRSxDQUFDLEVBQUU7SUFDbkUsTUFBTSxFQUFFLEVBQUUsR0FBRyxFQUFFLEVBQUUsRUFBRTtJQUNuQixHQUFHLEVBQUUsRUFBRSxHQUFHLEVBQUUsQ0FBQyxFQUFFO0lBQ2YsbUJBQW1CLEVBQUUsQ0FB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