Skip to content

Commit fbeda95

Browse files
feat: rework MissingStreamDataDetector to check last 3 received stats
1 parent 8f2084b commit fbeda95

File tree

1 file changed

+47
-39
lines changed

1 file changed

+47
-39
lines changed

src/detectors/MissingStreamDataDetector.ts

Lines changed: 47 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,20 @@ import {
99
import BaseIssueDetector from './BaseIssueDetector';
1010

1111
interface MissingStreamDetectorParams {
12-
timeoutMs?: number;
12+
steps?: number; // number of last stats to check
1313
}
1414

1515
export default class MissingStreamDataDetector extends BaseIssueDetector {
1616
readonly #lastMarkedAt = new Map<string, number>();
1717

1818
readonly #timeoutMs: number;
1919

20+
readonly #steps: number;
21+
2022
constructor(params: MissingStreamDetectorParams = {}) {
2123
super();
22-
this.#timeoutMs = params.timeoutMs ?? 10_000;
24+
this.#timeoutMs = 5_000;
25+
this.#steps = params.steps ?? 3;
2326
}
2427

2528
performDetection(data: WebRTCStatsParsed): IssueDetectorResult {
@@ -32,31 +35,27 @@ export default class MissingStreamDataDetector extends BaseIssueDetector {
3235
private processData(data: WebRTCStatsParsed): IssueDetectorResult {
3336
const issues: IssueDetectorResult = [];
3437

35-
const prevData = this.getLastProcessedStats(data.connection.id);
38+
const allLastProcessedStats = [...this.getAllLastProcessedStats(data.connection.id), data];
39+
if (allLastProcessedStats.length < this.#steps) {
40+
return issues;
41+
}
3642

37-
const { video: { inbound: newVideoInbound } } = data;
38-
const { audio: { inbound: newAudioInbound } } = data;
39-
const prevVideoInbound = prevData?.video.inbound;
40-
const prevAudioInbound = prevData?.audio.inbound;
43+
const lastThreeProcessedStats = allLastProcessedStats.slice(-this.#steps);
4144

45+
const lastThreeVideoInbound = lastThreeProcessedStats.map((stats) => stats.video.inbound);
46+
const lastThreeAudioInbound = lastThreeProcessedStats.map((stats) => stats.audio.inbound);
4247

43-
if (prevAudioInbound) {
44-
issues.push(...this.detectMissingData(
45-
newAudioInbound as unknown as CommonParsedInboundStreamStats[],
46-
prevAudioInbound as unknown as CommonParsedInboundStreamStats[],
47-
IssueType.Stream,
48-
IssueReason.MissingAudioStreamData,
49-
));
50-
}
48+
issues.push(...this.detectMissingData(
49+
lastThreeAudioInbound as unknown as CommonParsedInboundStreamStats[][],
50+
IssueType.Stream,
51+
IssueReason.MissingAudioStreamData,
52+
));
5153

52-
if (prevVideoInbound) {
53-
issues.push(...this.detectMissingData(
54-
newVideoInbound,
55-
prevVideoInbound,
56-
IssueType.Stream,
57-
IssueReason.MissingVideoStreamData,
58-
));
59-
}
54+
issues.push(...this.detectMissingData(
55+
lastThreeVideoInbound,
56+
IssueType.Stream,
57+
IssueReason.MissingVideoStreamData,
58+
));
6059

6160
const unvisitedTrackIds = new Set(this.#lastMarkedAt.keys());
6261

@@ -71,43 +70,47 @@ export default class MissingStreamDataDetector extends BaseIssueDetector {
7170
}
7271

7372
private detectMissingData(
74-
currentCommonInboundStats: CommonParsedInboundStreamStats[],
75-
previousCommonInboundStats: CommonParsedInboundStreamStats[],
76-
// commonStreamStats: CommonParsedInboundStreamStats[],
73+
lastThreeInboundStats: CommonParsedInboundStreamStats[][],
7774
type: IssueType,
7875
reason: IssueReason,
7976
): IssueDetectorResult {
8077
const issues: IssuePayload[] = [];
8178

82-
const mapStatsByTrackId = (items: CommonParsedInboundStreamStats[]) => new Map<string, CommonParsedInboundStreamStats>(
83-
items.map((item) => [item.track.trackIdentifier, item] as const),
84-
);
79+
const firstInboundStats = lastThreeInboundStats[0];
80+
const secondInboundStats = lastThreeInboundStats[1];
81+
const currentInboundStats = lastThreeInboundStats[2];
8582

86-
const prevInboundItemsByTrackId = mapStatsByTrackId(previousCommonInboundStats);
83+
const firstInboundItemsByTrackId = MissingStreamDataDetector.mapStatsByTrackId(firstInboundStats);
84+
const secondInboundItemsByTrackId = MissingStreamDataDetector.mapStatsByTrackId(secondInboundStats);
8785

88-
currentCommonInboundStats.forEach((inboundItem) => {
86+
currentInboundStats.forEach((inboundItem) => {
8987
const trackId = inboundItem.track.trackIdentifier;
9088

91-
const prevInboundItem = prevInboundItemsByTrackId.get(trackId);
92-
if (!prevInboundItem) {
89+
const firstInboundItem = firstInboundItemsByTrackId.get(trackId);
90+
const secondInboundItem = secondInboundItemsByTrackId.get(trackId);
91+
if (!firstInboundItem || !secondInboundItem) {
9392
return;
9493
}
9594

96-
const bytesReceivedDelta = inboundItem.bytesReceived - prevInboundItem.bytesReceived;
97-
95+
if (inboundItem.track.detached || inboundItem.track.ended) {
96+
return;
97+
}
9898

99-
if (bytesReceivedDelta === 0 && !inboundItem.track.detached && !inboundItem.track.ended) {
99+
if (
100+
firstInboundItem.bytesReceived === secondInboundItem.bytesReceived
101+
&& secondInboundItem.bytesReceived === inboundItem.bytesReceived
102+
) {
100103
const hasIssue = this.markIssue(trackId);
101104

102105
if (!hasIssue) {
103106
return;
104107
}
105108

106109
const statsSample = {
107-
bytesReceivedDelta,
110+
bytesReceivedDelta: 0,
108111
bytesReceived: inboundItem.bytesReceived,
109-
trackDetached: inboundItem.track.detached,
110-
trackEnded: inboundItem.track.ended,
112+
trackDetached: Boolean(inboundItem.track.detached),
113+
trackEnded: Boolean(inboundItem.track.ended),
111114
};
112115

113116
issues.push({
@@ -124,6 +127,11 @@ export default class MissingStreamDataDetector extends BaseIssueDetector {
124127
return issues;
125128
}
126129

130+
private static mapStatsByTrackId(items: CommonParsedInboundStreamStats[]) {
131+
return new Map<string, CommonParsedInboundStreamStats>(items
132+
.map((item) => [item.track.trackIdentifier, item] as const));
133+
}
134+
127135
private markIssue(trackId: string): boolean {
128136
const now = Date.now();
129137
const lastMarkedAt = this.#lastMarkedAt.get(trackId);

0 commit comments

Comments
 (0)