Start · Sprachen · JavaScript · Referenz · RTCOutboundRtpStreamStats.totalEncodeTime

RTCOutboundRtpStreamStats.totalEncodeTime

Eigenschaft

Gibt an, wie viele Sekunden insgesamt nötig waren, um die <code>framesEncoded</code> Frames dieses ausgehenden Video-Streams zu kodieren.

seit JavaScript Chrome 80+, Edge 80+, Safari 14.1+, Firefox 107+ ( Kategorie: webapi

Signatur

RTCOutboundRtpStreamStats.totalEncodeTime

Beschreibung

Die Eigenschaft totalEncodeTime des Wörterbuchs RTCOutboundRtpStreamStats ist eine Gleitkommazahl. Sie gibt die aufsummierte Zeit in Sekunden an, die der Encoder für alle bisher kodierten Frames dieses RTCRtpSender gebraucht hat. Der Wert steigt monoton und bezieht sich immer auf genau die Frames, die in framesEncoded gezählt werden.

Teilt man totalEncodeTime durch framesEncoded, erhält man die durchschnittliche Kodierzeit pro Frame. Das ist eine wichtige Kennzahl für die Last des Encoders. Nähert sich dieser Durchschnitt dem Frame-Intervall (bei 30 fps etwa 33 ms), kommt die CPU oder der Hardware-Encoder nicht mehr hinterher. Der Browser senkt dann oft selbst Auflösung oder Bildrate, was sich an qualityLimitationReason === "cpu" erkennen lässt.

Du bekommst den Wert über RTCPeerConnection.getStats() oder RTCRtpSender.getStats(). Er steht in den Einträgen mit type === "outbound-rtp" und kind === "video". Weil es ein kumulativer Zähler ist, solltest du für aktuelle Werte zwei Messungen nehmen und die Differenzen (Delta-Werte) auswerten, statt den Gesamtwert direkt zu verwenden.

Rückgabewert

Typ
number | undefined
Beschreibung
Aufsummierte Kodierzeit in Sekunden (Gleitkommazahl, z. B. 12.483). Bei Audio-Streams oder in Browsern ohne Unterstützung ist der Wert undefined.

Beispiele

Durchschnittliche Kodierzeit pro Frame auslesen

// pc ist eine bestehende RTCPeerConnection, die Video sendet
const logEncodeTime = async (pc) => {
  const report = await pc.getStats();
  report.forEach((stat) => {
    if (stat.type === 'outbound-rtp' && stat.kind === 'video' && stat.framesEncoded > 0) {
      const avgMs = (stat.totalEncodeTime / stat.framesEncoded) * 1000;
      console.log(`Stream ${stat.ssrc}: ${avgMs.toFixed(2)} ms pro Frame`);
    }
  });
};

await logEncodeTime(pc);
Stream 123456789: 4.87 ms pro Frame

Kodierzeit pro Intervall über Delta-Werte überwachen

const sender = pc.getSenders().find((s) => s.track?.kind === 'video');
let prev = null;

const sample = async () => {
  const report = await sender.getStats();
  for (const stat of report.values()) {
    if (stat.type !== 'outbound-rtp') continue;
    if (prev) {
      const frames = stat.framesEncoded - prev.framesEncoded;
      const time = stat.totalEncodeTime - prev.totalEncodeTime;
      if (frames > 0) {
        const msPerFrame = (time / frames) * 1000;
        console.log(`Letzte Sekunde: ${frames} Frames, ${msPerFrame.toFixed(2)} ms/Frame`);
      }
    }
    prev = { framesEncoded: stat.framesEncoded, totalEncodeTime: stat.totalEncodeTime };
  }
};

const timer = setInterval(sample, 1000);
// später: clearInterval(timer);
Letzte Sekunde: 30 Frames, 5.12 ms/Frame

Warnung bei überlastetem Encoder

const checkEncoderLoad = async (pc, fps = 30) => {
  const budgetMs = 1000 / fps;
  const report = await pc.getStats();
  for (const stat of report.values()) {
    if (stat.type === 'outbound-rtp' && stat.kind === 'video' && stat.framesEncoded) {
      const avgMs = (stat.totalEncodeTime ?? 0) / stat.framesEncoded * 1000;
      if (avgMs > budgetMs * 0.8) {
        console.warn(`Encoder am Limit: ${avgMs.toFixed(1)} ms (Budget ${budgetMs.toFixed(1)} ms), Grund: ${stat.qualityLimitationReason}`);
      } else {
        console.log(`Encoder ok: ${avgMs.toFixed(1)} ms`);
      }
    }
  }
};

await checkEncoderLoad(pc);
Encoder ok: 4.9 ms

// Wichtig · Fallstricke

  • Nur Video: Bei Audio-Einträgen (kind === "audio") fehlt die Eigenschaft. Prüfe also immer kind und ob der Wert überhaupt vorhanden ist (z. B. mit ?? 0).
  • Einheit ist Sekunden, nicht Millisekunden. Für ms-Werte musst du mit 1000 multiplizieren.
  • Kumulativer Zähler: Der Gesamtwert glättet kurze Lastspitzen weg. Echtzeit-Auswertungen gehen nur über Delta-Werte zwischen zwei getStats()-Aufrufen.
  • Simulcast: Jede Simulcast-Schicht hat einen eigenen outbound-rtp-Eintrag mit eigener ssrc und eigener totalEncodeTime. Für die Gesamtlast musst du die Werte aller Schichten addieren.
  • Division durch null: Solange framesEncoded gleich 0 ist, liefert die Division NaN.
  • Hardware-Encoder melden die Zeit je nach Plattform unterschiedlich genau. Werte aus verschiedenen Browsern oder Geräten sind deshalb nur bedingt vergleichbar.
  • Browser-Unterstützung: In Chromium-Browsern und Safari schon länger verfügbar, in Firefox erst ab Version 107. Ist die Eigenschaft nicht vorhanden, ist der Wert undefined.