Skip to content

Commit 97f992d

Browse files
committed
add rtt_us and use only baseline available stats
1 parent 1009d2c commit 97f992d

File tree

5 files changed

+49
-34
lines changed

5 files changed

+49
-34
lines changed

peer/proto

peer/src/google/protobuf/timestamp.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// @ts-nocheck
22
// @ts-nocheck
33
// @ts-nocheck
4+
// @ts-nocheck
45
// @generated by protobuf-ts 2.9.4 with parameter client_generic
56
// @generated from protobuf file "google/protobuf/timestamp.proto" (package "google.protobuf", syntax proto3)
67
// tslint:disable

peer/src/peer.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,9 @@ export class Peer {
334334

335335
const at = Date.now() * 1_000;
336336
const quality = calculateQualityScore(rawStats);
337+
if (!quality) {
338+
continue;
339+
}
337340

338341
events.push({
339342
timestampUs: BigInt(at),
@@ -342,7 +345,8 @@ export class Peer {
342345
dst: sess.other,
343346
},
344347
metrics: {
345-
qualityScore: BigInt(quality),
348+
qualityScore: quality.qualityScore,
349+
rttUs: quality.rtt,
346350
},
347351
});
348352
}

peer/src/quality.ts

Lines changed: 34 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -24,31 +24,31 @@ type RTCStatsType =
2424
| "track"
2525
| "transport";
2626

27+
// https://developer.mozilla.org/en-US/docs/Web/API/RTCInboundRtpStreamStats
2728
interface RTCReceiverStats extends RTCStats {
2829
type: "inbound-rtp" | "rtp-receiver" | "remote-outbound-rtp";
2930
kind?: "audio" | "video";
3031
bytesReceived?: number;
3132
packetsReceived?: number;
3233
packetsLost?: number;
3334
jitter?: number;
34-
framesDecoded?: number;
35-
framesDropped?: number;
36-
framesPerSecond?: number;
3735
totalSamplesReceived?: number;
36+
37+
// unsupported in Safari
38+
// framesDecoded?: number;
39+
// framesDropped?: number;
40+
// framesPerSecond?: number;
3841
}
3942

4043
interface RTCIceCandidatePairStats extends RTCStats {
4144
type: "candidate-pair";
4245
state?:
43-
| "new"
44-
| "checking"
45-
| "connected"
46-
| "completed"
4746
| "failed"
48-
| "disconnected";
49-
roundTripTime?: number;
50-
localCandidateId?: string;
51-
remoteCandidateId?: string;
47+
| "frozen"
48+
| "in-progress"
49+
| "succeeded"
50+
| "waiting";
51+
currentRoundTripTime?: number;
5252
}
5353

5454
interface RTCPeerConnectionStats extends RTCStats {
@@ -64,23 +64,33 @@ interface RTCPeerConnectionStats extends RTCStats {
6464
iceGatheringState?: "new" | "gathering" | "complete";
6565
}
6666

67-
export function calculateQualityScore(stats: RTCStatsReport): number {
67+
export interface QualityStats {
68+
qualityScore?: bigint;
69+
// rtt in microseconds
70+
rttUs?: bigint;
71+
}
72+
73+
export function calculateQualityScore(
74+
stats: RTCStatsReport,
75+
): QualityStats | null {
6876
let audioReceiverStats: RTCReceiverStats | undefined;
6977
let videoReceiverStats: RTCReceiverStats | undefined;
7078
let activeCandidatePairStats: RTCIceCandidatePairStats | undefined;
7179
let peerConnectionStats: RTCPeerConnectionStats | undefined;
7280

7381
for (const stat of stats.values()) {
7482
if (stat.type === "inbound-rtp" || stat.type === "rtp-receiver") {
83+
// https://developer.mozilla.org/en-US/docs/Web/API/RTCInboundRtpStreamStats
7584
const receiverStat = stat as RTCReceiverStats;
7685
if (receiverStat.kind === "audio") {
7786
audioReceiverStats = receiverStat;
7887
} else {
7988
videoReceiverStats = receiverStat;
8089
}
8190
} else if (stat.type === "candidate-pair") {
91+
// https://developer.mozilla.org/en-US/docs/Web/API/RTCIceCandidatePairStats
8292
const candidatePairStat = stat as RTCIceCandidatePairStats;
83-
if (candidatePairStat.state === "connected") {
93+
if (candidatePairStat.state === "succeeded") {
8494
activeCandidatePairStats = candidatePairStat;
8595
}
8696
} else if (stat.type === "peer-connection") {
@@ -92,9 +102,11 @@ export function calculateQualityScore(stats: RTCStatsReport): number {
92102
peerConnectionStats && peerConnectionStats.iceConnectionState &&
93103
!["connected", "completed"].includes(peerConnectionStats.iceConnectionState)
94104
) {
95-
return 15; // Low score for connection issues
105+
return null;
96106
}
97107

108+
const quality: QualityStats = {};
109+
98110
let audioQuality = 100;
99111
if (audioReceiverStats) {
100112
const packetsLost = audioReceiverStats.packetsLost ?? 0;
@@ -128,31 +140,24 @@ export function calculateQualityScore(stats: RTCStatsReport): number {
128140
const videoJitterScore = videoJitter < 0.02
129141
? 100
130142
: (videoJitter < 0.1 ? 70 : 30);
131-
const framesDropped = videoReceiverStats.framesDropped ?? 0;
132-
const framesDecoded = videoReceiverStats.framesDecoded ?? 0;
133-
const frameDropRatio = framesDecoded > 0
134-
? framesDropped / framesDecoded
135-
: 0;
136-
const videoFrameDropScore = Math.max(0, 100 - (frameDropRatio * 100));
137-
const videoFps = videoReceiverStats.framesPerSecond;
138-
const videoFpsScore = videoFps !== undefined
139-
? Math.min(100, (videoFps / 15) * (100 / 1))
140-
: 100;
141-
videoQuality =
142-
(videoPacketLossScore + videoJitterScore + videoFrameDropScore +
143-
videoFpsScore) / 4;
143+
videoQuality = (videoPacketLossScore + videoJitterScore) / 2;
144144
}
145145

146146
let latencyScore = 80;
147147
if (activeCandidatePairStats) {
148-
const rtt = activeCandidatePairStats.roundTripTime;
148+
const rtt = activeCandidatePairStats.currentRoundTripTime;
149149
if (rtt !== undefined) {
150+
// second to microseconds
151+
quality.rttUs = BigInt(rtt * 1e6);
150152
latencyScore = rtt < 0.1 ? 100 : (rtt < 0.3 ? 80 : (rtt < 0.5 ? 60 : 40));
151153
}
152154
}
153155

154156
const overallQuality = Math.round(
155157
(videoQuality * 0.5) + (audioQuality * 0.3) + (latencyScore * 0.2),
156158
);
157-
return Math.max(0, Math.min(100, overallQuality));
159+
const qualityScore = Math.max(0, Math.min(100, overallQuality));
160+
quality.qualityScore = BigInt(qualityScore);
161+
162+
return quality;
158163
}

peer/src/signaling.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -364,9 +364,13 @@ export interface AnalyticsMetrics {
364364
* - 20-39: poor
365365
* - 0-19: bad
366366
*
367-
* @generated from protobuf field: sint64 quality_score = 1;
367+
* @generated from protobuf field: optional sint64 quality_score = 1;
368368
*/
369-
qualityScore: bigint;
369+
qualityScore?: bigint;
370+
/**
371+
* @generated from protobuf field: optional sint64 rtt_us = 2;
372+
*/
373+
rttUs?: bigint;
370374
}
371375
/**
372376
* @generated from protobuf message pulsebeam.v1.AnalyticsReportResp
@@ -714,7 +718,8 @@ export const AnalyticsTags = new AnalyticsTags$Type();
714718
class AnalyticsMetrics$Type extends MessageType<AnalyticsMetrics> {
715719
constructor() {
716720
super("pulsebeam.v1.AnalyticsMetrics", [
717-
{ no: 1, name: "quality_score", kind: "scalar", T: 18 /*ScalarType.SINT64*/, L: 0 /*LongType.BIGINT*/ }
721+
{ no: 1, name: "quality_score", kind: "scalar", opt: true, T: 18 /*ScalarType.SINT64*/, L: 0 /*LongType.BIGINT*/ },
722+
{ no: 2, name: "rtt_us", kind: "scalar", opt: true, T: 18 /*ScalarType.SINT64*/, L: 0 /*LongType.BIGINT*/ }
718723
]);
719724
}
720725
}

0 commit comments

Comments
 (0)