@@ -40,6 +40,13 @@ interface RTCReceiverStats extends RTCStats {
4040 // framesPerSecond?: number;
4141}
4242
43+ // https://developer.mozilla.org/en-US/docs/Web/API/RTCRemoteInboundRtpStreamStats
44+ interface RTCRemoteInboundRtpStreamStats extends RTCStats {
45+ type : "remote-inbound-rtp" ;
46+ kind ?: "audio" | "video" ;
47+ roundTripTime : number ;
48+ }
49+
4350interface RTCIceCandidatePairStats extends RTCStats {
4451 type : "candidate-pair" ;
4552 state ?:
@@ -73,12 +80,13 @@ export interface QualityStats {
7380export function calculateQualityScore (
7481 stats : RTCStatsReport ,
7582) : QualityStats | null {
83+ const statsIter = stats . values ( ) as MapIterator < RTCStats > ;
7684 let audioReceiverStats : RTCReceiverStats | undefined ;
7785 let videoReceiverStats : RTCReceiverStats | undefined ;
78- let activeCandidatePairStats : RTCIceCandidatePairStats | undefined ;
86+ let rtt : number | undefined ;
7987 let peerConnectionStats : RTCPeerConnectionStats | undefined ;
8088
81- for ( const stat of stats . values ( ) ) {
89+ for ( const stat of statsIter ) {
8290 if ( stat . type === "inbound-rtp" || stat . type === "rtp-receiver" ) {
8391 // https://developer.mozilla.org/en-US/docs/Web/API/RTCInboundRtpStreamStats
8492 const receiverStat = stat as RTCReceiverStats ;
@@ -90,11 +98,20 @@ export function calculateQualityScore(
9098 } else if ( stat . type === "candidate-pair" ) {
9199 // https://developer.mozilla.org/en-US/docs/Web/API/RTCIceCandidatePairStats
92100 const candidatePairStat = stat as RTCIceCandidatePairStats ;
93- if ( candidatePairStat . state === "succeeded" ) {
94- activeCandidatePairStats = candidatePairStat ;
101+ // prioritize remote-inbound-rtp rtt
102+ if ( candidatePairStat . state === "succeeded" && ! rtt ) {
103+ rtt = candidatePairStat . currentRoundTripTime ;
95104 }
96105 } else if ( stat . type === "peer-connection" ) {
97106 peerConnectionStats = stat as RTCPeerConnectionStats ;
107+ } else if ( stat . type === "remote-inbound-rtp" ) {
108+ // There are 2 cases when we need this:
109+ // 1. Fallback for Firefox's missing ICE RTT support
110+ // 2. Faster RTT calculation as it is used in RTP streams
111+ //
112+ // If there's only data channel without media streams, Firefox won't generate RTT.
113+ const remoteInboundStat = stat as RTCRemoteInboundRtpStreamStats ;
114+ rtt = remoteInboundStat . roundTripTime ;
98115 }
99116 }
100117
@@ -144,13 +161,10 @@ export function calculateQualityScore(
144161 }
145162
146163 let latencyScore = 80 ;
147- if ( activeCandidatePairStats ) {
148- const rtt = activeCandidatePairStats . currentRoundTripTime ;
149- if ( rtt !== undefined ) {
150- // second to microseconds
151- quality . rttUs = BigInt ( rtt * 1e6 ) ;
152- latencyScore = rtt < 0.1 ? 100 : ( rtt < 0.3 ? 80 : ( rtt < 0.5 ? 60 : 40 ) ) ;
153- }
164+ if ( ! ! rtt ) {
165+ // second to microseconds
166+ quality . rttUs = BigInt ( Math . trunc ( rtt * 1e6 ) ) ;
167+ latencyScore = rtt < 0.1 ? 100 : ( rtt < 0.3 ? 80 : ( rtt < 0.5 ? 60 : 40 ) ) ;
154168 }
155169
156170 const overallQuality = Math . round (
0 commit comments