Goal
Add a WebRTC diagnostics mode that exposes periodic RTCPeerConnection.getStats() snapshots and emits a new SDK event named call-metrics during active calls.
This should help developers understand what is happening inside a WebRTC audio call instead of only seeing high-level states like connected, disconnected, or failed.
Why this matters
WebRTC can fail or degrade for many reasons: bad network conditions, TURN fallback, packet loss, jitter, weak audio input, or candidate-pair changes. Browser APIs already expose this information through getStats(), but raw RTCStatsReport data is large and hard to interpret.
The SDK should provide a useful, developer-friendly metrics layer.
Functional requirements
- Add an opt-in diagnostics configuration option, for example:
- enable/disable diagnostics mode
- configure snapshot interval
- Periodically call
RTCPeerConnection.getStats() while a call is active.
- Emit a
call-metrics event with normalized, easy-to-consume data.
- Provide an on-demand method to fetch the latest stats snapshot for the active call.
- Stop diagnostics timers when the call ends or the peer connection is disposed.
- Avoid leaking raw unnecessary browser/network details unless they are useful for debugging.
Metrics to expose
At minimum, expose useful audio-call health data:
Network/media quality
- Estimated bitrate
- Round-trip time / RTT
- Packets sent and received
- Packets lost
- Packet loss ratio
- Jitter
- Available outgoing bitrate when available
Audio visibility
- Local audio level
- Remote audio level if available
- Whether local audio appears muted/silent
ICE/candidate-pair visibility
- Selected candidate pair id
- Local candidate type:
host, srflx, relay, etc.
- Remote candidate type:
host, srflx, relay, etc.
- Local/remote candidate address metadata when safe and useful
- ICE connection state
- Peer connection state
Suggested event shape
chat.on('call-metrics', (metrics) => {
console.log(metrics.bitrateKbps);
console.log(metrics.rttMs);
console.log(metrics.packetLossRatio);
});
Suggested normalized payload:
type CallMetrics = {
timestamp: number;
bitrateKbps?: number;
availableOutgoingBitrateKbps?: number;
rttMs?: number;
packetsLost?: number;
packetLossRatio?: number;
jitterMs?: number;
localAudioLevel?: number;
remoteAudioLevel?: number;
selectedCandidatePairId?: string;
localCandidateType?: string;
remoteCandidateType?: string;
iceConnectionState?: RTCIceConnectionState;
connectionState?: RTCPeerConnectionState;
};
Learning material
This issue is a hands-on way to learn how WebRTC reports call quality.
getStats() basics
RTCPeerConnection.getStats() returns an RTCStatsReport, which is a map of many different stats objects. Important types include:
inbound-rtp: media received from the remote peer
outbound-rtp: media sent to the remote peer
candidate-pair: ICE connectivity pair stats
local-candidate: local ICE candidate details
remote-candidate: remote ICE candidate details
transport: transport-level stats and selected candidate-pair reference
media-source / track: audio level and media-source details, depending on browser support
Bitrate
Bitrate is usually calculated by comparing byte counters across two snapshots:
- read
bytesSent / bytesReceived
- store previous values and timestamp
- calculate delta bytes over delta time
- convert bytes to bits and seconds to kilobits per second
This teaches that many WebRTC stats are counters, not instant values.
RTT
Round-trip time often comes from selected candidate-pair stats, commonly currentRoundTripTime. It is usually reported in seconds, so the SDK should convert it to milliseconds for easier use.
Packet loss
Packet loss can be estimated from inbound RTP stats:
packetsLost
packetsReceived
packetLossRatio = packetsLost / (packetsLost + packetsReceived)
This teaches how loss impacts real-time media quality.
Jitter
Jitter measures packet timing variation. High jitter can cause choppy or robotic audio even when packet loss is low.
Audio level
Audio level can help answer questions like:
- Is the microphone producing input?
- Is the user muted?
- Is remote audio arriving but not audible?
Browser support varies, so the SDK should treat audio level fields as optional.
Candidate-pair behavior
The selected candidate pair explains the network path used by the call:
host means direct local/private candidate
srflx means server-reflexive candidate discovered through STUN
relay means TURN relay is being used
This helps developers understand whether the call is peer-to-peer or TURN-dependent.
Implementation notes
- Keep the raw WebRTC API hidden behind SDK-friendly types.
- Do not emit enormous raw
RTCStatsReport objects by default.
- Make diagnostics opt-in to avoid unnecessary CPU/battery usage.
- Ensure timers are cleaned up on call end.
- Handle browser differences gracefully: not all stats exist in every browser.
- Avoid crashing if
getStats() fails or returns incomplete data.
Testing ideas
- Mock
RTCPeerConnection.getStats() and verify normalized metrics.
- Verify periodic metrics are emitted only when diagnostics mode is enabled.
- Verify diagnostics stop after call end/dispose.
- Verify bitrate calculation across two snapshots.
- Verify missing optional stats do not throw.
- Verify selected candidate-pair information is included when available.
Acceptance criteria
Goal
Add a WebRTC diagnostics mode that exposes periodic
RTCPeerConnection.getStats()snapshots and emits a new SDK event namedcall-metricsduring active calls.This should help developers understand what is happening inside a WebRTC audio call instead of only seeing high-level states like
connected,disconnected, orfailed.Why this matters
WebRTC can fail or degrade for many reasons: bad network conditions, TURN fallback, packet loss, jitter, weak audio input, or candidate-pair changes. Browser APIs already expose this information through
getStats(), but rawRTCStatsReportdata is large and hard to interpret.The SDK should provide a useful, developer-friendly metrics layer.
Functional requirements
RTCPeerConnection.getStats()while a call is active.call-metricsevent with normalized, easy-to-consume data.Metrics to expose
At minimum, expose useful audio-call health data:
Network/media quality
Audio visibility
ICE/candidate-pair visibility
host,srflx,relay, etc.host,srflx,relay, etc.Suggested event shape
Suggested normalized payload:
Learning material
This issue is a hands-on way to learn how WebRTC reports call quality.
getStats()basicsRTCPeerConnection.getStats()returns anRTCStatsReport, which is a map of many different stats objects. Important types include:inbound-rtp: media received from the remote peeroutbound-rtp: media sent to the remote peercandidate-pair: ICE connectivity pair statslocal-candidate: local ICE candidate detailsremote-candidate: remote ICE candidate detailstransport: transport-level stats and selected candidate-pair referencemedia-source/track: audio level and media-source details, depending on browser supportBitrate
Bitrate is usually calculated by comparing byte counters across two snapshots:
bytesSent/bytesReceivedThis teaches that many WebRTC stats are counters, not instant values.
RTT
Round-trip time often comes from selected candidate-pair stats, commonly
currentRoundTripTime. It is usually reported in seconds, so the SDK should convert it to milliseconds for easier use.Packet loss
Packet loss can be estimated from inbound RTP stats:
packetsLostpacketsReceivedpacketLossRatio = packetsLost / (packetsLost + packetsReceived)This teaches how loss impacts real-time media quality.
Jitter
Jitter measures packet timing variation. High jitter can cause choppy or robotic audio even when packet loss is low.
Audio level
Audio level can help answer questions like:
Browser support varies, so the SDK should treat audio level fields as optional.
Candidate-pair behavior
The selected candidate pair explains the network path used by the call:
hostmeans direct local/private candidatesrflxmeans server-reflexive candidate discovered through STUNrelaymeans TURN relay is being usedThis helps developers understand whether the call is peer-to-peer or TURN-dependent.
Implementation notes
RTCStatsReportobjects by default.getStats()fails or returns incomplete data.Testing ideas
RTCPeerConnection.getStats()and verify normalized metrics.Acceptance criteria
call-metricsperiodically.