Start · Sprachen · JavaScript · Referenz · RTCStatsReport

RTCStatsReport

Schnittstelle

Map-ähnliches, schreibgeschütztes Objekt mit WebRTC-Statistiken, das <code>getStats()</code> für eine <code>RTCPeerConnection</code>, einen <code>RTCRtpSender</code> oder <code>RTCRtpReceiver</code> liefert.

seit JavaScript Baseline: alle modernen Browser (Chrome 58+, Firef Kategorie: webapi

Signatur

interface RTCStatsReport

Beschreibung

RTCStatsReport ist das Ergebnis der Methoden RTCPeerConnection.getStats(), RTCRtpSender.getStats() und RTCRtpReceiver.getStats(). Es handelt sich um ein Map-ähnliches, schreibgeschütztes Objekt: Die Schlüssel sind eindeutige Statistik-IDs (Strings), die Werte sind Statistik-Wörterbücher (RTCStats-Objekte) mit mindestens den Feldern id, type und timestamp.

Über das Feld type lässt sich unterscheiden, welche Art von Statistik vorliegt, z. B. "inbound-rtp", "outbound-rtp", "remote-inbound-rtp", "candidate-pair", "local-candidate", "remote-candidate", "transport", "codec" oder "media-source". Einträge verweisen über Felder wie transportId, codecId oder localCandidateId aufeinander – diese IDs können direkt mit report.get(id) aufgelöst werden.

Typische Anwendungsfälle sind Qualitätsanzeigen in Videokonferenz-Apps (Bitrate, Paketverlust, Jitter, Round-Trip-Time), Fehlerdiagnose bei Verbindungsproblemen (welches ICE-Kandidatenpaar ist aktiv? Relay über TURN?) sowie Telemetrie. Da die meisten Zähler kumulativ sind, berechnet man Raten, indem man zwei Berichte in zeitlichem Abstand vergleicht und die Differenz durch die Differenz der timestamp-Werte (in Millisekunden) teilt.

Das Interface stellt die üblichen schreibgeschützten Map-Methoden bereit: size, get(), has(), keys(), values(), entries(), forEach() sowie [Symbol.iterator]. Methoden wie set(), delete() oder clear() gibt es nicht.

Parameter

Name Typ Default Beschreibung
$size number Schreibgeschützt: Anzahl der enthaltenen Statistik-Einträge.
$get(id) (id: string) => RTCStats | undefined Liefert den Statistik-Eintrag zur angegebenen ID, z. B. um codecId oder transportId aufzulösen.
$has(id) (id: string) => boolean Prüft, ob ein Eintrag mit dieser ID existiert.
$forEach(callback[, thisArg]) (callback: (stats: RTCStats, id: string, report: RTCStatsReport) => void, thisArg?: any) => void Ruft callback für jeden Eintrag in Einfügereihenfolge auf.
$entries() / keys() / values() () => Iterator Iteratoren über [id, stats]-Paare, IDs bzw. Statistik-Objekte. Der Bericht selbst ist mit for...of iterierbar.

Rückgabewert

Typ
RTCStatsReport
Beschreibung
Wird als Erfüllungswert des Promise von getStats() geliefert; enthält eine Momentaufnahme aller Statistiken zum Abfragezeitpunkt.

Beispiele

Bericht abrufen und alle Einträge nach Typ auflisten

const pc = new RTCPeerConnection();
pc.createDataChannel('test');
await pc.setLocalDescription(await pc.createOffer());

const report = await pc.getStats();
console.log('Anzahl Einträge:', report.size);

for (const [id, stats] of report) {
  console.log(stats.type, id);
}
Anzahl Einträge: 3 peer-connection PC data-channel D1 transport T01

Eingehende Video-Bitrate aus zwei Berichten berechnen

// pc ist eine bestehende, verbundene RTCPeerConnection
const getInboundVideo = (report) =>
  [...report.values()].find(
    (s) => s.type === 'inbound-rtp' && s.kind === 'video'
  );

let last = null;

setInterval(async () => {
  const report = await pc.getStats();
  const now = getInboundVideo(report);
  if (!now) return;

  if (last) {
    const bytes = now.bytesReceived - last.bytesReceived;
    const ms = now.timestamp - last.timestamp;
    const kbps = Math.round((bytes * 8) / ms); // Bit pro ms = kbit/s
    const lossRate = now.packetsLost / (now.packetsReceived + now.packetsLost);
    console.log(`Bitrate: ${kbps} kbit/s, Verlust: ${(lossRate * 100).toFixed(1)} %, Jitter: ${now.jitter} s`);
  }
  last = now;
}, 1000);
Bitrate: 1487 kbit/s, Verlust: 0.3 %, Jitter: 0.004 s

Aktives ICE-Kandidatenpaar und Codec über IDs auflösen

const report = await pc.getStats();

report.forEach((stats) => {
  if (stats.type === 'transport' && stats.selectedCandidatePairId) {
    const pair = report.get(stats.selectedCandidatePairId);
    const local = report.get(pair.localCandidateId);
    const remote = report.get(pair.remoteCandidateId);
    console.log('RTT:', pair.currentRoundTripTime, 's');
    console.log('Lokal:', local.candidateType, local.protocol);
    console.log('Remote:', remote.candidateType);
    if (local.candidateType === 'relay') {
      console.log('Verbindung läuft über TURN-Server');
    }
  }
  if (stats.type === 'outbound-rtp' && stats.codecId) {
    const codec = report.get(stats.codecId);
    console.log(`${stats.kind}-Codec:`, codec.mimeType);
  }
});
RTT: 0.042 s Lokal: srflx udp Remote: host video-Codec: video/VP8

Statistiken nur für einen bestimmten Sender

const [sender] = pc.getSenders().filter((s) => s.track?.kind === 'audio');
const report = await sender.getStats();

const outbound = [...report.values()].find((s) => s.type === 'outbound-rtp');
console.log('Gesendete Pakete:', outbound?.packetsSent);
Gesendete Pakete: 1532

// Wichtig · Fallstricke

  • Kein Array, keine echte Map: RTCStatsReport ist nur Map-ähnlich und schreibgeschützt. Für filter()/find() erst mit [...report.values()] oder Array.from(report.values()) umwandeln.
  • IDs nicht fest verdrahten: Die Schlüssel sind browserspezifisch (z. B. "OT01V123" in Chrome, UUID-artig in Firefox) und können sich zwischen Aufrufen ändern. Immer über type und Felder wie kind oder ssrc filtern.
  • Kumulative Zähler: Werte wie bytesReceived oder packetsLost sind Summen seit Beginn. Raten nur über die Differenz zweier Berichte ermitteln; timestamp ist in Millisekunden angegeben.
  • Uneinheitliche Unterstützung: Nicht jeder Browser liefert jeden Statistik-Typ oder jedes Feld (z. B. fehlten in Safari lange einige candidate-pair-Felder). Felder daher defensiv mit ?. abfragen. Das veraltete, callback-basierte getStats() aus frühen Chrome-Versionen liefert ein anderes Format und sollte nicht mehr genutzt werden.
  • Performance: getStats() ist nicht kostenlos. Abfrageintervalle von etwa 1 s sind üblich; sehr häufiges Pollen kann bei vielen Verbindungen spürbar CPU kosten.
  • Datenschutz: Kandidaten-Statistiken können IP-Adressen enthalten (address). Beim Versand an Telemetrie-Server diese Felder entfernen oder anonymisieren.