Start · Sprachen · JavaScript · Referenz · RTCIceCandidatePairStats.timestamp

RTCIceCandidatePairStats.timestamp

Eigenschaft

Ein <code>DOMHighResTimeStamp</code> in Millisekunden, der angibt, wann die Statistikdaten dieses ICE-Kandidatenpaars erfasst wurden.

seit JavaScript Baseline: Chrome, Edge, Firefox, Safari (WebRTC St Kategorie: webapi

Signatur

RTCIceCandidatePairStats.timestamp

Beschreibung

Die schreibgeschützte Eigenschaft timestamp eines RTCIceCandidatePairStats-Objekts gibt an, zu welchem Zeitpunkt die übrigen Werte im Objekt erfasst wurden, zum Beispiel bytesSent, bytesReceived, currentRoundTripTime oder requestsReceived. Der Wert ist ein DOMHighResTimeStamp, also eine Gleitkommazahl in Millisekunden, die auch Bruchteile einer Millisekunde enthalten kann.

Laut Spezifikation bezieht sich der Zeitstempel auf die Unix-Epoche (1. Januar 1970, UTC). Er entspricht also performance.timeOrigin + performance.now() zum Erfassungszeitpunkt und liegt größenordnungsmäßig bei Date.now(). Die Eigenschaft wird vom Basis-Wörterbuch RTCStats geerbt. Alle Statistiktypen eines RTCStatsReport haben deshalb einen solchen Zeitstempel.

Wirklich nützlich wird timestamp, wenn du Raten berechnen willst. Die Zähler eines Kandidatenpaars wachsen nur an (kumulative Werte). Um Bitrate, Paketrate oder die Zahl der STUN-Anfragen pro Sekunde zu bestimmen, rufst du getStats() zweimal auf und teilst die Differenz der Zähler durch die Differenz der Zeitstempel. Der tatsächliche Erfassungszeitpunkt ist dabei genauer als eine selbst gemessene Zeit rund um den await-Aufruf.

Rückgabewert

Typ
number (DOMHighResTimeStamp)
Beschreibung
Erfassungszeitpunkt der Statistik in Millisekunden seit der Unix-Epoche, als Gleitkommazahl mit Bruchteilen einer Millisekunde.

Beispiele

Zeitstempel des aktiven Kandidatenpaars auslesen

// pc ist eine verbundene RTCPeerConnection
const logSelectedPair = async (pc) => {
  const report = await pc.getStats();
  let transport;
  report.forEach((s) => { if (s.type === 'transport') transport = s; });
  const pair = transport && report.get(transport.selectedCandidatePairId);
  if (!pair) return console.log('Kein aktives Kandidatenpaar');

  console.log('Erfasst am:', new Date(pair.timestamp).toISOString());
  console.log('Alter der Daten (ms):', (Date.now() - pair.timestamp).toFixed(1));
};

logSelectedPair(pc);
Erfasst am: 2024-05-14T09:31:07.412Z Alter der Daten (ms): 0.8

Sende- und Empfangsbitrate aus zwei Messungen berechnen

const getSelectedPair = async (pc) => {
  const report = await pc.getStats();
  for (const s of report.values()) {
    if (s.type === 'transport' && s.selectedCandidatePairId) {
      return report.get(s.selectedCandidatePairId);
    }
  }
  // Fallback: nominiertes, erfolgreiches Paar
  return [...report.values()].find(
    (s) => s.type === 'candidate-pair' && s.nominated && s.state === 'succeeded'
  );
};

const measureBitrate = async (pc, intervalMs = 1000) => {
  const a = await getSelectedPair(pc);
  await new Promise((r) => setTimeout(r, intervalMs));
  const b = await getSelectedPair(pc);
  if (!a || !b || a.id !== b.id) return null; // Paar hat gewechselt

  const seconds = (b.timestamp - a.timestamp) / 1000;
  return {
    sendKbps: ((b.bytesSent - a.bytesSent) * 8) / seconds / 1000,
    recvKbps: ((b.bytesReceived - a.bytesReceived) * 8) / seconds / 1000,
  };
};

const rates = await measureBitrate(pc);
console.log(rates);
{ sendKbps: 1487.3, recvKbps: 1520.9 }

Laufendes Monitoring mit veralteten Werten erkennen

let last = null;

const timer = setInterval(async () => {
  const report = await pc.getStats();
  const pair = [...report.values()].find(
    (s) => s.type === 'candidate-pair' && s.nominated
  );
  if (!pair) return;

  if (last && pair.timestamp === last.timestamp) {
    console.warn('Statistik wurde nicht aktualisiert (Cache)');
    return;
  }
  if (last) {
    const dt = (pair.timestamp - last.timestamp) / 1000;
    const reqPerSec = (pair.requestsSent - last.requestsSent) / dt;
    console.log(`RTT: ${pair.currentRoundTripTime * 1000} ms, STUN-Anfragen/s: ${reqPerSec.toFixed(2)}`);
  }
  last = pair;
}, 2000);

// Beim Beenden: clearInterval(timer); pc.close();
RTT: 23 ms, STUN-Anfragen/s: 0.40

// Wichtig · Fallstricke

  • Nicht mit performance.now() vergleichen: Der Wert bezieht sich auf die Unix-Epoche und nicht auf den Seitenstart. Zum Vergleichen eignen sich Date.now() oder performance.timeOrigin + performance.now(). Ältere Browserversionen hielten sich hier nicht immer an die Spezifikation. Verlass dich deshalb nur auf die Differenzen zwischen zwei Zeitstempeln.
  • Browser-Cache: Chrome speichert Statistiken kurzzeitig zwischen (etwa 50 ms). Rufst du getStats() sehr schnell hintereinander auf, kann derselbe timestamp zurückkommen. Eine Division durch eine Zeitdifferenz von 0 ergibt dann NaN oder Infinity. Prüfe das vorher.
  • Paarwechsel beachten: Bei einem ICE-Neustart oder einer neuen Nominierung kann sich das aktive Paar ändern. Berechne Raten nur, wenn beide Messungen dieselbe id haben. Sonst kommen durch die Zählerdifferenzen negative oder unsinnige Werte heraus.
  • Die Zeitgenauigkeit kann aus Datenschutzgründen reduziert sein (Schutz vor Fingerprinting und Timing-Angriffen, z. B. ohne Cross-Origin-Isolation).
  • Firefox kannte früher die nicht standardisierte Eigenschaft selected am Kandidatenpaar. Standardkonform ist der Weg über RTCTransportStats.selectedCandidatePairId.