Signatur
Beschreibung
Die Eigenschaft freezeCount des Dictionaries RTCInboundRtpStreamStats ist eine ganze Zahl, die angibt, wie oft der Empfänger bisher ein Einfrieren des Videos erlebt hat. Sie steht nur in Statistik-Objekten mit type === "inbound-rtp" und kind === "video" zur Verfügung. Bei Audio-Streams fehlt sie.
Laut Spezifikation gilt ein Frame als Freeze, wenn die Zeit zwischen zwei nacheinander gerenderten Frames mindestens Max(3 * avg_frame_duration_ms, avg_frame_duration_ms + 150) beträgt. Dabei ist avg_frame_duration_ms der Durchschnitt der Dauern der letzten 30 gerenderten Frames. Kurze Ruckler bei ansonsten flüssiger Wiedergabe werden also erkannt, während eine grundsätzlich niedrige Bildrate nicht als Freeze gilt.
Den Wert liest du über RTCPeerConnection.getStats() oder RTCRtpReceiver.getStats() aus. Er wächst über die gesamte Lebensdauer des Streams, fällt also nie. Für eine Überwachung der Qualität (etwa ein „Verbindung instabil“-Hinweis oder Telemetrie) bildest du deshalb die Differenz zwischen zwei Messungen. Zusammen mit totalFreezesDuration kannst du Häufigkeit und Gesamtdauer der Freezes auswerten.
Rückgabewert
undefined, wenn der Stream kein Video ist oder der Browser die Eigenschaft nicht unterstützt.Beispiele
Freezes aller eingehenden Video-Streams ausgeben
// pc ist eine bestehende, verbundene RTCPeerConnection
const logFreezes = async (pc) => {
const report = await pc.getStats();
report.forEach((stats) => {
if (stats.type === 'inbound-rtp' && stats.kind === 'video') {
console.log(`Stream ${stats.ssrc}: ${stats.freezeCount ?? 'n/a'} Freezes, ` +
`${(stats.totalFreezesDuration ?? 0).toFixed(2)} s eingefroren`);
}
});
};
await logFreezes(pc);
Neue Freezes pro Intervall erkennen und Nutzer warnen
const watchFreezes = (receiver, onFreeze, intervalMs = 2000) => {
let lastCount = null;
const timer = setInterval(async () => {
const report = await receiver.getStats();
for (const stats of report.values()) {
if (stats.type !== 'inbound-rtp' || stats.kind !== 'video') continue;
const count = stats.freezeCount;
if (count === undefined) return; // nicht unterstützt
if (lastCount !== null && count > lastCount) {
onFreeze(count - lastCount);
}
lastCount = count;
}
}, intervalMs);
return () => clearInterval(timer); // Aufräumfunktion
};
const videoReceiver = pc.getReceivers().find((r) => r.track.kind === 'video');
const stop = watchFreezes(videoReceiver, (neu) => {
console.warn(`${neu} neue(r) Freeze(s) – Verbindung instabil`);
});
// später: stop();
Freeze-Rate pro Minute für die Telemetrie berechnen
const freezeRate = async (pc) => {
const report = await pc.getStats();
const video = [...report.values()].find(
(s) => s.type === 'inbound-rtp' && s.kind === 'video'
);
if (!video || video.freezeCount === undefined) return null;
// Laufzeit grob über die Anzahl dekodierter Frames und die Bildrate schätzen
const seconds = video.framesDecoded / (video.framesPerSecond || 30);
const minutes = Math.max(seconds / 60, 1 / 60);
return +(video.freezeCount / minutes).toFixed(2);
};
console.log('Freezes/Minute:', await freezeRate(pc));
// Wichtig · Fallstricke
- Nur für Video: Bei Einträgen mit
kind === "audio"fehlt die Eigenschaft. Prüfe immer zuersttypeundkind. - Kumulativer Zähler: Der Wert wächst ständig. Für aktuelle Qualitätsaussagen brauchst du die Differenz zwischen zwei Messungen. Wird ein Stream neu ausgehandelt (neue SSRC), kann ein neues Statistik-Objekt entstehen, dessen Zähler wieder bei 0 beginnt.
- Freeze ist nicht dasselbe wie eine Pause: Sehr lange Lücken (ab 5 Sekunden) zählt die Spezifikation als Pause (
pauseCount/totalPausesDuration), zum Beispiel wenn der Sender das Video bewusst anhält. - Browser-Unterstützung: Chromium-basierte Browser unterstützen die Eigenschaft, Firefox und Safari aber nicht zuverlässig. Rechne mit
undefinedund nutze eine Absicherung wie?? 0oder eine Prüfung auf Unterstützung. - Leistung:
getStats()ist asynchron und nicht kostenlos. Abfrage-Intervalle von 1–5 Sekunden reichen in der Regel.