Signatur
Beschreibung
Die schreibgeschützte Eigenschaft silentConcealedSamples eines RTCInboundRtpStreamStats-Objekts (Statistik-Typ "inbound-rtp") zählt die Samples, die der Empfänger über die gesamte Lebensdauer des Statistik-Objekts verdeckt hat und die dabei stumm waren. Gemeint sind Samples, die keinen hörbaren Inhalt haben, also Stille oder Komfortrauschen (Comfort Noise).
Verdeckung (Concealment) setzt ein, wenn RTP-Pakete verloren gehen oder zu spät ankommen. Der Jitter-Buffer bzw. der Decoder erzeugt dann Ersatz-Samples. Der Wert ist eine Teilmenge von concealedSamples. Ziehst du ihn davon ab, bleiben die hörbar verdeckten Samples übrig, und genau die beeinträchtigen die wahrgenommene Audioqualität tatsächlich.
Nützlich ist die Eigenschaft vor allem für die Qualitätsüberwachung in WebRTC-Anwendungen: für Dashboards, Telemetrie oder das automatische Erkennen schlechter Verbindungen. Weil der Zähler nur wächst, vergleichst du am besten zwei Messungen und rechnest mit der Differenz zu totalSamplesReceived weiter.
Die Eigenschaft gibt es nur bei Audio-Streams (kind === "audio"). Bei Video-Streams fehlt sie.
Rückgabewert
unsigned long long) mit der Gesamtzahl der stumm verdeckten Samples. undefined bei Video-Streams oder wenn der Browser den Wert nicht unterstützt.Beispiele
Wert aus den Statistiken auslesen
// pc ist eine bestehende RTCPeerConnection mit empfangener Audiospur
const logSilentConcealment = async (pc) => {
const report = await pc.getStats();
report.forEach((stats) => {
if (stats.type === 'inbound-rtp' && stats.kind === 'audio') {
console.log(`Verdeckt gesamt: ${stats.concealedSamples}`);
console.log(`Davon stumm: ${stats.silentConcealedSamples}`);
}
});
};
logSilentConcealment(pc);
Anteil hörbarer Verdeckung pro Intervall berechnen
let previous = null;
const measureAudibleConcealment = async (receiver) => {
const report = await receiver.getStats();
const stats = [...report.values()].find(
(s) => s.type === 'inbound-rtp' && s.kind === 'audio'
);
if (!stats || stats.silentConcealedSamples === undefined) return;
if (previous) {
const received = stats.totalSamplesReceived - previous.totalSamplesReceived;
const concealed = stats.concealedSamples - previous.concealedSamples;
const silent = stats.silentConcealedSamples - previous.silentConcealedSamples;
const audible = concealed - silent;
const ratio = received > 0 ? (audible / received) * 100 : 0;
console.log(`Hörbar verdeckt: ${ratio.toFixed(2)} %`);
}
previous = stats;
};
const [audioReceiver] = pc.getReceivers().filter((r) => r.track.kind === 'audio');
setInterval(() => measureAudibleConcealment(audioReceiver), 2000);
Qualitätswarnung bei hoher Verdeckungsrate
const checkAudioQuality = async (pc, threshold = 0.05) => {
const report = await pc.getStats();
for (const s of report.values()) {
if (s.type !== 'inbound-rtp' || s.kind !== 'audio') continue;
const audible = (s.concealedSamples ?? 0) - (s.silentConcealedSamples ?? 0);
const rate = s.totalSamplesReceived ? audible / s.totalSamplesReceived : 0;
if (rate > threshold) {
console.warn('Schlechte Audioqualität erkannt:', (rate * 100).toFixed(1), '%');
} else {
console.log('Audioqualität in Ordnung');
}
}
};
checkAudioQuality(pc);
// Wichtig · Fallstricke
- Kumulativer Zähler: Der Wert zählt ab dem Anlegen des Statistik-Objekts nur hoch. Aussagekräftige Raten bekommst du nur aus der Differenz zweier Messungen.
- Teilmenge:
silentConcealedSamplesist immer ≤concealedSamples. Hohe stumme Werte sind meist unkritisch, etwa wenn der Sender Sprechpausen per DTX/Komfortrauschen überträgt. - Nur Audio: Bei
kind === "video"fehlt die Eigenschaft. Prüfe deshalb aufundefinedoder nutze??. - Samples ≠ Pakete: Gezählt werden Audio-Samples pro Kanal, keine RTP-Pakete. Bei 48 kHz entsprechen 480 Samples 10 ms Audio.
- Kompatibilität: Chromium-Browser unterstützen die Eigenschaft seit Langem. In Firefox und Safari kam sie später dazu. Vor dem Einsatz Funktionserkennung betreiben.