Signatur
Beschreibung
Die Eigenschaft totalFreezesDuration des Wörterbuchs RTCInboundRtpStreamStats (Statistik-Typ "inbound-rtp") summiert die Dauer aller Video-Einfrierer (Freezes) seit Beginn des Streams, in Sekunden. Die MDN-Kurzbeschreibung nennt hier RTCRemoteInboundRtpStreamStats. Das ist ein Fehler: Die Eigenschaft gehört zu RTCInboundRtpStreamStats, also zu den Statistiken des lokal empfangenen Streams.
Als Einfrieren gilt laut Spezifikation, wenn der Abstand zwischen zwei gerenderten Bildern größer ist als max(3 × durchschnittliche Bilddauer, durchschnittliche Bilddauer + 150 ms). Die durchschnittliche Bilddauer wird dabei über die letzten 30 gerenderten Bilder ermittelt. Jedes solche Ereignis erhöht freezeCount um eins und addiert seine Dauer zu totalFreezesDuration.
Die Eigenschaft ist eine wichtige Kennzahl für die wahrgenommene Videoqualität, z. B. für Qualitäts-Dashboards, adaptive Steuerung oder Telemetrie in Videokonferenz-Anwendungen. Weil der Wert kumulativ ist, bildet man für Auswertungen über die Zeit meist die Differenz zwischen zwei getStats()-Aufrufen. Zusammen mit freezeCount lässt sich außerdem die durchschnittliche Dauer eines Einfrierers berechnen.
Rückgabewert
undefined.Beispiele
Einfrier-Dauer aller eingehenden Video-Streams auslesen
// pc ist eine bestehende RTCPeerConnection mit empfangenem Video
const logFreezes = async (pc) => {
const stats = await pc.getStats();
stats.forEach((report) => {
if (report.type === 'inbound-rtp' && report.kind === 'video') {
console.log(
`SSRC ${report.ssrc}: ${report.freezeCount ?? 0} Einfrierer, ` +
`gesamt ${(report.totalFreezesDuration ?? 0).toFixed(2)} s`
);
}
});
};
await logFreezes(pc);
Einfrier-Anteil pro Intervall überwachen (Differenzbildung)
const monitorFreezes = (pc, intervalMs = 5000) => {
let last = new Map(); // id -> { t, freeze }
return setInterval(async () => {
const stats = await pc.getStats();
for (const r of stats.values()) {
if (r.type !== 'inbound-rtp' || r.kind !== 'video') continue;
const freeze = r.totalFreezesDuration ?? 0;
const prev = last.get(r.id);
if (prev) {
const dt = (r.timestamp - prev.t) / 1000; // Sekunden
const ratio = (freeze - prev.freeze) / dt;
if (ratio > 0.05) {
console.warn(`Video ${(ratio * 100).toFixed(1)} % der Zeit eingefroren`);
}
}
last.set(r.id, { t: r.timestamp, freeze });
}
}, intervalMs);
};
const timer = monitorFreezes(pc);
// später: clearInterval(timer);
Durchschnittliche Dauer eines Einfrierers berechnen
const averageFreeze = async (pc) => {
const stats = await pc.getStats();
const video = [...stats.values()].find(
(r) => r.type === 'inbound-rtp' && r.kind === 'video'
);
if (!video || video.totalFreezesDuration === undefined) {
return null; // nicht unterstützt oder kein Video
}
return video.freezeCount > 0
? video.totalFreezesDuration / video.freezeCount
: 0;
};
console.log(`Ø Einfrierer: ${await averageFreeze(pc)} s`);
// Wichtig · Fallstricke
- Nur Video: Bei Einträgen mit
kind === "audio"fehlt die Eigenschaft. Filtere deshalb immer nachtypeundkind. - Kumulativ: Der Wert wird seit Beginn des Streams aufaddiert. Für Aussagen über den aktuellen Zeitraum musst du Differenzen bilden und dafür
timestampdes Berichts verwenden, nichtDate.now(). - Einfrieren ist keine Pause: Kommen länger als 5 Sekunden keine Bilder an (z. B. weil der Sender das Video abgeschaltet hat), zählt das als Pause. Pausen werden in
pauseCountundtotalPausesDurationerfasst, nicht hier. - Browser-Kompatibilität: Chromium-basierte Browser (Chrome, Edge) unterstützen die Eigenschaft. Firefox und Safari liefern sie teilweise nicht. Prüfe deshalb auf
undefinedund nutze?? 0als Absicherung. - Fehler in der MDN-Beschreibung: Die Eigenschaft gehört nicht zu
RTCRemoteInboundRtpStreamStats("remote-inbound-rtp"), sondern zuRTCInboundRtpStreamStats("inbound-rtp").