UNPKG

@observertc/observer-js

Version:

Server Side NodeJS Library for processing ObserveRTC Samples

774 lines (773 loc) 38.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ObservedPeerConnection = void 0; const events_1 = require("events"); const ObservedInboundRtp_1 = require("./ObservedInboundRtp"); const logger_1 = require("./common/logger"); const ObservedOutboundRtp_1 = require("./ObservedOutboundRtp"); const ObservedCertificate_1 = require("./ObservedCertificate"); const ObservedCodec_1 = require("./ObservedCodec"); const ObservedDataChannel_1 = require("./ObservedDataChannel"); const ObservedIceCandidate_1 = require("./ObservedIceCandidate"); const ObservedIceCandidatePair_1 = require("./ObservedIceCandidatePair"); const ObservedIceTransport_1 = require("./ObservedIceTransport"); const ObservedMediaSource_1 = require("./ObservedMediaSource"); const ObservedPeerConnectionTransport_1 = require("./ObservedPeerConnectionTransport"); const ObservedMediaPlayout_1 = require("./ObservedMediaPlayout"); const ObservedRemoteInboundRtp_1 = require("./ObservedRemoteInboundRtp"); const ObservedRemoteOutboundRtp_1 = require("./ObservedRemoteOutboundRtp"); const ObservedInboundTrack_1 = require("./ObservedInboundTrack"); const ObservedOutboundTrack_1 = require("./ObservedOutboundTrack"); const utils_1 = require("./common/utils"); const logger = (0, logger_1.createLogger)('ObservedPeerConnection'); class ObservedPeerConnection extends events_1.EventEmitter { peerConnectionId; client; _visited = true; appData; calculatedScore = { weight: 1, value: undefined, }; closed = false; // timestamp of the PEER_CONNECTION_OPENED event openedAt; // timestamp of the PEER_CONNECTION_CLOSED event closedAt; updated = Date.now(); connectionState; iceConnectionState; iceGatheringState; availableIncomingBitrate = 0; availableOutgoingBitrate = 0; totalInboundPacketsLost = 0; totalInboundPacketsReceived = 0; totalOutboundPacketsSent = 0; totalDataChannelBytesSent = 0; totalDataChannelBytesReceived = 0; totalDataChannelMessagesSent = 0; totalDataChannelMessagesReceived = 0; totalSentAudioBytes = 0; totalSentVideoBytes = 0; totalSentAudioPackets = 0; totalSentVideoPackets = 0; totalReceivedAudioPacktes = 0; totalReceivedVideoPackets = 0; totalReceivedAudioBytes = 0; totalReceivedVideoBytes = 0; deltaInboundPacketsLost = 0; deltaInboundPacketsReceived = 0; deltaOutboundPacketsSent = 0; deltaDataChannelBytesSent = 0; deltaDataChannelBytesReceived = 0; deltaDataChannelMessagesSent = 0; deltaDataChannelMessagesReceived = 0; deltaInboundReceivedBytes = 0; deltaOutboundSentBytes = 0; deltaReceivedAudioBytes = 0; deltaReceivedVideoBytes = 0; deltaReceivedAudioPackets = 0; deltaReceivedVideoPackets = 0; deltaSentAudioBytes = 0; deltaSentVideoBytes = 0; deltaTransportSentBytes = 0; deltaTransportReceivedBytes = 0; receivingPacketsPerSecond = 0; sendingPacketsPerSecond = 0; sendingAudioBitrate = 0; sendingVideoBitrate = 0; receivingAudioBitrate = 0; receivingVideoBitrate = 0; currentRttInMs; currentJitter; usingTCP = false; usingTURN = false; observedTurnServer; observedCertificates = new Map(); observedCodecs = new Map(); observedDataChannels = new Map(); observedIceCandidates = new Map(); observedIceCandidatesPair = new Map(); observedIceTransports = new Map(); observedInboundRtps = new Map(); observedInboundTracks = new Map(); observedMediaPlayouts = new Map(); observedMediaSources = new Map(); observedOutboundRtps = new Map(); observedOutboundTracks = new Map(); observedPeerConnectionTransports = new Map(); observedRemoteInboundRtps = new Map(); observedRemoteOutboundRtps = new Map(); constructor(peerConnectionId, client) { super(); this.peerConnectionId = peerConnectionId; this.client = client; this.setMaxListeners(Infinity); } get score() { return this.calculatedScore.value; } get visited() { const visited = this._visited; this._visited = false; return visited; } get codecs() { return [...this.observedCodecs.values()]; } get inboundRtps() { return [...this.observedInboundRtps.values()]; } get remoteOutboundRtps() { return [...this.observedRemoteOutboundRtps.values()]; } get outboundRtps() { return [...this.observedOutboundRtps.values()]; } get remoteInboundRtps() { return [...this.observedRemoteInboundRtps.values()]; } get mediaSources() { return [...this.observedMediaSources.values()]; } get mediaPlayouts() { return [...this.observedMediaPlayouts.values()]; } get dataChannels() { return [...this.observedDataChannels.values()]; } get peerConnectionTransports() { return [...this.observedPeerConnectionTransports.values()]; } get iceTransports() { return [...this.observedIceTransports.values()]; } get iceCandidates() { return [...this.observedIceCandidates.values()]; } get iceCandidatePairs() { return [...this.observedIceCandidatesPair.values()]; } get certificates() { return [...this.observedCertificates.values()]; } get selectedIceCandidatePairs() { return this.iceTransports.map((iceTransport) => iceTransport.getSelectedCandidatePair()) .filter((pair) => pair !== undefined); } get selectedIceCandiadtePairForTurn() { return this.selectedIceCandidatePairs .filter((pair) => pair.getLocalCandidate()?.candidateType === 'relay' && pair.getRemoteCandidate()?.url?.startsWith('turn:')); } close() { if (this.closed) return; this.closed = true; this.observedCertificates.clear(); this.observedCodecs.clear(); this.observedDataChannels.clear(); this.observedIceCandidates.clear(); this.observedIceCandidatesPair.clear(); this.observedIceTransports.clear(); this.observedInboundRtps.clear(); this.observedInboundTracks.clear(); this.observedMediaPlayouts.clear(); this.observedMediaSources.clear(); this.observedOutboundRtps.clear(); this.observedOutboundTracks.clear(); this.observedPeerConnectionTransports.clear(); this.observedRemoteInboundRtps.clear(); this.observedRemoteOutboundRtps.clear(); this.client.call.observer.observedTURN.removePeerConnection(this); if (!this.closedAt) this.closedAt = Date.now(); this.emit('close'); } accept(sample) { if (this.closed) return; this._visited = true; this.availableIncomingBitrate = 0; this.availableOutgoingBitrate = 0; this.deltaInboundPacketsLost = 0; this.deltaInboundPacketsReceived = 0; this.deltaOutboundPacketsSent = 0; this.deltaDataChannelBytesSent = 0; this.deltaDataChannelBytesReceived = 0; this.deltaInboundReceivedBytes = 0; this.deltaOutboundSentBytes = 0; this.deltaReceivedAudioBytes = 0; this.deltaReceivedVideoBytes = 0; this.deltaReceivedAudioPackets = 0; this.deltaReceivedVideoPackets = 0; this.deltaSentAudioBytes = 0; this.deltaSentVideoBytes = 0; this.deltaTransportReceivedBytes = 0; this.deltaTransportSentBytes = 0; this.sendingAudioBitrate = 0; this.sendingVideoBitrate = 0; this.receivingAudioBitrate = 0; this.receivingVideoBitrate = 0; const now = Date.now(); const elapsedTimeInMs = now - this.updated; const elapsedTimeInSec = elapsedTimeInMs / 1000; const rttMeasurementsInSec = []; const jitterMeasurements = []; if (sample.certificates) { for (const certificate of sample.certificates) { this._updateCertificateStats(certificate); } } if (sample.codecs) { for (const codec of sample.codecs) { this._updateCodecStats(codec); } } if (sample.dataChannels) { for (const dataChannel of sample.dataChannels) { const observedDataChannel = this._updateDataChannelStats(dataChannel); if (!observedDataChannel) continue; this.deltaDataChannelBytesSent += observedDataChannel.deltaBytesSent; this.deltaDataChannelBytesReceived += observedDataChannel.deltaBytesReceived; this.deltaDataChannelMessagesSent += observedDataChannel.deltaMessagesSent; this.deltaDataChannelMessagesReceived += observedDataChannel.deltaMessagesReceived; } } if (sample.iceCandidates) { for (const iceCandidate of sample.iceCandidates) { this._updateIceCandidateStats(iceCandidate); } } if (sample.iceCandidatePairs) { for (const iceCandidatePair of sample.iceCandidatePairs) { const observedCandidatePair = this._updateIceCandidatePairStats(iceCandidatePair); if (!observedCandidatePair) continue; if (observedCandidatePair.currentRoundTripTime) { rttMeasurementsInSec.push(observedCandidatePair.currentRoundTripTime); } if (observedCandidatePair.availableIncomingBitrate) { this.availableIncomingBitrate += observedCandidatePair.availableIncomingBitrate; } if (observedCandidatePair.availableOutgoingBitrate) { this.availableOutgoingBitrate += observedCandidatePair.availableOutgoingBitrate; } } } if (sample.iceTransports) { for (const iceTransport of sample.iceTransports) { const observedIceTransport = this._updateIceTransportStats(iceTransport); if (!observedIceTransport) return; observedIceTransport.bytesReceived; } } if (sample.inboundRtps) { for (const inboundRtp of sample.inboundRtps) { const observedInboundRtp = this._updateInboundRtpStats(inboundRtp); if (!observedInboundRtp) continue; this.deltaInboundPacketsLost += observedInboundRtp.deltaLostPackets; this.deltaInboundPacketsReceived += observedInboundRtp.deltaReceivedPackets; this.deltaInboundReceivedBytes += observedInboundRtp.deltaBytesReceived; switch (inboundRtp.kind) { case 'audio': this.deltaReceivedAudioBytes += observedInboundRtp.deltaBytesReceived; this.deltaReceivedAudioPackets += observedInboundRtp.deltaReceivedPackets; break; case 'video': this.deltaReceivedVideoBytes += observedInboundRtp.deltaBytesReceived; this.deltaReceivedVideoPackets += observedInboundRtp.deltaReceivedPackets; break; } if (observedInboundRtp.jitter) { jitterMeasurements.push(observedInboundRtp.jitter); } } } if (sample.mediaPlayouts) { for (const mediaPlayout of sample.mediaPlayouts) { this._updateMediaPlayoutStats(mediaPlayout); } } if (sample.mediaSources) { for (const mediaSource of sample.mediaSources) { this._updateMediaSourceStats(mediaSource); } } if (sample.outboundRtps) { for (const outboundRtp of sample.outboundRtps) { const observedOutboundRtp = this._updateOutboundRtpStats(outboundRtp); if (!observedOutboundRtp) continue; this.deltaOutboundPacketsSent += observedOutboundRtp.deltaPacketsSent ?? 0; this.deltaOutboundSentBytes += observedOutboundRtp.deltaBytesSent ?? 0; switch (outboundRtp.kind) { case 'audio': this.deltaSentAudioBytes += observedOutboundRtp.deltaBytesSent; this.deltaSentAudioBytes += observedOutboundRtp.deltaPacketsSent; break; case 'video': this.deltaSentVideoBytes += observedOutboundRtp.deltaBytesSent; this.deltaSentVideoBytes += observedOutboundRtp.deltaPacketsSent; break; } } } if (sample.peerConnectionTransports) { for (const peerConnectionTransport of sample.peerConnectionTransports) { const observedTransport = this._updatePeerConnectionTransportStats(peerConnectionTransport); if (!observedTransport) continue; } } if (sample.remoteInboundRtps) { for (const remoteInboundRtp of sample.remoteInboundRtps) { const observedRemoteInboundRtp = this._updateRemoteInboundRtpStats(remoteInboundRtp); if (!observedRemoteInboundRtp) continue; if (observedRemoteInboundRtp.roundTripTime) { rttMeasurementsInSec.push(observedRemoteInboundRtp.roundTripTime); } } } if (sample.remoteOutboundRtps) { for (const remoteOutboundRtp of sample.remoteOutboundRtps) { const observedRemoteOutboundRtp = this._updateRemoteOutboundRtpStats(remoteOutboundRtp); if (!observedRemoteOutboundRtp) continue; } } // tracks should be updated last as they are derived stats // and depends on base stats but they all received in the sample sample if (sample.inboundTracks) { for (const inboundTrack of sample.inboundTracks) { this._updateInboundTrackSample(inboundTrack); } } if (sample.outboundTracks) { for (const outboundTrack of sample.outboundTracks) { this._updateOutboundTrackSample(outboundTrack); } } this.totalInboundPacketsLost += this.deltaInboundPacketsLost; this.totalInboundPacketsReceived += this.deltaInboundPacketsReceived; this.totalOutboundPacketsSent += this.deltaOutboundPacketsSent; this.totalDataChannelBytesSent += this.deltaDataChannelBytesSent; this.totalDataChannelBytesReceived += this.deltaDataChannelBytesReceived; this.totalDataChannelMessagesSent += this.deltaDataChannelMessagesSent; this.totalDataChannelMessagesReceived += this.deltaDataChannelMessagesReceived; this.totalReceivedAudioBytes += this.deltaReceivedAudioBytes; this.totalReceivedVideoBytes += this.deltaReceivedVideoBytes; this.totalSentAudioBytes += this.deltaSentAudioBytes; this.totalSentVideoBytes += this.deltaSentVideoBytes; this.totalReceivedAudioPacktes += this.deltaReceivedAudioPackets; this.totalReceivedVideoPackets += this.deltaReceivedVideoPackets; this.totalSentAudioPackets += this.deltaSentAudioBytes; this.totalSentVideoPackets += this.deltaSentVideoBytes; this.receivingPacketsPerSecond = this.deltaInboundPacketsReceived / elapsedTimeInSec; this.sendingPacketsPerSecond = this.deltaOutboundPacketsSent / elapsedTimeInSec; this.sendingAudioBitrate = (this.deltaSentAudioBytes * 8) / elapsedTimeInSec; this.sendingVideoBitrate = (this.deltaSentVideoBytes * 8) / elapsedTimeInSec; this.receivingAudioBitrate = (this.deltaReceivedAudioBytes * 8) / elapsedTimeInSec; this.receivingVideoBitrate = (this.deltaReceivedVideoBytes * 8) / elapsedTimeInSec; if (rttMeasurementsInSec.length > 0) { this.currentRttInMs = (0, utils_1.getMedian)(rttMeasurementsInSec, false) * 1000; } else { this.currentRttInMs = undefined; } if (jitterMeasurements.length > 0) { this.currentJitter = (0, utils_1.getMedian)(jitterMeasurements, false); } else { this.currentJitter = undefined; } const wasUsingTURN = this.usingTURN; const selectedIceCandidatePairs = this.selectedIceCandidatePairs; const selectedCandidatePairForTurn = []; this.usingTCP = false; this.usingTURN = false; for (const selectedCandidatePair of selectedIceCandidatePairs) { if (selectedCandidatePair.getLocalCandidate()?.protocol === 'tcp') { this.usingTCP = true; } if (selectedCandidatePair.getLocalCandidate()?.candidateType === 'relay' && selectedCandidatePair.getRemoteCandidate()?.url?.startsWith('turn:')) { selectedCandidatePairForTurn.push(selectedCandidatePair); this.usingTURN = true; } this.deltaTransportReceivedBytes += selectedCandidatePair.deltaBytesReceived; this.deltaTransportSentBytes += selectedCandidatePair.deltaBytesSent; } if (this.usingTURN) { if (!this.observedTurnServer) { this.observedTurnServer = this.client.call.observer.observedTURN.addPeerConnection(this); } this.observedTurnServer?.updateTurnUsage(...selectedCandidatePairForTurn); } else if (wasUsingTURN) { if (!this.usingTURN) { this.client.call.observer.observedTURN.removePeerConnection(this); } } this.calculatedScore.value = sample.score; this.updated = now; this._checkVisited(); this.emit('update'); } _checkVisited() { for (const certificate of [...this.observedCertificates.values()]) { if (certificate.visited) continue; this.observedCertificates.delete(certificate.id); } for (const codec of [...this.observedCodecs.values()]) { if (codec.visited) continue; this.observedCodecs.delete(codec.id); this.emit('removed-codec', codec); } for (const dataChannel of [...this.observedDataChannels.values()]) { if (dataChannel.visited) continue; this.observedDataChannels.delete(dataChannel.id); this.emit('removed-data-channel', dataChannel); } for (const iceCandidate of [...this.observedIceCandidates.values()]) { if (iceCandidate.visited) continue; this.observedIceCandidates.delete(iceCandidate.id); this.emit('removed-ice-candidate', iceCandidate); } for (const iceCandidatePair of [...this.observedIceCandidatesPair.values()]) { if (iceCandidatePair.visited) continue; this.observedIceCandidatesPair.delete(iceCandidatePair.id); this.emit('removed-ice-candidate-pair', iceCandidatePair); } for (const iceTransport of [...this.observedIceTransports.values()]) { if (iceTransport.visited) continue; this.observedIceTransports.delete(iceTransport.id); this.emit('removed-ice-transport', iceTransport); } for (const inboundRtp of [...this.observedInboundRtps.values()]) { if (inboundRtp.visited) continue; this.observedInboundRtps.delete(inboundRtp.ssrc); this.emit('removed-inbound-rtp', inboundRtp); } for (const inboundTrack of [...this.observedInboundTracks.values()]) { if (inboundTrack.visited) continue; this.observedInboundTracks.delete(inboundTrack.id); this.emit('removed-inbound-track', inboundTrack); } for (const mediaPlayout of [...this.observedMediaPlayouts.values()]) { if (mediaPlayout.visited) continue; this.observedMediaPlayouts.delete(mediaPlayout.id); this.emit('removed-media-playout', mediaPlayout); } for (const mediaSource of [...this.observedMediaSources.values()]) { if (mediaSource.visited) continue; this.observedMediaSources.delete(mediaSource.id); this.emit('removed-media-source', mediaSource); } for (const outboundRtp of [...this.observedOutboundRtps.values()]) { if (outboundRtp.visited) continue; this.observedOutboundRtps.delete(outboundRtp.ssrc); this.emit('removed-outbound-rtp', outboundRtp); } for (const outboundTrack of [...this.observedOutboundTracks.values()]) { if (outboundTrack.visited) continue; this.observedOutboundTracks.delete(outboundTrack.id); this.emit('removed-outbound-track', outboundTrack); } for (const peerConnectionTransport of [...this.observedPeerConnectionTransports.values()]) { if (peerConnectionTransport.visited) continue; this.observedPeerConnectionTransports.delete(peerConnectionTransport.id); this.emit('removed-peer-connection-transport', peerConnectionTransport); } for (const remoteInboundRtp of [...this.observedRemoteInboundRtps.values()]) { if (remoteInboundRtp.visited) continue; this.observedRemoteInboundRtps.delete(remoteInboundRtp.ssrc); this.emit('removed-remote-inbound-rtp', remoteInboundRtp); } for (const remoteOutboundRtp of [...this.observedRemoteOutboundRtps.values()]) { if (remoteOutboundRtp.visited) continue; this.observedRemoteOutboundRtps.delete(remoteOutboundRtp.ssrc); this.emit('removed-remote-outbound-rtp', remoteOutboundRtp); } } _updateCertificateStats(stats) { let observedCertificate = this.observedCertificates.get(stats.id); if (!observedCertificate) { if (!stats.timestamp || !stats.id || !stats.fingerprint) { return logger.warn(`ObservedPeerConnection received an invalid CertificateStats (missing timestamp OR id OR fingerprint field). PeerConnectionId: ${this.peerConnectionId} ClientId: ${this.client.clientId}, CallId: ${this.client.call.callId}`, stats); } observedCertificate = new ObservedCertificate_1.ObservedCertificate(stats.timestamp, stats.id, this); observedCertificate.update(stats); this.observedCertificates.set(stats.id, observedCertificate); this.emit('added-certificate', observedCertificate); } else { observedCertificate.update(stats); this.emit('updated-certificate', observedCertificate); } return observedCertificate; } _updateCodecStats(stats) { let observedCodec = this.observedCodecs.get(stats.id); if (!observedCodec) { if (!stats.timestamp || !stats.id || !stats.mimeType) { return logger.warn(`ObservedPeerConnection received an invalid CodecStats (missing timestamp OR id OR mimeType field). PeerConnectionId: ${this.peerConnectionId} ClientId: ${this.client.clientId}, CallId: ${this.client.call.callId}`, stats); } observedCodec = new ObservedCodec_1.ObservedCodec(stats.timestamp, stats.id, stats.mimeType, this); observedCodec.update(stats); this.observedCodecs.set(stats.id, observedCodec); this.emit('added-codec', observedCodec); } else { observedCodec.update(stats); } this.emit('updated-codec', observedCodec); return observedCodec; } _updateDataChannelStats(stats) { let observedDataChannel = this.observedDataChannels.get(stats.id); if (!observedDataChannel) { if (!stats.timestamp || !stats.id) { return logger.warn(`ObservedPeerConnection received an invalid DataChannelStats (missing timestamp OR id field). PeerConnectionId: ${this.peerConnectionId} ClientId: ${this.client.clientId}, CallId: ${this.client.call.callId}`, stats); } observedDataChannel = new ObservedDataChannel_1.ObservedDataChannel(stats.timestamp, stats.id, this); observedDataChannel.update(stats); this.observedDataChannels.set(stats.id, observedDataChannel); this.emit('added-data-channel', observedDataChannel); } else { observedDataChannel.update(stats); } this.emit('updated-data-channel', observedDataChannel); return observedDataChannel; } _updateIceCandidateStats(stats) { let observedIceCandidate = this.observedIceCandidates.get(stats.id); if (!observedIceCandidate) { if (!stats.timestamp || !stats.id) { return logger.warn(`ObservedPeerConnection received an invalid IceCandidateStats (missing timestamp OR id field). PeerConnectionId: ${this.peerConnectionId} ClientId: ${this.client.clientId}, CallId: ${this.client.call.callId}`, stats); } observedIceCandidate = new ObservedIceCandidate_1.ObservedIceCandidate(stats.timestamp, stats.id, this); observedIceCandidate.update(stats); this.observedIceCandidates.set(stats.id, observedIceCandidate); this.emit('added-ice-candidate', observedIceCandidate); } else { observedIceCandidate.update(stats); } this.emit('updated-ice-candidate', observedIceCandidate); return observedIceCandidate; } _updateIceCandidatePairStats(stats) { let observedIceCandidatePair = this.observedIceCandidatesPair.get(stats.id); if (!observedIceCandidatePair) { if (!stats.timestamp || !stats.id) { return logger.warn(`ObservedPeerConnection received an invalid IceCandidateStats (missing timestamp OR id field). PeerConnectionId: ${this.peerConnectionId} ClientId: ${this.client.clientId}, CallId: ${this.client.call.callId}`, stats); } observedIceCandidatePair = new ObservedIceCandidatePair_1.ObservedIceCandidatePair(stats.timestamp, stats.id, this); observedIceCandidatePair.update(stats); this.observedIceCandidatesPair.set(stats.id, observedIceCandidatePair); this.emit('added-ice-candidate-pair', observedIceCandidatePair); } else { observedIceCandidatePair.update(stats); } this.emit('updated-ice-candidate-pair', observedIceCandidatePair); return observedIceCandidatePair; } _updateIceTransportStats(stats) { let observedIceTransport = this.observedIceTransports.get(stats.id); if (!observedIceTransport) { if (!stats.timestamp || !stats.id) { return logger.warn(`ObservedPeerConnection received an invalid IceCandidateStats (missing timestamp OR id field). PeerConnectionId: ${this.peerConnectionId} ClientId: ${this.client.clientId}, CallId: ${this.client.call.callId}`, stats); } observedIceTransport = new ObservedIceTransport_1.ObservedIceTransport(stats.timestamp, stats.id, this); observedIceTransport.update(stats); this.observedIceTransports.set(stats.id, observedIceTransport); this.emit('added-ice-transport', observedIceTransport); } else { observedIceTransport.update(stats); } this.emit('updated-ice-transport', observedIceTransport); return observedIceTransport; } _updateInboundRtpStats(stats) { let observedInboundRtp = this.observedInboundRtps.get(stats.ssrc); if (!observedInboundRtp) { if (!stats.timestamp || !stats.id || !stats.ssrc || !stats.kind || !stats.trackIdentifier) { return logger.warn(`ObservedPeerConnection received an invalid InboundRtpStats (missing timestamp OR id OR ssrc OR kind OR trackIdentifier field). PeerConnectionId: ${this.peerConnectionId} ClientId: ${this.client.clientId}, CallId: ${this.client.call.callId}`, stats); } observedInboundRtp = new ObservedInboundRtp_1.ObservedInboundRtp(stats.timestamp, stats.id, stats.ssrc, stats.kind, stats.trackIdentifier, this); observedInboundRtp.update(stats); this.observedInboundRtps.set(stats.ssrc, observedInboundRtp); this.emit('added-inbound-rtp', observedInboundRtp); } else { observedInboundRtp.update(stats); } this.emit('updated-inbound-rtp', observedInboundRtp); return observedInboundRtp; } _updateInboundTrackSample(stats) { let observedInboundTrack = this.observedInboundTracks.get(stats.id); if (!observedInboundTrack) { if (!stats.timestamp || !stats.id || !stats.kind) { return logger.warn(`ObservedPeerConnection received an invalid InboundTrackSample (missing timestamp OR id OR kind field). PeerConnectionId: ${this.peerConnectionId} ClientId: ${this.client.clientId}, CallId: ${this.client.call.callId}`, stats); } const inboundRtp = [...this.observedInboundRtps.values()].find((inbRtp) => inbRtp.trackIdentifier === stats.id); const mediaPlayout = inboundRtp ? [...this.observedMediaPlayouts.values()].find((mp) => mp.id === inboundRtp.playoutId) : undefined; observedInboundTrack = new ObservedInboundTrack_1.ObservedInboundTrack(stats.timestamp, stats.id, stats.kind, this, inboundRtp, mediaPlayout); observedInboundTrack.update(stats); this.observedInboundTracks.set(stats.id, observedInboundTrack); this.emit('added-inbound-track', observedInboundTrack); } else { observedInboundTrack.update(stats); } this.emit('updated-inbound-track', observedInboundTrack); return observedInboundTrack; } _updateMediaPlayoutStats(stats) { let observedMediaPlayout = this.observedMediaPlayouts.get(stats.id); if (!observedMediaPlayout) { if (!stats.timestamp || !stats.id || !stats.kind) { return logger.warn(`ObservedPeerConnection received an invalid InboundRtpStats (missing timestamp OR id OR kind OR trackIdentifier field). PeerConnectionId: ${this.peerConnectionId} ClientId: ${this.client.clientId}, CallId: ${this.client.call.callId}`, stats); } observedMediaPlayout = new ObservedMediaPlayout_1.ObservedMediaPlayout(stats.timestamp, stats.id, stats.kind, this); observedMediaPlayout.update(stats); this.observedMediaPlayouts.set(stats.id, observedMediaPlayout); this.emit('added-media-playout', observedMediaPlayout); } else { observedMediaPlayout.update(stats); } this.emit('updated-media-playout', observedMediaPlayout); return observedMediaPlayout; } _updateMediaSourceStats(stats) { let observedMediaSource = this.observedMediaSources.get(stats.id); if (!observedMediaSource) { if (!stats.timestamp || !stats.id || !stats.kind) { return logger.warn(`ObservedPeerConnection received an invalid InboundRtpStats (missing timestamp OR id OR kind field). PeerConnectionId: ${this.peerConnectionId} ClientId: ${this.client.clientId}, CallId: ${this.client.call.callId}`, stats); } observedMediaSource = new ObservedMediaSource_1.ObservedMediaSource(stats.timestamp, stats.id, stats.kind, this); observedMediaSource.update(stats); this.observedMediaSources.set(stats.id, observedMediaSource); this.emit('added-media-source', observedMediaSource); } else { observedMediaSource.update(stats); } this.emit('updated-media-source', observedMediaSource); return observedMediaSource; } _updateOutboundRtpStats(stats) { let observedOutboundRtp = this.observedOutboundRtps.get(stats.ssrc); if (!observedOutboundRtp) { if (!stats.timestamp || !stats.id || !stats.ssrc || !stats.kind) { return logger.warn(`ObservedPeerConnection received an invalid OutboundRtpStats (missing timestamp OR id OR ssrc OR kind field). PeerConnectionId: ${this.peerConnectionId} ClientId: ${this.client.clientId}, CallId: ${this.client.call.callId}`, stats); } observedOutboundRtp = new ObservedOutboundRtp_1.ObservedOutboundRtp(stats.timestamp, stats.id, stats.ssrc, stats.kind, this); observedOutboundRtp.update(stats); this.observedOutboundRtps.set(stats.ssrc, observedOutboundRtp); this.emit('added-outbound-rtp', observedOutboundRtp); } else { observedOutboundRtp.update(stats); } this.emit('updated-outbound-rtp', observedOutboundRtp); return observedOutboundRtp; } _updateOutboundTrackSample(stats) { let observedOutboundTrack = this.observedOutboundTracks.get(stats.id); if (!observedOutboundTrack) { if (!stats.timestamp || !stats.id || !stats.kind) { return logger.warn(`ObservedPeerConnection received an invalid OutboundTrackSample (missing timestamp OR id OR kind field). PeerConnectionId: ${this.peerConnectionId} ClientId: ${this.client.clientId}, CallId: ${this.client.call.callId}`, stats); } const observedMediaSource = [...this.observedMediaSources.values()].find((mediaSource) => mediaSource.trackIdentifier === stats.id); const outboundRtps = observedMediaSource ? [...this.observedOutboundRtps.values()].filter((outboundRtp) => outboundRtp.mediaSourceId === observedMediaSource?.id) : undefined; observedOutboundTrack = new ObservedOutboundTrack_1.ObservedOutboundTrack(stats.timestamp, stats.id, stats.kind, this, outboundRtps, observedMediaSource); observedOutboundTrack.update(stats); this.observedOutboundTracks.set(stats.id, observedOutboundTrack); this.emit('added-outbound-track', observedOutboundTrack); } else { observedOutboundTrack.update(stats); } this.emit('updated-outbound-track', observedOutboundTrack); return observedOutboundTrack; } _updatePeerConnectionTransportStats(stats) { let observedPeerConnectionTransport = this.observedPeerConnectionTransports.get(stats.id); if (!observedPeerConnectionTransport) { if (!stats.timestamp || !stats.id) { return logger.warn(`ObservedPeerConnection received an invalid PeerConnectionTransportStats (missing timestamp OR id field). PeerConnectionId: ${this.peerConnectionId} ClientId: ${this.client.clientId}, CallId: ${this.client.call.callId}`, stats); } observedPeerConnectionTransport = new ObservedPeerConnectionTransport_1.ObservedPeerConnectionTransport(stats.timestamp, stats.id, this); observedPeerConnectionTransport.update(stats); this.observedPeerConnectionTransports.set(stats.id, observedPeerConnectionTransport); this.emit('added-peer-connection-transport', observedPeerConnectionTransport); } else { observedPeerConnectionTransport.update(stats); } this.emit('updated-peer-connection-transport', observedPeerConnectionTransport); return observedPeerConnectionTransport; } _updateRemoteInboundRtpStats(stats) { let observedRemoteInboundRtp = this.observedRemoteInboundRtps.get(stats.ssrc); if (!observedRemoteInboundRtp) { if (!stats.timestamp || !stats.id || !stats.ssrc || !stats.kind) { return logger.warn(`ObservedPeerConnection received an invalid RemoteInboundRtpStats (missing timestamp OR id OR ssrc OR kind field). PeerConnectionId: ${this.peerConnectionId} ClientId: ${this.client.clientId}, CallId: ${this.client.call.callId}`, stats); } observedRemoteInboundRtp = new ObservedRemoteInboundRtp_1.ObservedRemoteInboundRtp(stats.timestamp, stats.id, stats.ssrc, stats.kind, this); observedRemoteInboundRtp.update(stats); this.observedRemoteInboundRtps.set(stats.ssrc, observedRemoteInboundRtp); this.emit('added-remote-inbound-rtp', observedRemoteInboundRtp); } else { observedRemoteInboundRtp.update(stats); } this.emit('updated-remote-inbound-rtp', observedRemoteInboundRtp); return observedRemoteInboundRtp; } _updateRemoteOutboundRtpStats(stats) { let observedRemoteOutboundRtp = this.observedRemoteOutboundRtps.get(stats.ssrc); if (!observedRemoteOutboundRtp) { if (!stats.timestamp || !stats.id || !stats.ssrc || !stats.kind) { return logger.warn(`ObservedPeerConnection received an invalid RemoteOutboundRtpStats (missing timestamp OR id OR ssrc OR kind field). PeerConnectionId: ${this.peerConnectionId} ClientId: ${this.client.clientId}, CallId: ${this.client.call.callId}`, stats); } observedRemoteOutboundRtp = new ObservedRemoteOutboundRtp_1.ObservedRemoteOutboundRtp(stats.timestamp, stats.id, stats.ssrc, stats.kind, this); observedRemoteOutboundRtp.update(stats); this.observedRemoteOutboundRtps.set(stats.ssrc, observedRemoteOutboundRtp); this.emit('added-remote-outbound-rtp', observedRemoteOutboundRtp); } else { observedRemoteOutboundRtp.update(stats); } this.emit('updated-remote-outbound-rtp', observedRemoteOutboundRtp); return observedRemoteOutboundRtp; } } exports.ObservedPeerConnection = ObservedPeerConnection;