UNPKG

sinch-rtc

Version:

RTC JavaScript/Web SDK

339 lines 12.8 kB
"use strict"; 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()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.CumulativeValues = exports.WebRTCStatsCollector = exports.OUTBOUND_RTP_VIDEO_STATS = exports.OUTBOUND_RTP_AUDIO_STATS = exports.INBOUND_RTP_VIDEO_STATS = exports.INBOUND_RTP_AUDIO_STATS = void 0; const WebRTCStats_types_1 = require("./WebRTCStats.types"); const WebRTCStats_1 = require("./WebRTCStats"); const CallReportUtils_1 = require("./CallReportUtils"); const STATS_COLLECTION_INTERVAL_AT_CALL_START_MS = 2000; const COUNT_FOR_INITIAL_INTERVAL = 10; const STATS_COLLECTION_INTERVAL_MS = 10000; exports.INBOUND_RTP_AUDIO_STATS = [ "actualBitrate", "audioLevel", "bytesReceived", "codec", "concealedSamples", "concealmentEvents", "currentRoundTripTime", "jitter", "packetsLost", "packetsReceived", "timestamp", "totalSamplesDuration", "totalSamplesReceived", ]; exports.INBOUND_RTP_VIDEO_STATS = [ "actualBitrate", "bytesReceived", "codec", "currentRoundTripTime", "frameHeight", "frameWidth", "framesDecoded", "framesDropped", "framesReceived", "packetsLost", "packetsReceived", "qpSum", "timestamp", ]; exports.OUTBOUND_RTP_AUDIO_STATS = [ "actualBitrate", "audioLevel", "bytesSent", "codec", "currentRoundTripTime", "packetsSent", "timestamp", "totalSamplesDuration", ]; exports.OUTBOUND_RTP_VIDEO_STATS = [ "actualBitrate", "bytesSent", "codec", "currentRoundTripTime", "frameHeight", "frameWidth", "framesEncoded", "framesSent", "hugeFramesSent", "packetsSent", "qpSum", "timestamp", ]; const RTC_MEDIA_TYPE_MAPPING = { [WebRTCStats_types_1.RtcStatsTypes.InboundRtp]: { [WebRTCStats_types_1.MediaType.Audio]: { STATS: exports.INBOUND_RTP_AUDIO_STATS, }, [WebRTCStats_types_1.MediaType.Video]: { STATS: exports.INBOUND_RTP_VIDEO_STATS, }, }, [WebRTCStats_types_1.RtcStatsTypes.OutboundRtp]: { [WebRTCStats_types_1.MediaType.Audio]: { STATS: exports.OUTBOUND_RTP_AUDIO_STATS, }, [WebRTCStats_types_1.MediaType.Video]: { STATS: exports.OUTBOUND_RTP_VIDEO_STATS, }, }, }; class WebRTCStatsCollector { constructor() { this.getPropertyValuesFromNames = (names, objectWithValues) => names.map((name) => objectWithValues[name]); this.candidatePair = {}; this.codec = new Map(); this.cumulativeValues = new Map(); this.mediaSource = new Map(); this.rawStatsReport = new Map(); this.remoteCandidates = new Map(); this.localCandidates = new Map(); this.tracks = new Map(); this.webRtcStats = new WebRTCStats_1.WebRTCStats(); } startStatsCollection(call) { let statsCounter = 0; this.intervalId = setInterval(() => { statsCounter++; if (statsCounter > COUNT_FOR_INITIAL_INTERVAL) { clearInterval(this.intervalId); this.collectStats(call); this.intervalId = setInterval(() => this.collectStats(call), STATS_COLLECTION_INTERVAL_MS); } else { this.collectStats(call); } }, STATS_COLLECTION_INTERVAL_AT_CALL_START_MS); } stopStatsCollection() { if (this.intervalId) { clearInterval(this.intervalId); this.intervalId = undefined; } } reset() { this.candidatePair = {}; this.codec = new Map(); this.cumulativeValues = new Map(); this.mediaSource = new Map(); this.rawStatsReport = new Map(); this.remoteCandidates = new Map(); this.localCandidates = new Map(); this.tracks = new Map(); this.webRtcStats = new WebRTCStats_1.WebRTCStats(); } getStats() { return this.webRtcStats.getStats(); } getConnectionInfos() { return this.webRtcStats.connectionInfos; } addCodec(codecId, statsEntry) { const codec = this.codec.get(codecId); if (!codec) { return; } statsEntry.codec = codec.mimeType; } addRTT(statsEntry) { var _a; statsEntry.currentRoundTripTime = (_a = this.candidatePair) === null || _a === void 0 ? void 0 : _a.currentRoundTripTime; } addAudioLevelAndTotalSamplesDuration(statsEntry) { const trackId = statsEntry.trackId; const track = this.tracks.get(trackId); if (!track) { return; } const mediaSourceId = track.mediaSourceId; const mediaSource = this.mediaSource.get(mediaSourceId); if (!mediaSource) { return; } statsEntry.audioLevel = mediaSource.audioLevel; statsEntry.totalSamplesDuration = mediaSource.totalSamplesDuration; } addActualBitrate(statsEntry) { const statsKey = statsEntry.id; const timestamp = statsEntry.timestamp; const bytes = statsEntry.bytesReceived ? statsEntry.bytesReceived : statsEntry.bytesSent; const cumulativeValue = this.cumulativeValues.get(statsKey); cumulativeValue.addValue(timestamp, bytes * 8); statsEntry.actualBitrate = cumulativeValue.getCurrentAverageValue(); } addRequiredProps() { this.rawStatsReport.forEach((rawStat) => { this.addCodec(rawStat.codecId, rawStat); this.addRTT(rawStat); this.addAudioLevelAndTotalSamplesDuration(rawStat); this.addActualBitrate(rawStat); }); } updateStatsReport() { const statsReport = this.webRtcStats.statsReport; this.rawStatsReport.forEach((rawStat, key) => { var _a; const statsEntry = statsReport.get(key); const mediaType = (_a = rawStat.mediaType) !== null && _a !== void 0 ? _a : rawStat.kind; const type = rawStat.type; const names = RTC_MEDIA_TYPE_MAPPING[type][mediaType] .STATS; const values = this.getPropertyValuesFromNames(names, rawStat); if (statsEntry) { statsEntry.values.push(values); } else { statsReport.set(key, { id: key, mediaType, type, names, values: [values], }); } }); } updateConnectionInfo() { const localCandidateId = this.candidatePair.localCandidateId; const remoteCandidateId = this.candidatePair.remoteCandidateId; const localCandidate = this.localCandidates.get(localCandidateId); const remoteCandidate = this.remoteCandidates.get(remoteCandidateId); if (localCandidate == undefined || remoteCandidate == undefined) { return; } const connectionInfo = { localIceCandidate: CallReportUtils_1.CallReportUtils.getIceCandidate(localCandidate), remoteIceCandidate: CallReportUtils_1.CallReportUtils.getIceCandidate(remoteCandidate), timestamp: new Date(), }; this.webRtcStats.connectionInfos.push(connectionInfo); } collectStats(call) { return __awaiter(this, void 0, void 0, function* () { try { const rawReport = yield call.getPeerConnectionStats(); if (!rawReport) { return; } rawReport.forEach((rawStatsEntry) => { const type = rawStatsEntry.type; switch (type) { case WebRTCStats_types_1.RtcStatsTypes.Codec: { this.codec.set(rawStatsEntry.id, { codecId: rawStatsEntry.id, mimeType: rawStatsEntry.mimeType, }); break; } case WebRTCStats_types_1.RtcStatsTypes.CandidatePair: { if (rawStatsEntry.state === "succeeded") { this.candidatePair = rawStatsEntry; } break; } case WebRTCStats_types_1.RtcStatsTypes.Track: { this.tracks.set(rawStatsEntry.id, rawStatsEntry); break; } case WebRTCStats_types_1.RtcStatsTypes.MediaSource: { const kind = rawStatsEntry.kind; if (kind === WebRTCStats_types_1.MediaType.Audio) { this.mediaSource.set(rawStatsEntry.id, rawStatsEntry); } break; } case WebRTCStats_types_1.RtcStatsTypes.RemoteCandidate: { this.remoteCandidates.set(rawStatsEntry.id, this.createCandidate(rawStatsEntry)); break; } case WebRTCStats_types_1.RtcStatsTypes.LocalCandidate: { this.localCandidates.set(rawStatsEntry.id, this.createCandidate(rawStatsEntry)); break; } case WebRTCStats_types_1.RtcStatsTypes.InboundRtp: case WebRTCStats_types_1.RtcStatsTypes.OutboundRtp: { if (!this.cumulativeValues.has(rawStatsEntry.id)) { this.cumulativeValues.set(rawStatsEntry.id, new CumulativeValues()); } this.rawStatsReport.set(rawStatsEntry.id, rawStatsEntry); break; } default: break; } }); this.addRequiredProps(); this.updateStatsReport(); this.updateConnectionInfo(); } catch (error) { console.error(error); } }); } createCandidate(rawStatsEntry) { const id = rawStatsEntry.id; const type = rawStatsEntry.type; const candidateType = rawStatsEntry.candidateType; const protocol = rawStatsEntry.protocol; const address = rawStatsEntry.address; const port = rawStatsEntry.port; const timestamp = rawStatsEntry.timestamp; const relayProtocol = rawStatsEntry.relayProtocol; const url = rawStatsEntry.url; return { id, timestamp, type, candidateType, protocol, address, port, relayProtocol, url, }; } } exports.WebRTCStatsCollector = WebRTCStatsCollector; class CumulativeValues { constructor() { this.previousTimestamp = 0; this.previousValue = 0; this.currentAverageValue = 0; } reset(initialTimestamp, initialVal) { this.previousTimestamp = initialTimestamp; this.previousValue = initialVal; this.currentAverageValue = initialVal; } addValue(timestamp, value) { if (value > this.previousValue && timestamp > this.previousTimestamp && this.previousTimestamp !== 0) { const deltaTimestamp = (timestamp - this.previousTimestamp) / 1000000; this.currentAverageValue = (value - this.previousValue) / deltaTimestamp; this.previousTimestamp = timestamp; this.previousValue = value; } else { this.reset(timestamp, value); } } getCurrentAverageValue() { return this.currentAverageValue; } } exports.CumulativeValues = CumulativeValues; //# sourceMappingURL=WebRTCStatsCollector.js.map