@@ -33,17 +33,22 @@ struct AudioDriftTracker {
3333 baseline_offset_secs : Option < f64 > ,
3434 drift_warning_logged : bool ,
3535 first_frame_timestamp_secs : Option < f64 > ,
36+ last_valid_wall_clock_secs : f64 ,
37+ resync_count : u32 ,
3638}
3739
3840const AUDIO_WALL_CLOCK_TOLERANCE_SECS : f64 = 0.1 ;
3941const VIDEO_WALL_CLOCK_TOLERANCE_SECS : f64 = 0.1 ;
42+ const AUDIO_LARGE_FORWARD_JUMP_SECS : f64 = 5.0 ;
4043
4144impl AudioDriftTracker {
4245 fn new ( ) -> Self {
4346 Self {
4447 baseline_offset_secs : None ,
4548 drift_warning_logged : false ,
4649 first_frame_timestamp_secs : None ,
50+ last_valid_wall_clock_secs : 0.0 ,
51+ resync_count : 0 ,
4752 }
4853 }
4954
@@ -65,6 +70,24 @@ impl AudioDriftTracker {
6570 return None ;
6671 }
6772
73+ if frame_elapsed_secs > wall_clock_secs + AUDIO_LARGE_FORWARD_JUMP_SECS {
74+ self . resync_count += 1 ;
75+ warn ! (
76+ frame_elapsed_secs,
77+ wall_clock_secs,
78+ forward_jump_secs = frame_elapsed_secs - wall_clock_secs,
79+ resync_count = self . resync_count,
80+ "Audio timestamp jumped forward (system sleep/wake?), resyncing to wall clock"
81+ ) ;
82+
83+ self . first_frame_timestamp_secs = Some ( frame_timestamp_secs - wall_clock_secs) ;
84+ self . baseline_offset_secs = None ;
85+ self . drift_warning_logged = false ;
86+
87+ self . last_valid_wall_clock_secs = wall_clock_secs;
88+ return Some ( Duration :: from_secs_f64 ( wall_clock_secs) ) ;
89+ }
90+
6891 if frame_elapsed_secs > wall_clock_secs + AUDIO_WALL_CLOCK_TOLERANCE_SECS {
6992 return None ;
7093 }
@@ -100,10 +123,12 @@ impl AudioDriftTracker {
100123 self . drift_warning_logged = true ;
101124 }
102125
126+ self . last_valid_wall_clock_secs = wall_clock_secs;
103127 let corrected_secs = adjusted_frame_elapsed * drift_ratio;
104128 return Some ( Duration :: from_secs_f64 ( corrected_secs. max ( 0.0 ) ) ) ;
105129 }
106130
131+ self . last_valid_wall_clock_secs = wall_clock_secs;
107132 Some ( Duration :: from_secs_f64 ( frame_elapsed_secs. max ( 0.0 ) ) )
108133 }
109134}
@@ -202,7 +227,7 @@ pub struct TimestampAnomalyTracker {
202227 total_forward_skew_secs : f64 ,
203228 max_forward_skew_secs : f64 ,
204229 last_valid_duration : Option < Duration > ,
205- accumulated_compensation : Duration ,
230+ accumulated_compensation_secs : f64 ,
206231 resync_count : u64 ,
207232}
208233
@@ -217,7 +242,7 @@ impl TimestampAnomalyTracker {
217242 total_forward_skew_secs : 0.0 ,
218243 max_forward_skew_secs : 0.0 ,
219244 last_valid_duration : None ,
220- accumulated_compensation : Duration :: ZERO ,
245+ accumulated_compensation_secs : 0.0 ,
221246 resync_count : 0 ,
222247 }
223248 }
@@ -233,8 +258,8 @@ impl TimestampAnomalyTracker {
233258 return self . handle_backward_timestamp ( signed_secs) ;
234259 }
235260
236- let raw_duration = Duration :: from_secs_f64 ( signed_secs) ;
237- let adjusted = raw_duration . saturating_add ( self . accumulated_compensation ) ;
261+ let adjusted_secs = ( signed_secs + self . accumulated_compensation_secs ) . max ( 0.0 ) ;
262+ let adjusted = Duration :: from_secs_f64 ( adjusted_secs ) ;
238263
239264 if let Some ( last) = self . last_valid_duration
240265 && let Some ( forward_jump) = adjusted. checked_sub ( last)
@@ -281,12 +306,11 @@ impl TimestampAnomalyTracker {
281306 backward_secs = skew_secs,
282307 consecutive = self . consecutive_anomalies,
283308 total_anomalies = self . anomaly_count,
309+ resync_count = self . resync_count,
284310 "Large backward timestamp jump detected (clock skew?), compensating"
285311 ) ;
286312
287- let compensation = Duration :: from_secs_f64 ( skew_secs) ;
288- self . accumulated_compensation =
289- self . accumulated_compensation . saturating_add ( compensation) ;
313+ self . accumulated_compensation_secs += skew_secs;
290314 self . resync_count += 1 ;
291315
292316 let adjusted = self . last_valid_duration . unwrap_or ( Duration :: ZERO ) ;
@@ -317,21 +341,29 @@ impl TimestampAnomalyTracker {
317341 self . max_forward_skew_secs = jump_secs;
318342 }
319343
344+ let expected_increment = Duration :: from_millis ( 33 ) ;
345+ let adjusted = last. saturating_add ( expected_increment) ;
346+
347+ let compensation_secs = current. as_secs_f64 ( ) - adjusted. as_secs_f64 ( ) ;
348+ self . accumulated_compensation_secs -= compensation_secs;
349+ self . resync_count += 1 ;
350+
320351 warn ! (
321352 stream = self . stream_name,
322353 forward_secs = jump_secs,
323354 last_valid_ms = last. as_millis( ) ,
324355 current_ms = current. as_millis( ) ,
325356 total_anomalies = self . anomaly_count,
326- "Large forward timestamp jump detected (system sleep/wake?), clamping"
357+ resync_count = self . resync_count,
358+ compensation_applied_secs = format!( "{:.3}" , compensation_secs) ,
359+ accumulated_compensation_secs = format!( "{:.3}" , self . accumulated_compensation_secs) ,
360+ "Large forward timestamp jump detected (system sleep/wake?), resyncing timeline"
327361 ) ;
328362
329- let expected_increment = Duration :: from_millis ( 33 ) ;
330- let clamped = last. saturating_add ( expected_increment) ;
331- self . last_valid_duration = Some ( clamped) ;
363+ self . last_valid_duration = Some ( adjusted) ;
332364 self . consecutive_anomalies = 0 ;
333365
334- Ok ( clamped )
366+ Ok ( adjusted )
335367 }
336368
337369 pub fn log_stats_if_notable ( & self ) {
@@ -347,7 +379,7 @@ impl TimestampAnomalyTracker {
347379 total_forward_skew_secs = format!( "{:.3}" , self . total_forward_skew_secs) ,
348380 max_forward_skew_secs = format!( "{:.3}" , self . max_forward_skew_secs) ,
349381 resync_count = self . resync_count,
350- accumulated_compensation_ms = self . accumulated_compensation . as_millis ( ) ,
382+ accumulated_compensation_secs = format! ( "{:.3}" , self . accumulated_compensation_secs ) ,
351383 "Timestamp anomaly statistics"
352384 ) ;
353385 }
0 commit comments