demo · v141

RTP Stats Explorer

Set up a loopback RTCPeerConnection, poll getStats() every second, and drill into the full stats map by type. Filter, inspect, and chart jitter and packet counts over time — all in-page, no server needed.

probing RTCPeerConnection…

Set up a loopback peer connection first, then poll stats.

stats timeline

jitter & packets received over time (auto-poll)

jitter (ms×10) packetsReceived (scaled) packetsLost

the chrome 141 alignment

What changed Before Chrome 141, outbound-rtp and inbound-rtp entries only appeared in getStats() after the first RTP packet was actually sent or received. Firefox and Safari created these rows as soon as the SSRC was negotiated via SDP — matching the W3C spec. Chrome 141 aligns to the spec, so RTP stats rows now exist even with zero packets sent.
Browser outbound-rtp on addTrack? outbound-rtp after offer/answer? outbound-rtp after first packet?
Chrome < 141 no no yes
Chrome 141+ yes ✓ spec yes ✓ spec yes
Firefox yes ✓ spec yes ✓ spec yes
Safari yes ✓ spec yes ✓ spec yes

zero-byte row callout

Operational consequence With Chrome 141, outbound-rtp rows appear before any packets are sent — packetsSent: 0, bytesSent: 0. Bitrate monitors that divide bytesSent by elapsed time will show 0 kbps or NaN for the first poll cycle. One-line fix:
if (r.type === "outbound-rtp" && r.packetsSent === 0) continue;

the call

const pc = new RTCPeerConnection();
// … addTrack, offer/answer …

const stats = await pc.getStats();

stats.forEach((report) => {
  console.log(report.type, report.id);

  if (report.type === "inbound-rtp") {
    console.log("jitter:", report.jitter);
    console.log("packetsLost:", report.packetsLost);
    console.log("packetsReceived:", report.packetsReceived);
    console.log("bytesReceived:", report.bytesReceived);
  }

  if (report.type === "outbound-rtp") {
    // Chrome 141: this row exists even when packetsSent === 0
    console.log("packetsSent:", report.packetsSent);
    console.log("bytesSent:", report.bytesSent);
  }
});

see also