From ff67f0f78127b054a11718f164306423586fb7e2 Mon Sep 17 00:00:00 2001 From: balazskreith Date: Mon, 10 Feb 2025 09:52:39 +0200 Subject: [PATCH] save --- src/ClientMonitor.ts | 5 ++- src/ClientMonitorConfig.ts | 20 +++++++++- src/ClientMonitorEvents.ts | 13 +++++++ src/detectors/AudioDesyncDetector.ts | 4 ++ src/detectors/DryInboundTrack.ts | 15 ++++++-- src/detectors/DryOutboundTrack.ts | 50 +++++++++++++++++++++++++ src/detectors/IceTupleChangeDetector.ts | 47 +++++++++++++++++++++++ src/monitors/InboundTrackMonitor.ts | 7 +--- src/monitors/OutboundTrackMonitor.ts | 5 ++- src/monitors/PeerConnectionMonitor.ts | 2 + 10 files changed, 156 insertions(+), 12 deletions(-) create mode 100644 src/detectors/DryOutboundTrack.ts create mode 100644 src/detectors/IceTupleChangeDetector.ts diff --git a/src/ClientMonitor.ts b/src/ClientMonitor.ts index 9060940..445bdb7 100644 --- a/src/ClientMonitor.ts +++ b/src/ClientMonitor.ts @@ -90,7 +90,10 @@ export class ClientMonitor extends EventEmitter { videoFreezesDetector: config?.videoFreezesDetector ?? { }, - stuckedInboundTrackDetector: config?.stuckedInboundTrackDetector ?? { + dryInboundTrackDetector: config?.dryInboundTrackDetector ?? { + thresholdInMs: 5000, + }, + dryOutboundTrackDetector: config?.dryOutboundTrackDetector ?? { thresholdInMs: 5000, }, audioDesyncDetector: config?.audioDesyncDetector ?? { diff --git a/src/ClientMonitorConfig.ts b/src/ClientMonitorConfig.ts index 085c589..82b0cac 100644 --- a/src/ClientMonitorConfig.ts +++ b/src/ClientMonitorConfig.ts @@ -69,7 +69,25 @@ export type ClientMonitorConfig = { /** * Configuration for detecting inbound track stalling during monitoring. */ - stuckedInboundTrackDetector: { + dryInboundTrackDetector: { + /** + * If true, the detection of stalled inbound tracks is disabled. + * + * DEFAULT: false + */ + disabled?: boolean; + + /** + * The time threshold (in milliseconds) to determine if an inbound track + * is considered stalled. + */ + thresholdInMs: number; + }; + + /** + * Configuration for detecting inbound track stalling during monitoring. + */ + dryOutboundTrackDetector: { /** * If true, the detection of stalled inbound tracks is disabled. * diff --git a/src/ClientMonitorEvents.ts b/src/ClientMonitorEvents.ts index 9cf62b6..7fab8ec 100644 --- a/src/ClientMonitorEvents.ts +++ b/src/ClientMonitorEvents.ts @@ -10,6 +10,7 @@ import { InboundTrackMonitor } from "./monitors/InboundTrackMonitor"; import { MediaPlayoutMonitor } from "./monitors/MediaPlayoutMonitor"; import { MediaSourceMonitor } from "./monitors/MediaSourceMonitor"; import { OutboundRtpMonitor } from "./monitors/OutboundRtpMonitor"; +import { OutboundTrackMonitor } from "./monitors/OutboundTrackMonitor"; import { PeerConnectionMonitor } from "./monitors/PeerConnectionMonitor"; import { PeerConnectionTransportMonitor } from "./monitors/PeerConnectionTransportMonitor"; import { RemoteInboundRtpMonitor } from "./monitors/RemoteInboundRtpMonitor"; @@ -75,10 +76,18 @@ export type DryInboundTrackEventPayload = ClientMonitorBaseEvent & { trackMonitor: InboundTrackMonitor, } +export type DryOutboundTrackEventPayload = ClientMonitorBaseEvent & { + trackMonitor: OutboundTrackMonitor, +} + export type TooLongPcConnectionEstablishmentEventPayload = ClientMonitorBaseEvent & { peerConnectionMonitor: PeerConnectionMonitor, } +export type IceTupleChangedEventPayload = ClientMonitorBaseEvent & { + peerConnectionMonitor: PeerConnectionMonitor, +} + export type ScoreEventPayload = ClientMonitorBaseEvent & { clientScore: number, remarks?: string[], @@ -148,6 +157,8 @@ export type NewCertificateMonitorEventPayload = ClientMonitorBaseEvent & { certificateMonitor: CertificateMonitor, } + + export type ClientMonitorEvents = { 'sample-created': [SampleCreatedEventPayload], "stats-collected": [StatsCollectedEventPayload], @@ -161,6 +172,8 @@ export type ClientMonitorEvents = { 'synthesized-samples': [SynthesizedSamplesEventPayload], 'freezed-video-track': [FreezedVideoTrackEventPayload], 'dry-inbound-track': [DryInboundTrackEventPayload], + 'dry-outbound-track': [DryOutboundTrackEventPayload], + 'ice-tuple-changed': [IceTupleChangedEventPayload], 'too-long-pc-connection-establishment': [TooLongPcConnectionEstablishmentEventPayload] // 'ice-restart': [peerConnectionMonitor: PeerConnectionMonitor], 'score': [ScoreEventPayload], diff --git a/src/detectors/AudioDesyncDetector.ts b/src/detectors/AudioDesyncDetector.ts index d020daa..35c569c 100644 --- a/src/detectors/AudioDesyncDetector.ts +++ b/src/detectors/AudioDesyncDetector.ts @@ -9,6 +9,7 @@ export class AudioDesyncDetector implements Detector { ) { } + public lastDesyncDuration?: number; private _startedDesyncAt?: number; private _prevCorrectedSamples = 0; @@ -42,6 +43,9 @@ export class AudioDesyncDetector implements Detector { if (!inboundRtp.desync) { if (wasDesync) { + if (this._startedDesyncAt) { + this.lastDesyncDuration = Date.now() - this._startedDesyncAt; + } this._startedDesyncAt = undefined; } return; diff --git a/src/detectors/DryInboundTrack.ts b/src/detectors/DryInboundTrack.ts index 5dc0900..bec923b 100644 --- a/src/detectors/DryInboundTrack.ts +++ b/src/detectors/DryInboundTrack.ts @@ -17,15 +17,24 @@ export class DryInboundTrackDetector implements Detector { } private get config() { - return this.peerConnection.parent.config.stuckedInboundTrackDetector; + return this.peerConnection.parent.config.dryInboundTrackDetector; } + private _activatedAt?: number; + public update() { if (this._evented || this.config.disabled) return; if (this.trackMonitor.getInboundRtp()?.bytesReceived !== 0) return; - const inboundRtp = this.trackMonitor.getInboundRtp(); + if (this.trackMonitor.remoteOutboundTrackPaused) { + this._activatedAt = undefined; + return; + } + + if (!this._activatedAt) { + this._activatedAt = Date.now(); + } - const duration = Date.now() - inboundRtp.addedAt; + const duration = Date.now() - this._activatedAt; if (duration < this.config.thresholdInMs) return; diff --git a/src/detectors/DryOutboundTrack.ts b/src/detectors/DryOutboundTrack.ts new file mode 100644 index 0000000..bfd97ff --- /dev/null +++ b/src/detectors/DryOutboundTrack.ts @@ -0,0 +1,50 @@ +import { OutboundTrackMonitor } from "../monitors/OutboundTrackMonitor"; +import { Detector } from "./Detector"; + + +export class DryOutboundTrackDetector implements Detector { + public readonly name = 'dry-outbound-track-detector'; + + public constructor( + public readonly trackMonitor: OutboundTrackMonitor, + ) { + } + + private _evented = false; + + private get peerConnection() { + return this.trackMonitor.getPeerConnection(); + } + + private get config() { + return this.peerConnection.parent.config.dryOutboundTrackDetector; + } + + private _activatedAt?: number; + + public update() { + if (this._evented || this.config.disabled) return; + if (this.trackMonitor.getOutboundRtps()?.[0].bytesSent !== 0) return; + if (this.trackMonitor.track.muted || this.trackMonitor.track.readyState !== 'live') { + this._activatedAt = undefined; + return; + } + + if (!this._activatedAt) { + this._activatedAt = Date.now(); + } + + const duration = Date.now() - this._activatedAt; + + if (duration < this.config.thresholdInMs) return; + + this._evented = true; + + const clientMonitor = this.peerConnection.parent; + + clientMonitor.emit('dry-outbound-track', { + trackMonitor: this.trackMonitor, + clientMonitor: clientMonitor, + }); + } +} \ No newline at end of file diff --git a/src/detectors/IceTupleChangeDetector.ts b/src/detectors/IceTupleChangeDetector.ts new file mode 100644 index 0000000..43edcee --- /dev/null +++ b/src/detectors/IceTupleChangeDetector.ts @@ -0,0 +1,47 @@ +import { PeerConnectionMonitor } from "../monitors/PeerConnectionMonitor"; +import { Detector } from "./Detector"; + +export class IceTupleChangeDetector implements Detector { + public readonly name = 'ice-tuple-change-detector'; + + public constructor( + public readonly pcMonitor: PeerConnectionMonitor, + ) { + } + + public readonly tuples = new Set(); + + public update() { + if (this.pcMonitor.closed) return; + + const selectedIceCandidatePairs = this.pcMonitor.selectedIceCandidatePairs; + const wasEmpty = this.tuples.size === 0; + let changed = false; + const curentTuples = new Set(); + + for (const pair of selectedIceCandidatePairs) { + const local = pair.getLocalCandidate(); + const remote = pair.getRemoteCandidate(); + const tuple = `${local?.address}:${local?.port}:${remote?.address}:${remote?.port}:${local?.protocol}`; + + curentTuples.add(tuple); + if (!this.tuples.has(tuple)) { + changed = true; + this.tuples.add(tuple); + } + } + for (const tuple of this.tuples) { + if (!curentTuples.has(tuple)) { + changed = true; + this.tuples.delete(tuple); + } + } + + if (wasEmpty || !changed) return; + + this.pcMonitor.parent.emit('ice-tuple-changed', { + clientMonitor: this.pcMonitor.parent, + peerConnectionMonitor: this.pcMonitor, + }); + } + } \ No newline at end of file diff --git a/src/monitors/InboundTrackMonitor.ts b/src/monitors/InboundTrackMonitor.ts index 1724b96..4b1fd9f 100644 --- a/src/monitors/InboundTrackMonitor.ts +++ b/src/monitors/InboundTrackMonitor.ts @@ -7,16 +7,11 @@ import { InboundRtpMonitor } from "./InboundRtpMonitor"; import { OutboundTrackSample } from "../schema/ClientSample"; export class InboundTrackMonitor { - public static applyOnAppDataAtSampling = = Record>(appData: Record) => { - return { - ...appData, - }; - } - public readonly direction = 'inbound'; public readonly detectors: Detectors; // public contentType: 'lowmotion' | 'highmotion' | 'standard' = 'standard'; public dtxMode = false; + public remoteOutboundTrackPaused = false; public calculatedScore: CalculatedScore = { weight: this.kind === 'audio' ? 1 : 2, diff --git a/src/monitors/OutboundTrackMonitor.ts b/src/monitors/OutboundTrackMonitor.ts index 4c04e4a..9ad7b1e 100644 --- a/src/monitors/OutboundTrackMonitor.ts +++ b/src/monitors/OutboundTrackMonitor.ts @@ -1,4 +1,5 @@ import { Detectors } from "../detectors/Detectors"; +import { DryOutboundTrackDetector } from "../detectors/DryOutboundTrack"; import { InboundTrackSample } from "../schema/ClientSample"; import { CalculatedScore } from "../scores/CalculatedScore"; import { MediaSourceMonitor } from "./MediaSourceMonitor"; @@ -36,7 +37,9 @@ export class OutboundTrackMonitor { attachments?: Record, ) { this.attachments = attachments; - this.detectors = new Detectors(); + this.detectors = new Detectors( + new DryOutboundTrackDetector(this), + ); } diff --git a/src/monitors/PeerConnectionMonitor.ts b/src/monitors/PeerConnectionMonitor.ts index 1d0ccd6..b6fe627 100644 --- a/src/monitors/PeerConnectionMonitor.ts +++ b/src/monitors/PeerConnectionMonitor.ts @@ -22,6 +22,7 @@ import { CongestionDetector } from "../detectors/CongestionDetector"; import { InboundTrackMonitor } from "./InboundTrackMonitor"; import { OutboundTrackMonitor } from "./OutboundTrackMonitor"; import { CalculatedScore } from "../scores/CalculatedScore"; +import { IceTupleChangeDetector } from "../detectors/IceTupleChangeDetector"; const logger = createLogger('PeerConnectionMonitor'); @@ -142,6 +143,7 @@ export class PeerConnectionMonitor extends EventEmitter { this.detectors = new Detectors( new LongPcConnectionEstablishmentDetector(this), new CongestionDetector(this), + new IceTupleChangeDetector(this), ); }