Signatur
Beschreibung
Die schreibgeschützte Eigenschaft totalEncodedBytesTarget des Wörterbuchs RTCOutboundRtpStreamStats ist ein Zähler, der nur wächst. Jedes Mal, wenn der Encoder einen Frame erfolgreich enkodiert hat, wird die Zielgröße dieses Frames in Bytes addiert. Die Zielgröße ist die Größe, die der Encoder laut seiner Ratensteuerung für den Frame anpeilt. Wie groß der Frame am Ende tatsächlich wird, spielt für diesen Wert keine Rolle.
Der Wert zeigt also, wie viele Daten der Encoder liefern sollte. Setzt du ihn ins Verhältnis zu framesEncoded, erhältst du die durchschnittliche Zielgröße pro Frame. Vergleichst du ihn mit den tatsächlich gesendeten Nutzdaten (bytesSent - headerBytesSent), siehst du, ob der Encoder sein Ziel regelmäßig über- oder unterschreitet. Das hilft bei der Fehlersuche in der Bandbreitenschätzung, bei Qualitätseinbrüchen oder bei zu hoher Datenrate.
Du liest die Eigenschaft aus einem RTCStatsReport, den RTCPeerConnection.getStats() oder RTCRtpSender.getStats() liefert. Dort filterst du nach Einträgen mit type === "outbound-rtp". Da es sich um einen kumulierten Zähler handelt, brauchst du für Aussagen über einen Zeitraum zwei Momentaufnahmen und ihre Differenz.
Rückgabewert
unsigned long long): Summe der Zielgrößen aller bisher enkodierten Frames in Bytes. Gibt der Browser die Metrik nicht an, fehlt die Eigenschaft und der Wert ist undefined. Bei Audio-Streams ist sie in der Regel nicht vorhanden.Beispiele
Durchschnittliche Zielgröße pro Frame ermitteln
// pc ist eine bestehende RTCPeerConnection, die Video sendet
const logTargetFrameSize = async (pc) => {
const report = await pc.getStats();
report.forEach((stats) => {
if (stats.type !== 'outbound-rtp' || stats.kind !== 'video') return;
const { totalEncodedBytesTarget, framesEncoded } = stats;
if (totalEncodedBytesTarget === undefined || !framesEncoded) {
console.log('Metrik nicht verfügbar');
return;
}
const avg = totalEncodedBytesTarget / framesEncoded;
console.log(`SSRC ${stats.ssrc}: Ø Zielgröße ${avg.toFixed(0)} Bytes/Frame`);
});
};
logTargetFrameSize(pc);
Ziel-Bitrate des Encoders über ein Intervall berechnen
// Differenz zweier Momentaufnahmen -> angestrebte Nutzdaten-Bitrate
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
const getVideoOutbound = async (sender) => {
const report = await sender.getStats();
return [...report.values()].find((s) => s.type === 'outbound-rtp');
};
const measureTargetBitrate = async (sender, intervalMs = 2000) => {
const a = await getVideoOutbound(sender);
await sleep(intervalMs);
const b = await getVideoOutbound(sender);
if (a?.totalEncodedBytesTarget === undefined) return null;
const bytes = b.totalEncodedBytesTarget - a.totalEncodedBytesTarget;
const seconds = (b.timestamp - a.timestamp) / 1000;
return Math.round((bytes * 8) / seconds / 1000); // kbit/s
};
const [videoSender] = pc.getSenders().filter((s) => s.track?.kind === 'video');
console.log(`Ziel: ${await measureTargetBitrate(videoSender)} kbit/s`);
Soll und Ist der Encoder-Ausgabe vergleichen
// Überschreitet der Encoder dauerhaft sein Ziel?
const compareTargetVsActual = async (pc) => {
const report = await pc.getStats();
for (const s of report.values()) {
if (s.type !== 'outbound-rtp' || s.totalEncodedBytesTarget === undefined) continue;
// Nutzdaten ohne RTP-Header (Retransmissions sind hier enthalten)
const payload = s.bytesSent - (s.headerBytesSent ?? 0);
const ratio = payload / s.totalEncodedBytesTarget;
console.log(`Ist/Soll-Verhältnis: ${ratio.toFixed(2)}`);
if (ratio > 1.2) console.warn('Encoder liegt deutlich über dem Ziel');
}
};
compareTargetVsActual(pc);
// Wichtig · Fallstricke
- Kompatibilität: Die Metrik steht in der W3C-Spezifikation „Identifiers for WebRTC's Statistics API“, wird aber derzeit nur von Chromium-Browsern (Chrome, Edge, Opera) gemeldet. In Firefox und Safari fehlt sie meist. Prüfe deshalb immer auf
undefined. - Kumulierter Zähler: Der Wert zählt seit Beginn des Streams hoch. Für aktuelle Raten brauchst du zwei Momentaufnahmen und die Differenz ihrer
timestamp-Werte in Millisekunden. - Soll statt Ist: Der Wert sagt nichts über tatsächlich gesendete Daten aus. Für die echte Datenmenge nimmst du
bytesSentbzw.headerBytesSent. Diese Zähler enthalten allerdings auch Retransmissions und Padding. Ein Vergleich mit dem Zielwert ist deshalb nur ein Näherungswert. - Simulcast/SVC: Bei Simulcast gibt es pro Schicht (
rid) einen eigenenoutbound-rtp-Eintrag mit eigenem Zähler. Für eine Gesamtbetrachtung musst du die Werte selbst addieren. - Pausierte oder deaktivierte Encodings (
active: false) erhöhen den Zähler nicht.