Signatur
Beschreibung
Die schreibgeschützte Eigenschaft totalSquaredInterFrameDelay des Wörterbuchs RTCInboundRtpStreamStats ist ein aufsummierter Wert: Nach jedem dargestellten Frame misst der Browser die Zeit seit dem vorherigen dargestellten Frame (die Zwischenbildverzögerung), quadriert sie und addiert sie zu diesem Zähler. Der Wert wird in Sekunden² angegeben und gilt nur für Video-Streams (kind === "video").
Für sich allein ist der Wert wenig aussagekräftig. Zusammen mit totalInterFrameDelay (Summe der einfachen Verzögerungen) und framesRendered lässt sich jedoch die Varianz bzw. Standardabweichung der Zwischenbildverzögerung berechnen – ein gutes Maß für ruckelndes Video (Frame-Jitter), auch wenn die mittlere Bildrate unauffällig ist:
Varianz = (totalSquaredInterFrameDelay - totalInterFrameDelay² / n) / n, wobeindie Anzahl der Frames ist (laut SpezifikationframesRendered).
Man erhält den Wert über RTCPeerConnection.getStats() bzw. RTCRtpReceiver.getStats(), indem man im zurückgegebenen RTCStatsReport nach Einträgen mit type === "inbound-rtp" filtert. Da der Zähler seit Beginn des Streams kumuliert, bildet man für Live-Überwachung meist Differenzen zwischen zwei Messzeitpunkten.
Rückgabewert
undefined bei Audio-Streams oder wenn der Browser die Eigenschaft nicht unterstützt.Beispiele
Wert aus dem Statistikbericht auslesen
// pc ist eine bestehende RTCPeerConnection mit eingehendem Video
const logSquaredDelay = async (pc) => {
const report = await pc.getStats();
report.forEach((stats) => {
if (stats.type === 'inbound-rtp' && stats.kind === 'video') {
console.log('Summe quadrierter Verzögerungen:',
stats.totalSquaredInterFrameDelay?.toFixed(4), 's²');
}
});
};
logSquaredDelay(pc);
Standardabweichung der Zwischenbildverzögerung (Frame-Jitter)
const getFrameJitter = async (receiver) => {
const report = await receiver.getStats();
for (const stats of report.values()) {
if (stats.type !== 'inbound-rtp' || stats.kind !== 'video') continue;
const { totalInterFrameDelay: sum, totalSquaredInterFrameDelay: sumSq } = stats;
const n = stats.framesRendered ?? stats.framesDecoded;
if (sum === undefined || sumSq === undefined || !n) return null;
const mean = sum / n;
const variance = Math.max(0, (sumSq - (sum * sum) / n) / n);
return { meanMs: mean * 1000, stdDevMs: Math.sqrt(variance) * 1000 };
}
return null;
};
const [videoReceiver] = pc.getReceivers().filter((r) => r.track.kind === 'video');
console.log(await getFrameJitter(videoReceiver));
Ruckeln im laufenden Betrieb über Intervall-Differenzen erkennen
let prev = null;
const monitor = async (pc) => {
const report = await pc.getStats();
const cur = [...report.values()].find(
(s) => s.type === 'inbound-rtp' && s.kind === 'video'
);
if (!cur || cur.totalSquaredInterFrameDelay === undefined) return;
if (prev) {
const n = cur.framesRendered - prev.framesRendered;
const sum = cur.totalInterFrameDelay - prev.totalInterFrameDelay;
const sumSq = cur.totalSquaredInterFrameDelay - prev.totalSquaredInterFrameDelay;
if (n > 0) {
const stdDevMs = Math.sqrt(Math.max(0, (sumSq - sum * sum / n) / n)) * 1000;
if (stdDevMs > 20) console.warn(`Video ruckelt: σ = ${stdDevMs.toFixed(1)} ms`);
}
}
prev = cur;
};
const timer = setInterval(() => monitor(pc), 2000);
// Später: clearInterval(timer);
// Wichtig · Fallstricke
- Nur Video: Für Audio-Streams (
kind === "audio") ist die Eigenschaft nicht vorhanden. - Einheit: Der Wert ist in Sekunden² angegeben, nicht in Millisekunden. Erst nach dem Wurzelziehen der Varianz und Multiplikation mit 1000 erhält man Millisekunden.
- Kumulativ: Der Zähler wächst über die gesamte Lebensdauer des Streams. Für aktuelle Werte immer Differenzen zwischen zwei
getStats()-Aufrufen bilden, sonst glätten alte Daten neue Probleme weg. - Nenner: Die Spezifikation nennt
framesRendered; in älteren Implementierungen bzw. Spezifikationsständen wurdeframesDecodedverwendet. Streng genommen gibt es eine Verzögerung weniger als Frames – bei großen Frame-Zahlen ist der Unterschied vernachlässigbar. - Rundungsfehler: Durch Gleitkomma-Ungenauigkeit kann die berechnete Varianz minimal negativ werden – vor
Math.sqrt()mitMath.max(0, …)absichern. - Kompatibilität: In Chromium-basierten Browsern verfügbar, in Firefox und Safari je nach Version eingeschränkt oder fehlend. Immer per Prüfung auf
undefinedabsichern.