Skip to content

Commit f39a4ec

Browse files
committed
Improve audio and video timestamp resync logic
1 parent ad0fb8d commit f39a4ec

File tree

1 file changed

+45
-13
lines changed
  • crates/recording/src/output_pipeline

1 file changed

+45
-13
lines changed

crates/recording/src/output_pipeline/core.rs

Lines changed: 45 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -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

3840
const AUDIO_WALL_CLOCK_TOLERANCE_SECS: f64 = 0.1;
3941
const VIDEO_WALL_CLOCK_TOLERANCE_SECS: f64 = 0.1;
42+
const AUDIO_LARGE_FORWARD_JUMP_SECS: f64 = 5.0;
4043

4144
impl 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

Comments
 (0)