Signatur
Beschreibung
Die schreibgeschützte Eigenschaft frames gehört zum Dictionary RTCVideoSourceStats. Dieses erhält man über RTCPeerConnection.getStats() oder RTCRtpSender.getStats() als Eintrag mit type === "media-source" und kind === "video". Der Wert ist ein fortlaufender Zähler (unsigned long). Er zählt, wie viele Frames die Quelle, also meist die Kamera oder die Bildschirmfreigabe hinter einem MediaStreamTrack, bisher an den Sender geliefert hat.
Der Zähler steigt nur an und bezieht sich auf die Quelle, nicht auf das, was tatsächlich kodiert oder gesendet wurde. Deshalb eignet er sich gut für die Fehlersuche. Vergleicht man ihn mit framesEncoded oder framesSent aus den outbound-rtp-Statistiken, sieht man, ob Frames schon in der Kamera fehlen oder erst beim Encoder verloren gehen, etwa durch CPU-Überlastung oder Bandbreitenanpassung.
Für eine aktuelle Bildrate misst man den Unterschied zwischen zwei Abfragen und teilt ihn durch den Unterschied der timestamp-Werte (in Millisekunden). Der absolute Wert allein zeigt nur, wie viel die Quelle seit ihrem Start insgesamt geliefert hat.
Rückgabewert
unsigned long): Gesamtzahl der Frames, die die Videoquelle seit Beginn geliefert hat. Fehlt die Unterstützung, ist die Eigenschaft undefined.Beispiele
Frame-Zähler der Kameraquelle auslesen
const stream = await navigator.mediaDevices.getUserMedia({ video: true });
const pc = new RTCPeerConnection();
const [track] = stream.getVideoTracks();
const sender = pc.addTrack(track, stream);
// Kurz warten, damit die Quelle Frames liefert
await new Promise((r) => setTimeout(r, 2000));
const report = await sender.getStats();
for (const stats of report.values()) {
if (stats.type === 'media-source' && stats.kind === 'video') {
console.log(`Quelle ${stats.trackIdentifier}: ${stats.frames} Frames`);
}
}
Aktuelle Bildrate aus zwei Abfragen berechnen
const getVideoSource = async (pc) => {
const report = await pc.getStats();
return [...report.values()].find(
(s) => s.type === 'media-source' && s.kind === 'video'
);
};
const measureFps = async (pc, intervalMs = 1000) => {
const a = await getVideoSource(pc);
await new Promise((r) => setTimeout(r, intervalMs));
const b = await getVideoSource(pc);
if (!a || !b || a.frames === undefined) return null;
const deltaFrames = b.frames - a.frames;
const deltaSec = (b.timestamp - a.timestamp) / 1000;
return deltaFrames / deltaSec;
};
console.log(`Quelle liefert ${(await measureFps(pc)).toFixed(1)} fps`);
Verlorene Frames zwischen Quelle und Encoder erkennen
const checkEncoderLoss = async (sender) => {
const report = await sender.getStats();
let source, outbound;
for (const s of report.values()) {
if (s.type === 'media-source' && s.kind === 'video') source = s;
if (s.type === 'outbound-rtp' && s.kind === 'video') outbound = s;
}
if (!source || !outbound) return;
const lost = source.frames - outbound.framesEncoded;
const ratio = (lost / source.frames) * 100;
console.log(`Nicht kodiert: ${lost} Frames (${ratio.toFixed(1)} %)`);
};
setInterval(() => checkEncoderLoss(sender), 5000);
// Wichtig · Fallstricke
- Browser-Unterstützung: Chromium-basierte Browser und Safari liefern
frames. Firefox stelltmedia-source-Statistiken nur teilweise bereit. Prüfen Sie deshalb immer mitstats.frames !== undefined, ob der Wert vorhanden ist. - Der Wert ist ein Zähler über die ganze Lebensdauer und keine Rate. Für die Bildrate brauchen Sie zwei Messungen und die Differenz der
timestamp-Werte. Alternativ gibt esframesPerSecond. - Wird der Track mit
RTCRtpSender.replaceTrack()ausgetauscht, kann ein neuesmedia-source-Objekt mit eigener ID entstehen, dessen Zähler wieder bei 0 beginnt. Vergleichen Sie Messungen daher über dieselbeidoder dieselbetrackIdentifier. - Kodierte oder gesendete Frames können weniger sein als
frames, zum Beispiel wegen CPU-Überlastung, Bandbreitenanpassung oder deaktiviertem Track (track.enabled = false). Das ist erwartetes Verhalten und kein Fehler der Statistik. - Es gibt Werte nur für lokale Quellen, also Sender. Für empfangene Videos nutzen Sie
RTCInboundRtpStreamStats.framesDecodedbzw.framesReceived.