Signatur
Beschreibung
Die Eigenschaft totalSamplesReceived gehört zu einem RTCInboundRtpStreamStats-Objekt. Solche Objekte liefert RTCPeerConnection.getStats() bzw. RTCRtpReceiver.getStats() als Einträge mit type === "inbound-rtp". Der Wert ist ein fortlaufender Zähler. Er erfasst alle Audio-Samples, die der Decoder ausgegeben hat. Dazu gehören auch Samples, die der Jitter-Buffer wegen Paketverlust künstlich erzeugt hat (Concealment).
Die Eigenschaft gibt es nur bei Audio-Streams (kind === "audio"). Bei Stereo-Audio zählen die Samples beider Kanäle zusammen als ein Sample, der Zähler steigt also pro Zeitpunkt und nicht pro Kanal. Bei 48 kHz wächst der Wert um etwa 48.000 pro Sekunde.
Am meisten bringt der Wert zusammen mit concealedSamples und silentConcealedSamples. Das Verhältnis concealedSamples / totalSamplesReceived ist eine gängige Kennzahl für die Audioqualität. Wenn sie steigt, gehen Pakete verloren oder kommen zu spät an, und der Nutzer hört Aussetzer. Weil es ein kumulativer Zähler ist, bildet man für Live-Werte die Differenz zwischen zwei Messungen.
Beispiele
Empfangene Samples eines Audio-Streams auslesen
// pc ist eine bestehende, verbundene RTCPeerConnection
const logAudioSamples = async (pc) => {
const report = await pc.getStats();
report.forEach((stats) => {
if (stats.type === 'inbound-rtp' && stats.kind === 'audio') {
console.log(`Empfangene Samples: ${stats.totalSamplesReceived}`);
console.log(`Davon verschleiert: ${stats.concealedSamples}`);
}
});
};
logAudioSamples(pc);
Concealment-Rate als Qualitätskennzahl berechnen
const getConcealmentRate = async (receiver) => {
const report = await receiver.getStats();
for (const stats of report.values()) {
if (stats.type === 'inbound-rtp' && stats.kind === 'audio') {
const { totalSamplesReceived = 0, concealedSamples = 0 } = stats;
if (totalSamplesReceived === 0) return 0;
return concealedSamples / totalSamplesReceived;
}
}
return null;
};
const [audioReceiver] = pc.getReceivers().filter((r) => r.track.kind === 'audio');
const rate = await getConcealmentRate(audioReceiver);
console.log(`Concealment-Rate: ${(rate * 100).toFixed(2)} %`);
Live-Monitoring über Differenzen zwischen zwei Messungen
let previous = null;
const monitorAudio = async (pc) => {
const report = await pc.getStats();
for (const stats of report.values()) {
if (stats.type !== 'inbound-rtp' || stats.kind !== 'audio') continue;
if (previous) {
const deltaTotal = stats.totalSamplesReceived - previous.totalSamplesReceived;
const deltaConcealed = stats.concealedSamples - previous.concealedSamples;
const rate = deltaTotal > 0 ? deltaConcealed / deltaTotal : 0;
if (rate > 0.05) {
console.warn(`Schlechte Audioqualität: ${(rate * 100).toFixed(1)} % verschleiert`);
} else {
console.log(`Samples in der letzten Sekunde: ${deltaTotal}`);
}
}
previous = stats;
}
};
const intervalId = setInterval(() => monitorAudio(pc), 1000);
// Später: clearInterval(intervalId);
// Wichtig · Fallstricke
- Nur Audio: Bei Video-Streams (
kind === "video") gibt es die Eigenschaft nicht. Deshalb vorher immerkindprüfen oder einen Standardwert setzen. - Kumulativ: Der Zähler wird nie zurückgesetzt, solange der Stream besteht. Für aktuelle Qualitätswerte also Differenzen zwischen zwei
getStats()-Aufrufen bilden und nicht die Absolutwerte vergleichen. - Enthält verschleierte Samples: Ein steigender Wert heißt nicht, dass echte Audiodaten angekommen sind. Die tatsächlich empfangenen Samples sind ungefähr
totalSamplesReceived - concealedSamples. - Kanäle: Bei Mehrkanal-Audio zählt ein Sample pro Zeitpunkt, nicht pro Kanal.
- Browser-Kompatibilität: Chromium-basierte Browser und Safari unterstützen die Eigenschaft. In Firefox kann sie fehlen oder anders gefüllt sein. Beim Zugriff am besten mit einem Standardwert absichern (
stats.totalSamplesReceived ?? 0). - Die Statistiken werden intern nur in bestimmten Abständen aktualisiert. Zu häufiges Abfragen (z. B. öfter als alle 100 ms) liefert keine genaueren Werte und kostet unnötig Rechenzeit.