Start · Sprachen · JavaScript · Referenz · RTCInboundRtpStreamStats.silentConcealedSamples

RTCInboundRtpStreamStats.silentConcealedSamples

Eigenschaft

Gibt an, wie viele verdeckte (künstlich erzeugte) Audio-Samples der empfangenen Spur stumm oder als Komfortrauschen eingefügt wurden.

seit JavaScript Chrome 86+, Firefox 125+, Safari 16.4+ (nicht Base Kategorie: webapi

Signatur

RTCInboundRtpStreamStats.silentConcealedSamples

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

Typ
number | undefined
Beschreibung
Eine nicht negative Ganzzahl (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);
Verdeckt gesamt: 4800 Davon stumm: 960

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);
Hörbar verdeckt: 0.85 %

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);
Audioqualität in Ordnung

// 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: silentConcealedSamples ist 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 auf undefined oder 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.