Title here
Summary here
Client Monitor · 4.6.0
· balazskreith
Browser stats normalization, Firefox transport fixes, and issue lifecycle reporting in samples.
View release on GitHub ↗getStats() output as close to the W3C webrtc-stats spec as possible before monitors consume it. Adapters do three things and no more: fold a value into the standard field it provably belongs to (a renamed member, a legacy report carrying the same measurement), infer references — the *Id fields wiring one report to another, which a browser may omit but the report graph determines — and map legacy enum spellings onto the values the monitors accept. Measured values are never invented: an approximated number is indistinguishable downstream from one the browser reported, so a field a browser omits stays omitted, and an ambiguous reference is left unset rather than guessed. Nothing is discarded either, except a value that survives elsewhere: members the spec dropped but a browser still fills (candidate-pair.priority, Chromium’s contentType, Firefox’s selected) are left on the stat, since the monitors carry through whatever they receive. Every fix feature-detects from the report itself (no version parsing), so an adapter over an already-conformant report is a no-op. All deviations were verified against engine sources (Blink/libwebrtc, WebKit release branches, Firefox release tags) and MDN compat data.ChromeStatsAdapter (Chrome/Edge/Opera): folds the legacy mediaType alias into kind, the legacy ip into address on ICE candidates, and the deprecated track/stream reports (≤ M111) into inbound-rtp. Reference inference runs too, as a safety net for older versions and relayed stats.SafariStatsAdapter: folds deprecated track reports (≤ 16.x) into inbound-rtp — critically recovering trackIdentifier, absent before Safari 16.4, without which the monitor cannot bind a stream to its MediaStreamTrack — and datachannelid into dataChannelIdentifier (≤ 17.6); maps legacy candidate-pair.state spellings (inprogress → in-progress, cancelled → failed); infers codec.transportId, spec-required but unfilled through 17.3, and inbound-rtp.remoteId, which WebKit dropped in 16.4 through 16.6.FirefoxStatsAdapter (replaces both Firefox94StatsAdapter and FirefoxTransportStatsAdapter, which are merged into it — Firefox now needs exactly one adapter, with no registration-order constraint between two): folds mediaType into kind and the non-standard discardedPackets alias into packetsDiscarded; maps candidate-pair.state: 'cancelled' → 'failed'; reconstructs the transport report Firefox ships none of before 153 from the selected candidate pair, as before. Infers outbound-rtp.mediaSourceId — never emitted by Firefox, and the reference through which a sent stream reaches its source and its MediaStreamTrack, so outbound track monitoring works on Firefox again; resolved by kind when a single source of that kind exists (simulcast encodings all resolve to it), left unset when several sources make it ambiguous. Also infers transportId on RTP, codec and ICE reports, absent before Firefox 153, resolved against the transport report native or reconstructed. Brace-wrapped {uuid} track identifiers are intentionally preserved, since they match Firefox’s MediaStreamTrack.id format exactly.remoteId/localId cross-references between the local and remote view of a stream are resolved by SSRC (the synchronization source is the stream’s identity), and codecId by codec kind, transport and — where a browser tags codec entries with a direction — encode/decode.transport report listed after the selected candidate pair was ignored, and a second, reconstructed transport was appended next to it. The scan for an existing transport stopped at the first selected pair, and getStats() does not guarantee report order, so on Firefox 153+ this could produce two transports for one connection — which in turn made the transport reference ambiguous. The whole report is now scanned before deciding.inbound-rtp.framesRendered (no engine emits it), remote-inbound-rtp.packetsReceived (Chromium/WebKit), Firefox’s qualityLimitation* family and audio media-source levels, Safari’s media-playout reports and nulled host-candidate address, and Chromium’s requestsSent accounting (later checks land in consentRequestsSent).StatsAdapter interface + StatsAdapters registry) are now exported, so applications can reuse or extend them.sendResolvedIssuesToServer, default true): the sample schema’s ClientIssue gained an optional key field, and with the flag on both entries of a stateful issue carry it — the raise entry as before plus a companion <issueType>-resolved entry whose payload holds raisedAt (equal to the raise entry’s timestamp, a secondary join), the resolve comment, and — flattened in — only a payload explicitly passed to the resolution (the built-in detectors pass their final payload, so durationInMs appears; the raise-time payload is not repeated, since the server already has it from the raise entry). The purpose is server-side, on-the-fly tracking of each client’s currently active issues — open on the raise entry, close on the matching key — enabling cross-client correlation and immediate actions without waiting for post-hoc analysis. Issues still active at close() are auto-resolved into the final sample. With the flag off, the wire format is identical to previous releases (raise entries only, no key); the realtime 'issue-resolved' event is unaffected either way. Servers switching on issue type should ignore or handle the -resolved suffix.Source: GitHub release 4.6.0. Published at 2026-08-16T13:06:37Z (UTC).