Signatur
Beschreibung
Die Eigenschaft nackCount eines RTCOutboundRtpStreamStats-Objekts zählt, wie viele NACK-Pakete (Negative Acknowledgements, RTCP-Feedback nach RFC 4585) der Sender vom entfernten Empfänger bekommen hat. Mit einem NACK meldet der Empfänger, dass ein oder mehrere RTP-Pakete verloren gegangen sind, und fordert sie erneut an.
Der Wert ist ein kumulativer Zähler ab Beginn des Streams. Er steigt nur, er wird also nicht zurückgesetzt. Aussagekräftig wird er, wenn man die Differenz zwischen zwei Abfragen von RTCPeerConnection.getStats() bildet: Daraus ergibt sich eine NACK-Rate pro Sekunde, ein guter Hinweis auf Paketverlust auf dem Weg zum Empfänger.
Wichtig: Ein NACK-Paket kann mehrere verlorene Sequenznummern enthalten. nackCount zählt die NACK-Pakete, nicht die angeforderten RTP-Pakete. Um zu sehen, wie viel tatsächlich neu gesendet wurde, nimmt man retransmittedPacketsSent und retransmittedBytesSent dazu. Auf der Empfängerseite gibt es das Gegenstück RTCInboundRtpStreamStats.nackCount, also die Zahl der gesendeten NACKs.
Rückgabewert
unsigned long): Gesamtzahl der vom Sender empfangenen NACK-Pakete. Kann undefined sein, wenn der Browser den Wert nicht liefert oder NACK nicht ausgehandelt wurde.Beispiele
NACK-Zähler aller ausgehenden Streams auslesen
// pc ist eine verbundene RTCPeerConnection
const logNacks = async (pc) => {
const stats = await pc.getStats();
stats.forEach((report) => {
if (report.type === 'outbound-rtp') {
console.log(`${report.kind} (SSRC ${report.ssrc}): ${report.nackCount ?? 'k. A.'} NACKs`);
}
});
};
await logNacks(pc);
NACK-Rate pro Sekunde überwachen
const monitorNackRate = (pc, intervalMs = 2000) => {
const previous = new Map();
return setInterval(async () => {
const stats = await pc.getStats();
for (const report of stats.values()) {
if (report.type !== 'outbound-rtp' || report.nackCount === undefined) continue;
const prev = previous.get(report.id);
if (prev) {
const seconds = (report.timestamp - prev.timestamp) / 1000;
const rate = (report.nackCount - prev.nackCount) / seconds;
if (rate > 5) {
console.warn(`Hoher Paketverlust bei ${report.kind}: ${rate.toFixed(1)} NACK/s`);
}
}
previous.set(report.id, { nackCount: report.nackCount, timestamp: report.timestamp });
}
}, intervalMs);
};
const timer = monitorNackRate(pc);
// später: clearInterval(timer);
NACKs zu Neuübertragungen ins Verhältnis setzen
const sender = pc.getSenders().find((s) => s.track?.kind === 'video');
const stats = await sender.getStats();
for (const report of stats.values()) {
if (report.type === 'outbound-rtp') {
const { nackCount = 0, retransmittedPacketsSent = 0, packetsSent = 0 } = report;
console.log({
nackCount,
retransmittedPacketsSent,
retransmitAnteil: packetsSent ? `${((retransmittedPacketsSent / packetsSent) * 100).toFixed(2)} %` : '0 %'
});
}
}
// Wichtig · Fallstricke
- Kumulativer Zähler: Der absolute Wert sagt allein wenig. Für Qualitätsmetriken immer Differenzen über die Zeit bilden und dafür
timestampverwenden. - Pakete, nicht Verluste: Ein NACK kann mehrere verlorene Pakete melden.
nackCountist daher kein direktes Maß für den Paketverlust. Für den Verlust aus Empfängersicht gibt esRTCRemoteInboundRtpStreamStats.packetsLostbzw.fractionLost. - Aushandlung: NACKs gibt es nur, wenn im SDP
a=rtcp-fb:… nackausgehandelt wurde. Bei Video ist das üblich, bei Audio (Opus) meist nicht. Dort bleibt der Wert oft bei0oder fehlt ganz. - Kompatibilität: Chromium-Browser liefern den Wert zuverlässig. Firefox und Safari unterstützen ihn ebenfalls, aber je nach Version nicht für alle Codecs. Deshalb mit
?? 0oder einerundefined-Prüfung absichern. - Bei Simulcast gibt es pro Schicht (
rid) einen eigenenoutbound-rtp-Bericht mit einem eigenennackCount.