infobip-rtc
Version:
Infobip RTC JavaScript SDK - Infobip WebRTC API Implementation
232 lines • 12 kB
JavaScript
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
import { NetworkQualityStatistics } from "./media/NetworkQualityStatistics";
import { MediaStatsAdapter } from "./media/MediaStatsAdapter";
import { CallMediaStatsLog, IceCandidatePairLog, SelectedIceCandidatePairLog } from "../Log";
import { MediaStatsDiffUtil } from "./media/MediaStatsDiffUtil";
import { TotalMediaStats } from "../../call/stats/TotalMediaStats";
import { AudioStats } from "../../call/stats/AudioStats";
import { CurrentMediaStats } from "../../call/stats/CurrentMediaStats";
import { MediaType } from "../util/MediaType";
import { isEqual } from "lodash";
import { WsAction } from "../../call/ws/WsAction";
export class PeerConnectionMediaMonitor {
constructor(callId, name, conferenceId, pc, mediaType, logger, gateway) {
this.callId = callId;
this.name = name;
this.conferenceId = conferenceId;
this.pc = pc;
this.mediaType = mediaType;
this.logger = logger;
this.gateway = gateway;
this.previousConferenceStats = new Map();
this.iceCandidateInterval = setInterval(() => this.sendIceCandidates(), PeerConnectionMediaMonitor.DEFAULT_MONITOR_ICE_CANDIDATE_INTERVAL);
this.summaryInterval = setInterval(() => this.sendSummaryStats(), PeerConnectionMediaMonitor.DEFAULT_MONITOR_SUMMARY_INTERVAL);
this.trackMonitoringInterval = setInterval(() => this.sendTrackStats(), PeerConnectionMediaMonitor.DEFAULT_MONITOR_TRACK_INTERVAL);
}
stop() {
let totalMediaStats = this.sendTotalCallStats();
clearInterval(this.iceCandidateInterval);
clearInterval(this.summaryInterval);
clearInterval(this.trackMonitoringInterval);
return totalMediaStats;
}
onNetworkQualityStatistics(callback) {
this.networkQualityStatisticsListener = callback;
}
isAudio() {
return this.mediaType === MediaType.AUDIO;
}
isVideo() {
return this.mediaType === MediaType.VIDEO;
}
sendSelectedCandidatePair(rtcStatsReport) {
var _a, _b;
let newSelectedCandidatePair = MediaStatsAdapter.extractSelectedCandidatePair(rtcStatsReport, this.mediaType);
if (!newSelectedCandidatePair) {
return;
}
const isLocalEqual = isEqual(newSelectedCandidatePair.local, (_a = this.selectedCandidatePair) === null || _a === void 0 ? void 0 : _a.local);
const isRemoteEqual = isEqual(newSelectedCandidatePair.remote, (_b = this.selectedCandidatePair) === null || _b === void 0 ? void 0 : _b.remote);
if (isLocalEqual && isRemoteEqual) {
return;
}
this.selectedCandidatePair = newSelectedCandidatePair;
this.logger.log(new SelectedIceCandidatePairLog(this.callId, this.selectedCandidatePair));
this.gateway.send({
action: WsAction.ICE_CANDIDATE_PAIR_SELECTED,
callId: this.callId,
selectedCandidatePair: this.selectedCandidatePair
});
}
sendTotalCallStats() {
let totalMediaStats = new TotalMediaStats();
this.previousConferenceStats.forEach((callStats, trackId) => {
var _a;
if (trackId) {
if (this.isAudio()) {
this.fillAudioStats(callStats, totalMediaStats.totalAudioStats);
}
this.sendExtractedStats(callStats, trackId, "Total");
}
else {
if (this.isAudio()) {
totalMediaStats.totalAudioStats.roundTripTime = (_a = callStats.callStats.currentRoundTripTime) !== null && _a !== void 0 ? _a : 0;
}
this.sendExtractedMediaStats(callStats, callStats.callStats.timestamp, null, "Total");
}
});
return totalMediaStats;
}
sendTrackStats() {
if (!this.callId && !this.conferenceId) {
return;
}
if (!this.pc || !PeerConnectionMediaMonitor.MONITOR_ICE_STATES.has(this.pc.iceConnectionState)) {
return;
}
this.pc.getSenders().forEach(sender => this.sendStatsForTrack(sender.track));
this.pc.getReceivers().forEach(receiver => this.sendStatsForTrack(receiver.track));
}
sendIceCandidates() {
return __awaiter(this, void 0, void 0, function* () {
if (!this.callId && !this.conferenceId) {
return;
}
if (!this.pc || !PeerConnectionMediaMonitor.MONITOR_ICE_STATES.has(this.pc.iceConnectionState)) {
return;
}
const report = yield this.pc.getStats();
const pairs = MediaStatsAdapter.extractCandidatePairs(report, this.mediaType);
pairs.forEach(pair => {
this.logger.log(new IceCandidatePairLog(this.callId, pair));
});
});
}
sendSummaryStats() {
if (!this.callId && !this.conferenceId) {
return;
}
if (!this.pc || !PeerConnectionMediaMonitor.MONITOR_ICE_STATES.has(this.pc.iceConnectionState)) {
return;
}
this.sendSummaryStatsForPeerConnection();
}
sendStatsForTrack(track) {
if (!track) {
return;
}
const trackId = track === null || track === void 0 ? void 0 : track.id;
const trackLabel = (track === null || track === void 0 ? void 0 : track.label) || (this.name + "_" + trackId);
this.sendStatsForLabeledTrack(track, trackLabel || "unknown");
}
emitNetworkQualityStats(previousCallStats, diffStats) {
const remoteAudioStats = diffStats.remoteAudio;
if (!remoteAudioStats) {
return;
}
const localAudioStats = diffStats.localAudio;
if (!localAudioStats) {
return;
}
if (!diffStats.callStats.currentRoundTripTime) {
return;
}
const networkQualityStatistics = NetworkQualityStatistics.forRTCStats(diffStats.callStats.currentRoundTripTime, remoteAudioStats.jitter, remoteAudioStats.packetsReceived, remoteAudioStats.packetsLost, remoteAudioStats.codec || localAudioStats.codec);
if (previousCallStats || networkQualityStatistics.mos !== 1.0) {
diffStats.callStats.mos = networkQualityStatistics.mos;
if (this.networkQualityStatisticsListener) {
let currentMediaStats = this.getCurrentMediaStats(diffStats);
this.networkQualityStatisticsListener(networkQualityStatistics, currentMediaStats);
}
}
}
getCurrentMediaStats(diffStats) {
var _a, _b;
let currentMediaStats = new CurrentMediaStats();
let currentAudioStats = new AudioStats();
currentAudioStats.roundTripTime = (_a = diffStats.callStats.currentRoundTripTime) !== null && _a !== void 0 ? _a : 0;
currentAudioStats.jitter = diffStats.remoteAudio.jitter;
currentAudioStats.codec = (_b = diffStats.remoteAudio.codec) !== null && _b !== void 0 ? _b : diffStats.localAudio.codec;
currentAudioStats.packetsLost = diffStats.remoteAudio.packetsLost;
currentAudioStats.packetsReceived = diffStats.remoteAudio.packetsReceived;
currentAudioStats.bytesReceived = diffStats.remoteAudio.bytesReceived;
currentAudioStats.packetsSent = diffStats.localAudio.packetsSent;
currentAudioStats.retransmittedPacketsSent = diffStats.localAudio.retransmittedPacketsSent;
currentAudioStats.bytesSent = diffStats.localAudio.bytesSent;
currentAudioStats.retransmittedBytesSent = diffStats.localAudio.retransmittedBytesSent;
currentMediaStats.currentAudioStats = currentAudioStats;
return currentMediaStats;
}
sendSummaryStatsForPeerConnection() {
this.pc.getStats()
.then(rtcStatsReport => {
let currentCallStats = MediaStatsAdapter.extract(rtcStatsReport, this.isVideo());
let previousCallStats = this.previousConferenceStats.get(null);
let diffCallStats = MediaStatsDiffUtil.diff(previousCallStats, currentCallStats, this.isVideo());
this.emitNetworkQualityStats(previousCallStats, diffCallStats);
this.sendExtractedMediaStats(diffCallStats, diffCallStats.callStats.timestamp, null, this.name);
this.previousConferenceStats.set(null, currentCallStats);
this.sendSelectedCandidatePair(rtcStatsReport);
});
}
sendStatsForLabeledTrack(track, label) {
this.pc.getStats(track)
.then(rtcStatsReport => {
let currentCallStats = MediaStatsAdapter.extract(rtcStatsReport, this.isVideo());
let previousCallStats = this.previousConferenceStats.get(track.id);
let diffCallStats = MediaStatsDiffUtil.diff(previousCallStats, currentCallStats, this.isVideo());
this.sendExtractedStats(diffCallStats, track.id, label);
this.previousConferenceStats.set(track.id, currentCallStats);
this.sendSelectedCandidatePair(rtcStatsReport);
});
}
sendExtractedMediaStats(data, timestamp, trackId, label) {
data.trackId = trackId || data["id"];
this.logger.log(new CallMediaStatsLog(label, this.conferenceId, this.callId, this.name, data, timestamp));
}
sendExtractedStats(diffCallStats, trackId, label) {
diffCallStats.extractedRemoteStats.forEach(remoteStats => this.sendExtractedMediaStats(remoteStats, diffCallStats.callStats.timestamp, trackId, label));
diffCallStats.extractedLocalStats.forEach(localStats => this.sendExtractedMediaStats(localStats, diffCallStats.callStats.timestamp, trackId, label));
}
fillAudioStats(stats, audioStats) {
let audioLocalMediaStats = stats.localAudio;
if (audioLocalMediaStats != null) {
this.fillLocalAudioStats(audioStats, audioLocalMediaStats);
}
let audioRemoteMediaStats = stats.remoteAudio;
if (audioRemoteMediaStats != null) {
this.fillRemoteAudioStats(audioStats, audioRemoteMediaStats);
}
}
fillRemoteAudioStats(audioStats, audioRemoteMediaStats) {
if (audioStats.codec == null) {
audioStats.codec = audioRemoteMediaStats.codec;
}
audioStats.bytesReceived = audioRemoteMediaStats.bytesReceived;
audioStats.packetsReceived = audioRemoteMediaStats.packetsReceived;
audioStats.packetsLost = audioRemoteMediaStats.packetsLost;
audioStats.jitter = audioRemoteMediaStats.jitter;
}
fillLocalAudioStats(audioStats, audioLocalMediaStats) {
if (audioStats.codec == null) {
audioStats.codec = audioLocalMediaStats.codec;
}
audioStats.bytesSent = audioLocalMediaStats.bytesSent;
audioStats.packetsSent = audioLocalMediaStats.packetsSent;
audioStats.retransmittedBytesSent = audioLocalMediaStats.retransmittedBytesSent;
audioStats.retransmittedPacketsSent = audioLocalMediaStats.retransmittedPacketsSent;
}
}
PeerConnectionMediaMonitor.MONITOR_ICE_STATES = new Set(["connected", "completed"]);
PeerConnectionMediaMonitor.DEFAULT_MONITOR_ICE_CANDIDATE_INTERVAL = 10000;
PeerConnectionMediaMonitor.DEFAULT_MONITOR_SUMMARY_INTERVAL = 1000;
PeerConnectionMediaMonitor.DEFAULT_MONITOR_TRACK_INTERVAL = 10000;
//# sourceMappingURL=PeerConnectionMediaMonitor.js.map