@@ -9,17 +9,20 @@ import {
99import BaseIssueDetector from './BaseIssueDetector' ;
1010
1111interface MissingStreamDetectorParams {
12- timeoutMs ?: number ;
12+ steps ?: number ; // number of last stats to check
1313}
1414
1515export 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