Start · Sprachen · JavaScript · Referenz · RTCInboundRtpStreamStats.keyFramesDecoded

RTCInboundRtpStreamStats.keyFramesDecoded

Eigenschaft

Gibt an, wie viele Schlüsselbilder (Keyframes) in einem eingehenden RTP-Videostrom bisher insgesamt erfolgreich dekodiert wurden.

seit JavaScript Chrome 74+, Edge 79+, Safari 17.4+; Firefox: bishe Kategorie: webapi

Signatur

RTCInboundRtpStreamStats.keyFramesDecoded

Beschreibung

Die Eigenschaft keyFramesDecoded gehört zum Wörterbuch RTCInboundRtpStreamStats. Dieses Objekt steht in dem RTCStatsReport, den RTCPeerConnection.getStats() liefert, und zwar in Einträgen vom Typ "inbound-rtp". Der Wert ist ein fortlaufender Zähler (unsigned long). Er zählt die Schlüsselbilder, die der Decoder für diesen Medienstrom bisher erfolgreich verarbeitet hat. Bei VP8 sind das Keyframes, bei H.264 IDR-Frames.

Ein Schlüsselbild ist ein vollständiges Bild, das nicht von früheren Bildern abhängt. Nach einem Paketverlust, einem Beitritt zu einer laufenden Sitzung oder einem Auflösungswechsel muss der Empfänger ein neues Keyframe erhalten, bevor er wieder sauber dekodieren kann. Deshalb eignet sich keyFramesDecoded gut für die Diagnose der Videoqualität. Steigt der Zähler oft, fordert der Empfänger häufig neue Keyframes an (per PLI/FIR). Das deutet meist auf Paketverlust oder eine instabile Verbindung hin.

Aussagekräftig wird der Wert vor allem im Vergleich mit anderen Kennzahlen. Das Verhältnis zu framesDecoded zeigt den Anteil der Schlüsselbilder an allen dekodierten Bildern. pliCount und firCount zeigen, wie viele Keyframe-Anfragen der Empfänger gesendet hat. Die Eigenschaft gibt es nur bei Video-Strömen (kind === "video").

Rückgabewert

Typ
number
Beschreibung
Eine nicht-negative ganze Zahl: wie viele Schlüsselbilder in diesem eingehenden Videostrom bisher insgesamt erfolgreich dekodiert wurden. Bei Audio-Strömen oder fehlender Browser-Unterstützung ist der Wert undefined.

Beispiele

Anzahl dekodierter Keyframes auslesen

// pc ist eine bestehende, verbundene RTCPeerConnection
const logKeyFrames = async (pc) => {
  const report = await pc.getStats();
  report.forEach((stats) => {
    if (stats.type === 'inbound-rtp' && stats.kind === 'video') {
      console.log(`SSRC ${stats.ssrc}: ${stats.keyFramesDecoded ?? 'n/a'} Keyframes dekodiert`);
    }
  });
};

await logKeyFrames(pc);
SSRC 123456789: 14 Keyframes dekodiert

Keyframe-Rate pro Intervall überwachen

// Misst alle 5 Sekunden, wie viele neue Keyframes dazugekommen sind
const monitorKeyFrames = (pc, intervalMs = 5000) => {
  let last = new Map();

  const timer = setInterval(async () => {
    const report = await pc.getStats();
    for (const stats of report.values()) {
      if (stats.type !== 'inbound-rtp' || stats.kind !== 'video') continue;
      if (typeof stats.keyFramesDecoded !== 'number') continue;

      const prev = last.get(stats.id) ?? stats.keyFramesDecoded;
      const delta = stats.keyFramesDecoded - prev;
      last.set(stats.id, stats.keyFramesDecoded);

      if (delta > 2) {
        console.warn(`Viele Keyframes (${delta}) in ${intervalMs / 1000}s – mögliche Paketverluste`);
      }
    }
  }, intervalMs);

  return () => clearInterval(timer);
};

const stopMonitoring = monitorKeyFrames(pc);
// später: stopMonitoring();
Viele Keyframes (4) in 5s – mögliche Paketverluste

Keyframe-Anteil und PLI-Anfragen vergleichen

const analyzeVideo = async (receiver) => {
  // getStats() direkt am RTCRtpReceiver liefert nur die Statistik dieses Tracks
  const report = await receiver.getStats();
  for (const s of report.values()) {
    if (s.type === 'inbound-rtp' && s.kind === 'video' && s.framesDecoded > 0) {
      const ratio = ((s.keyFramesDecoded / s.framesDecoded) * 100).toFixed(2);
      console.log({
        framesDecoded: s.framesDecoded,
        keyFramesDecoded: s.keyFramesDecoded,
        keyFrameAnteil: `${ratio} %`,
        pliCount: s.pliCount,
      });
    }
  }
};

const [videoReceiver] = pc.getReceivers().filter((r) => r.track.kind === 'video');
await analyzeVideo(videoReceiver);
{ framesDecoded: 3000, keyFramesDecoded: 6, keyFrameAnteil: '0.20 %', pliCount: 3 }

// Wichtig · Fallstricke

  • Nur Video: Bei Audio-Einträgen vom Typ inbound-rtp fehlt die Eigenschaft. Filtern Sie daher immer nach kind === "video".
  • Kumulativer Zähler: Der Wert steigt über die ganze Lebensdauer des Stroms nur an. Um Raten zu berechnen, bilden Sie die Differenz zwischen zwei Messungen und beziehen sie auf die Zeit (timestamp).
  • Browser-Kompatibilität: Chromium-basierte Browser und neuere Safari-Versionen unterstützen die Eigenschaft. Firefox liefert sie derzeit nicht. Prüfen Sie deshalb mit typeof stats.keyFramesDecoded === "number", bevor Sie rechnen.
  • Simulcast/SVC: Wechselt der SFU die Ebene oder die Auflösung, kommen oft zusätzliche Keyframes. Ein Anstieg bedeutet also nicht zwingend Paketverlust.
  • Performance: getStats() ist asynchron und nicht kostenlos. Fragen Sie die Statistik höchstens etwa einmal pro Sekunde ab.