infobip-rtc
Version:
Infobip RTC JavaScript SDK - Infobip WebRTC API Implementation
202 lines • 10.1 kB
JavaScript
import { CallStats } from "../CallStats";
import { AudioRemoteMediaStats } from "./audio/AudioRemoteMediaStats";
import { VideoRemoteMediaStats } from "./video/VideoRemoteMediaStats";
import { VideoLocalMediaStats } from "./video/VideoLocalMediaStats";
import { SelectedCandidatePair } from "../transport/SelectedCandidatePair";
import { IceCandidate } from "../transport/IceCandidate";
import { AudioLocalMediaStats } from "./audio/AudioLocalMediaStats";
import { ExtractedStats } from "./ExtractedStats";
export class MediaStatsAdapter {
static extract(rtcStatsReport, isVideo) {
let callStats = new CallStats(isVideo);
let selectedCandidatePair = null;
const extractedRemoteStats = new Map();
const extractedLocalStats = new Map();
rtcStatsReport.forEach((statsReport) => {
if (statsReport.type === "transport" && statsReport.dtlsState === "connected") {
selectedCandidatePair = rtcStatsReport.get(statsReport.selectedCandidatePairId);
}
else if (statsReport.type === "inbound-rtp") {
const inboundRtp = statsReport;
const stats = MediaStatsAdapter.extractInboundRTPStats(inboundRtp);
MediaStatsAdapter.populateCodec(inboundRtp, rtcStatsReport, stats);
extractedRemoteStats.set(statsReport.id, stats);
}
else if (statsReport.type === "outbound-rtp") {
const outboundRtp = statsReport;
const stats = MediaStatsAdapter.extractOutboundRTPStats(outboundRtp);
MediaStatsAdapter.populateCodec(outboundRtp, rtcStatsReport, stats);
extractedLocalStats.set(stats.id, stats);
}
else if (statsReport.type === "remote-inbound-rtp") {
MediaStatsAdapter.extractRTTIfMissing(callStats, statsReport);
}
});
if (!selectedCandidatePair) {
selectedCandidatePair = MediaStatsAdapter.getSelectedCandidatePair(rtcStatsReport);
}
if (selectedCandidatePair) {
MediaStatsAdapter.extractTransportMetrics(callStats, selectedCandidatePair);
}
return new ExtractedStats(callStats, extractedRemoteStats, extractedLocalStats);
}
static populateCodec(statsReport, rtcStatsReport, stats) {
const codecId = statsReport.codecId;
const codecStatsReport = !codecId ? MediaStatsAdapter.extractCodecStatsReport(rtcStatsReport) : rtcStatsReport.get(codecId);
if (!codecStatsReport) {
return;
}
if (stats.video) {
stats.codec = MediaStatsAdapter.extractVideoCodec(codecStatsReport);
}
else {
stats.codec = MediaStatsAdapter.extractAudioCodec(codecStatsReport);
}
}
static extractSelectedCandidatePair(rtcStatsReport) {
let selectedCandidatePair = null;
rtcStatsReport.forEach((statsReport) => {
if (statsReport.type === "transport" && statsReport.selectedCandidatePairId) {
selectedCandidatePair = rtcStatsReport.get(statsReport.selectedCandidatePairId);
}
});
if (!selectedCandidatePair) {
selectedCandidatePair = MediaStatsAdapter.getSelectedCandidatePair(rtcStatsReport);
}
if (!selectedCandidatePair) {
return null;
}
let localCandidate = rtcStatsReport.get(selectedCandidatePair.localCandidateId);
let remoteCandidate = rtcStatsReport.get(selectedCandidatePair.remoteCandidateId);
let local = new IceCandidate(localCandidate);
let remote = new IceCandidate(remoteCandidate);
return new SelectedCandidatePair(local, remote);
}
static getSelectedCandidatePair(stats) {
let selectedCandidatePair = null;
stats.forEach((statsReport) => {
if (statsReport.type === 'candidate-pair' && statsReport.selected) {
selectedCandidatePair = statsReport;
}
});
return selectedCandidatePair;
}
static extractInboundRTPStats(statsReport) {
let stats;
if (statsReport.kind === "audio") {
stats = MediaStatsAdapter.extractAudioRemoteMediaStats(statsReport);
MediaStatsAdapter.extractCommonRemoteMediaStats(stats, statsReport);
}
else {
stats = MediaStatsAdapter.extractVideoRemoteMediaStats(statsReport);
MediaStatsAdapter.extractCommonRemoteMediaStats(stats, statsReport);
}
return stats;
}
static extractOutboundRTPStats(statsReport) {
let stats;
if (statsReport.kind === "audio") {
stats = new AudioLocalMediaStats(statsReport.id);
MediaStatsAdapter.extractCommonLocalMediaStats(stats, statsReport);
}
else {
stats = MediaStatsAdapter.extractVideoLocalMediaStats(statsReport);
MediaStatsAdapter.extractCommonLocalMediaStats(stats, statsReport);
}
return stats;
}
static extractCommonRemoteMediaStats(remoteMediaStats, statsReport) {
remoteMediaStats.packetsReceived = statsReport.packetsReceived;
remoteMediaStats.packetsLost = statsReport.packetsLost;
remoteMediaStats.bytesReceived = statsReport.bytesReceived;
MediaStatsAdapter.extractAverageJitterBufferDelay(remoteMediaStats, statsReport);
}
static extractCommonLocalMediaStats(localMediaStats, statsReport) {
localMediaStats.bytesSent = statsReport.bytesSent;
localMediaStats.packetsSent = statsReport.packetsSent;
localMediaStats.retransmittedBytesSent = statsReport.retransmittedBytesSent;
localMediaStats.retransmittedPacketsSent = statsReport.retransmittedPacketsSent;
}
static extractAudioRemoteMediaStats(statsReport) {
const audioRemoteMediaStats = new AudioRemoteMediaStats(statsReport.id);
audioRemoteMediaStats.jitter = statsReport.jitter * 1000;
audioRemoteMediaStats.concealedSamples = statsReport.concealedSamples;
audioRemoteMediaStats.concealmentEvents = statsReport.concealmentEvents;
return audioRemoteMediaStats;
}
static extractVideoRemoteMediaStats(statsReport) {
const videoRemoteMediaStats = new VideoRemoteMediaStats(statsReport.id);
videoRemoteMediaStats.frameWidth = statsReport.frameWidth;
videoRemoteMediaStats.frameHeight = statsReport.frameHeight;
videoRemoteMediaStats.framesReceived = statsReport.framesReceived;
videoRemoteMediaStats.framesDropped = statsReport.framesDropped;
videoRemoteMediaStats.framesDecoded = statsReport.framesDecoded;
videoRemoteMediaStats.keyFramesDecoded = statsReport.keyFramesDecoded;
videoRemoteMediaStats.nackCount = statsReport.nackCount;
videoRemoteMediaStats.firCount = statsReport.firCount;
videoRemoteMediaStats.pliCount = statsReport.pliCount;
videoRemoteMediaStats.qpSum = statsReport.qpSum;
if (videoRemoteMediaStats.framesDecoded > 0) {
videoRemoteMediaStats.averageQP = Math.round(videoRemoteMediaStats.qpSum / videoRemoteMediaStats.framesDecoded);
}
return videoRemoteMediaStats;
}
static extractVideoLocalMediaStats(statsReport) {
const videoLocalMediaStats = new VideoLocalMediaStats(statsReport.id);
videoLocalMediaStats.frameWidth = statsReport.frameWidth;
videoLocalMediaStats.frameHeight = statsReport.frameHeight;
videoLocalMediaStats.framesSent = statsReport.framesSent;
videoLocalMediaStats.framesEncoded = statsReport.framesEncoded;
videoLocalMediaStats.keyFramesEncoded = statsReport.keyFramesEncoded;
videoLocalMediaStats.nackCount = statsReport.nackCount;
videoLocalMediaStats.firCount = statsReport.firCount;
videoLocalMediaStats.pliCount = statsReport.pliCount;
videoLocalMediaStats.qpSum = statsReport.qpSum;
if (videoLocalMediaStats.framesEncoded > 0) {
videoLocalMediaStats.averageQP = Math.round(videoLocalMediaStats.qpSum / videoLocalMediaStats.framesEncoded);
}
videoLocalMediaStats.qualityLimitationReason = statsReport.qualityLimitationReason;
return videoLocalMediaStats;
}
static extractAverageJitterBufferDelay(remoteMediaStats, statsReport) {
remoteMediaStats.jitterBufferDelay = statsReport.jitterBufferDelay;
remoteMediaStats.jitterBufferEmittedCount = statsReport.jitterBufferEmittedCount;
if (remoteMediaStats.jitterBufferEmittedCount > 0) {
remoteMediaStats.averageJitterBufferDelay = Math.round(remoteMediaStats.jitterBufferDelay / remoteMediaStats.jitterBufferEmittedCount);
}
return remoteMediaStats;
}
static extractTransportMetrics(callStats, statsReport) {
callStats.bytesReceived = statsReport.bytesReceived;
callStats.bytesSent = statsReport.bytesSent;
callStats.availableOutgoingBitrate = statsReport.availableOutgoingBitrate;
if (statsReport.currentRoundTripTime) {
callStats.currentRoundTripTime = statsReport.currentRoundTripTime * 1000;
}
}
static extractRTTIfMissing(callStats, statsReport) {
if (!callStats.currentRoundTripTime) {
callStats.currentRoundTripTime = statsReport.roundTripTime * 1000;
}
}
static extractCodecStatsReport(rtcStatsReport) {
return Array.from(rtcStatsReport.values()).find((statsReport) => {
return statsReport.type === "codec";
});
}
static extractAudioCodec(codecStatsReport) {
let codec = codecStatsReport.mimeType;
if (codec) {
return codec.toLowerCase().replace("audio/", "");
}
return null;
}
static extractVideoCodec(codecStatsReport) {
let codec = codecStatsReport.mimeType;
if (codec) {
return codec.toLowerCase().replace("video/", "");
}
return null;
}
}
//# sourceMappingURL=MediaStatsAdapter.js.map