Signatur
RTCInboundRtpStreamStats.totalInterFrameDelay
Beschreibung
<p>Die Eigenschaft <code>totalInterFrameDelay</code> des Wörterbuchs <code>RTCInboundRtpStreamStats</code> enthält die Summe aller Abstände (<em>Inter-Frame-Delays</em>) in Sekunden zwischen zwei nacheinander gerenderten Frames. Der Wert wird jeweils direkt nach dem Rendern eines Frames erhöht. Er existiert nur für Video-Streams (<code>kind === "video"</code>). Bei Audio-Streams fehlt er.</p>
<p>Weil der Wert kumuliert ist, sagt er allein wenig aus. Nützlich wird er erst im Zusammenspiel mit anderen Werten: Teilt man den Zuwachs von <code>totalInterFrameDelay</code> zwischen zwei <code>getStats()</code>-Abfragen durch den Zuwachs von <code>framesRendered</code>, erhält man den <strong>mittleren Frame-Abstand</strong> in diesem Zeitraum. Der Kehrwert davon ist die tatsächlich wahrgenommene Bildrate.</p>
<p>Zusammen mit <code>totalSquaredInterFrameDelay</code> lässt sich die <strong>Varianz</strong> der Frame-Abstände berechnen. Die Formel lautet <code>(totalSquaredInterFrameDelay - totalInterFrameDelay² / n) / n</code>, wobei <code>n</code> die Anzahl der Frames ist. Eine hohe Varianz zeigt ruckelnde Wiedergabe (<em>Jank</em>) an, auch wenn die mittlere Bildrate in Ordnung aussieht. Das ist ein wichtiger Messwert für die Dienstgüte (QoS) in Videokonferenz-Anwendungen.</p>
Rückgabewert
Typ
number | undefined
Beschreibung
Nicht-negative Gleitkommazahl: die aufsummierte Zeit zwischen gerenderten Frames in <strong>Sekunden</strong>. Bei Audio-Streams oder fehlender Unterstützung ist der Wert <code>undefined</code>.
Beispiele
Wert für eingehende Video-Streams auslesen
// pc ist eine bestehende RTCPeerConnection mit empfangenem Video
const logInterFrameDelay = async (pc) => {
const stats = await pc.getStats();
stats.forEach((report) => {
if (report.type === 'inbound-rtp' && report.kind === 'video') {
console.log(`SSRC ${report.ssrc}: totalInterFrameDelay = ${report.totalInterFrameDelay?.toFixed(3)} s`);
}
});
};
await logInterFrameDelay(pc);
SSRC 3735928559: totalInterFrameDelay = 42.137 s
Mittleren Frame-Abstand und effektive Bildrate messen
// Unterschied zwischen zwei Abfragen im Abstand von 1 Sekunde
const getInboundVideo = async (pc) => {
const stats = await pc.getStats();
return [...stats.values()].find(
(r) => r.type === 'inbound-rtp' && r.kind === 'video'
);
};
const measureSmoothness = async (pc) => {
const a = await getInboundVideo(pc);
await new Promise((resolve) => setTimeout(resolve, 1000));
const b = await getInboundVideo(pc);
if (!a || !b || b.totalInterFrameDelay === undefined) return null;
const frames = b.framesRendered - a.framesRendered;
const delay = b.totalInterFrameDelay - a.totalInterFrameDelay;
if (frames <= 0) return null;
const avgDelayMs = (delay / frames) * 1000;
return { avgDelayMs: avgDelayMs.toFixed(1), fps: (1000 / avgDelayMs).toFixed(1) };
};
console.log(await measureSmoothness(pc));
{ avgDelayMs: '33.4', fps: '29.9' }
Varianz und Standardabweichung der Frame-Abstände (Ruckel-Erkennung)
const jitterOfFrames = (report) => {
const n = report.framesRendered ?? report.framesDecoded;
const sum = report.totalInterFrameDelay;
const sumSq = report.totalSquaredInterFrameDelay;
if (!n || sum === undefined || sumSq === undefined) return null;
const variance = (sumSq - (sum * sum) / n) / n;
const stdDevMs = Math.sqrt(Math.max(variance, 0)) * 1000;
return stdDevMs;
};
const stats = await pc.getStats();
for (const report of stats.values()) {
if (report.type === 'inbound-rtp' && report.kind === 'video') {
const sd = jitterOfFrames(report);
console.log(sd > 15 ? `Ruckelig: σ = ${sd.toFixed(1)} ms` : `Flüssig: σ = ${sd?.toFixed(1)} ms`);
}
}
Flüssig: σ = 4.8 ms
// Wichtig · Fallstricke
<ul>
<li><strong>Einheit Sekunden:</strong> Der Wert ist in Sekunden angegeben, nicht in Millisekunden. Für Anzeigen muss man ihn meist mit 1000 multiplizieren.</li>
<li><strong>Nur Video:</strong> Bei <code>kind === "audio"</code> gibt es die Eigenschaft nicht. Prüfe deshalb immer auf <code>undefined</code>.</li>
<li><strong>Kumulierter Wert:</strong> Er wächst über die ganze Lebensdauer des Streams. Aussagekräftige Werte für einen bestimmten Zeitraum bekommt man nur, wenn man die Differenz zwischen zwei <code>getStats()</code>-Aufrufen bildet. Bei einem neuen Stream mit neuer SSRC oder einer neuen Verbindung beginnt der Wert wieder bei 0.</li>
<li><strong>Frame-Zähler:</strong> Frühere Versionen der Spezifikation und einige Implementierungen erfassten den Wert beim <em>Dekodieren</em> statt beim Rendern. Bei der Mittelwert- und Varianzberechnung wurde dann <code>framesDecoded</code> statt <code>framesRendered</code> verwendet. Plane für ältere Browser einen Rückgriff ein (siehe Beispiel 3).</li>
<li><strong>Kompatibilität:</strong> Chromium-Browser, Firefox und Safari unterstützen die Eigenschaft in aktuellen Versionen. Ältere Browser liefern sie teils nicht. Frage Statistiken nicht zu oft ab (Richtwert: höchstens etwa einmal pro Sekunde), denn <code>getStats()</code> ist verhältnismäßig aufwendig.</li>
</ul>