diff --git a/spec/unit/webrtc/stats/statsReportBuilder.spec.ts b/spec/unit/webrtc/stats/statsReportBuilder.spec.ts index b1843bbfc..fd75c9054 100644 --- a/spec/unit/webrtc/stats/statsReportBuilder.spec.ts +++ b/spec/unit/webrtc/stats/statsReportBuilder.spec.ts @@ -87,6 +87,10 @@ describe("StatsReportBuilder", () => { ["REMOTE_VIDEO_TRACK_ID", { height: 960, width: 1080 }], ]), }, + jitter: new Map([ + ["REMOTE_AUDIO_TRACK_ID", 0.1], + ["REMOTE_VIDEO_TRACK_ID", 50], + ]), }); }); }); @@ -99,6 +103,7 @@ describe("StatsReportBuilder", () => { remoteAudioTrack.setCodec("opus"); remoteAudioTrack.setLoss({ packetsTotal: 20, packetsLost: 0, isDownloadStream: true }); remoteAudioTrack.setBitrate({ download: 4000, upload: 0 }); + remoteAudioTrack.setJitter(0.1); localVideoTrack.setCodec("v8"); localVideoTrack.setLoss({ packetsTotal: 30, packetsLost: 6, isDownloadStream: false }); @@ -111,5 +116,6 @@ describe("StatsReportBuilder", () => { remoteVideoTrack.setBitrate({ download: 5000000, upload: 0 }); remoteVideoTrack.setFramerate(60); remoteVideoTrack.setResolution({ width: 1080, height: 960 }); + remoteVideoTrack.setJitter(50); }; }); diff --git a/spec/unit/webrtc/stats/trackStatsReporter.spec.ts b/spec/unit/webrtc/stats/trackStatsReporter.spec.ts index 47232a9e8..9595881bf 100644 --- a/spec/unit/webrtc/stats/trackStatsReporter.spec.ts +++ b/spec/unit/webrtc/stats/trackStatsReporter.spec.ts @@ -246,4 +246,30 @@ describe("TrackStatsReporter", () => { }); }); }); + + describe("should build jitter value in Track Stats", () => { + it("and returns track stats without jitter if report not 'inbound-rtp'", async () => { + const trackStats = new MediaTrackStats("1", "remote", "video"); + TrackStatsReporter.buildJitter(trackStats, { jitter: 0.01 }); + expect(trackStats.getJitter()).toEqual(0); + }); + + it("and returns track stats with jitter", async () => { + const trackStats = new MediaTrackStats("1", "remote", "video"); + TrackStatsReporter.buildJitter(trackStats, { type: "inbound-rtp", jitter: 0.01 }); + expect(trackStats.getJitter()).toEqual(10); + }); + + it("and returns negative jitter if stats has no jitter value", async () => { + const trackStats = new MediaTrackStats("1", "remote", "video"); + TrackStatsReporter.buildJitter(trackStats, { type: "inbound-rtp" }); + expect(trackStats.getJitter()).toEqual(-1); + }); + + it("and returns jitter as number", async () => { + const trackStats = new MediaTrackStats("1", "remote", "video"); + TrackStatsReporter.buildJitter(trackStats, { type: "inbound-rtp", jitter: "0.5" }); + expect(trackStats.getJitter()).toEqual(500); + }); + }); }); diff --git a/src/webrtc/stats/media/mediaTrackStats.ts b/src/webrtc/stats/media/mediaTrackStats.ts index aba7250fc..66475e19c 100644 --- a/src/webrtc/stats/media/mediaTrackStats.ts +++ b/src/webrtc/stats/media/mediaTrackStats.ts @@ -45,6 +45,7 @@ export class MediaTrackStats { private bitrate: Bitrate = { download: 0, upload: 0 }; private resolution: Resolution = { width: -1, height: -1 }; private framerate = 0; + private jitter = 0; private codec = ""; private isAlive = true; private isMuted = false; @@ -140,4 +141,15 @@ export class MediaTrackStats { public get enabled(): boolean { return this.isEnabled; } + + public setJitter(jitter: number): void { + this.jitter = jitter; + } + + /** + * Jitter in milliseconds + */ + public getJitter(): number { + return this.jitter; + } } diff --git a/src/webrtc/stats/statsReport.ts b/src/webrtc/stats/statsReport.ts index 45914fa11..9396d0dfe 100644 --- a/src/webrtc/stats/statsReport.ts +++ b/src/webrtc/stats/statsReport.ts @@ -38,6 +38,7 @@ export interface ConnectionStatsReport { resolution: ResolutionMap; framerate: FramerateMap; codec: CodecMap; + jitter: Map; transport: TransportStats[]; } diff --git a/src/webrtc/stats/statsReportBuilder.ts b/src/webrtc/stats/statsReportBuilder.ts index c1af471ce..eeca4ed40 100644 --- a/src/webrtc/stats/statsReportBuilder.ts +++ b/src/webrtc/stats/statsReportBuilder.ts @@ -37,6 +37,7 @@ export class StatsReportBuilder { }; const framerates: FramerateMap = { local: new Map(), remote: new Map() }; const codecs: CodecMap = { local: new Map(), remote: new Map() }; + const jitter = new Map(); let audioBitrateDownload = 0; let audioBitrateUpload = 0; @@ -67,6 +68,9 @@ export class StatsReportBuilder { resolutions[trackStats.getType()].set(trackId, trackStats.getResolution()); framerates[trackStats.getType()].set(trackId, trackStats.getFramerate()); codecs[trackStats.getType()].set(trackId, trackStats.getCodec()); + if (trackStats.getType() === "remote") { + jitter.set(trackId, trackStats.getJitter()); + } trackStats.resetBitrate(); } @@ -97,6 +101,7 @@ export class StatsReportBuilder { report.framerate = framerates; report.resolution = resolutions; report.codec = codecs; + report.jitter = jitter; return report; } diff --git a/src/webrtc/stats/statsReportGatherer.ts b/src/webrtc/stats/statsReportGatherer.ts index 58bfde022..c2d74a95f 100644 --- a/src/webrtc/stats/statsReportGatherer.ts +++ b/src/webrtc/stats/statsReportGatherer.ts @@ -137,6 +137,7 @@ export class StatsReportGatherer { } const ts = this.trackStats.findTransceiverByTrackId(trackStats.trackId); TrackStatsReporter.setTrackStatsState(trackStats, ts); + TrackStatsReporter.buildJitter(trackStats, now); } else if (before) { byteSentStats.set(trackStats.trackId, StatsValueFormatter.getNonNegativeValue(now.bytesSent)); TrackStatsReporter.buildBitrateSend(trackStats, now, before); diff --git a/src/webrtc/stats/trackStatsReporter.ts b/src/webrtc/stats/trackStatsReporter.ts index 6d12ef334..8cf8be20a 100644 --- a/src/webrtc/stats/trackStatsReporter.ts +++ b/src/webrtc/stats/trackStatsReporter.ts @@ -153,4 +153,18 @@ export class TrackStatsReporter { }); return { audioTrackSummary, videoTrackSummary }; } + + public static buildJitter(trackStats: MediaTrackStats, statsReport: any): void { + if (statsReport.type !== "inbound-rtp") { + return; + } + + const jitterStr = statsReport?.jitter; + if (jitterStr !== undefined) { + const jitter = StatsValueFormatter.getNonNegativeValue(jitterStr); + trackStats.setJitter(Math.round(jitter * 1000)); + } else { + trackStats.setJitter(-1); + } + } }