From bfbcbbc328d0f74ef694c890e544075110d36d07 Mon Sep 17 00:00:00 2001 From: ManthanNimodiya Date: Thu, 2 Jul 2026 09:05:17 +0530 Subject: [PATCH] fix(recording): advance gate frame timestamp past trimmed samples to prevent double-count in editor mic offset --- crates/recording/src/output_pipeline/core.rs | 25 +++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/crates/recording/src/output_pipeline/core.rs b/crates/recording/src/output_pipeline/core.rs index 28a8850666..44a209086c 100644 --- a/crates/recording/src/output_pipeline/core.rs +++ b/crates/recording/src/output_pipeline/core.rs @@ -187,7 +187,15 @@ pub(crate) async fn apply_video_start_gate( offset_ns = offset_ns as i64, "Trimmed leading audio samples for encoder-pair alignment" ); - VideoStartGateAction::UseFrame(AudioFrame::new(trimmed, frame.timestamp)) + // Advance the timestamp to the first *committed* sample so that + // first_timestamp (and therefore mic_start_time in metadata) reflects + // the actual capture time after the trim, not the pre-trim buffer start. + // Without this, the editor's mic_offset = display_start - mic_start equals + // trim_duration and causes it to skip that same duration a second time. + let trim_duration = Duration::from_nanos( + trim_samples as u64 * 1_000_000_000 / sample_rate as u64, + ); + VideoStartGateAction::UseFrame(AudioFrame::new(trimmed, frame.timestamp + trim_duration)) } None => { warn!( @@ -4492,6 +4500,21 @@ mod tests { let expected_trim = ns_to_sample_count(video_start_ns, info.sample_rate) as usize; assert_eq!(new_frame.inner.samples(), 2048 - expected_trim); + // Timestamp must be advanced by the trim so that mic_start_time in + // metadata reflects the first committed sample, preventing the editor + // from double-counting the same gap as a skip offset. + let trim_duration = Duration::from_nanos( + expected_trim as u64 * 1_000_000_000 / info.sample_rate as u64, + ); + let expected_ts = Timestamp::Instant(start + trim_duration); + assert_eq!( + new_frame + .timestamp + .signed_duration_since_secs(master_clock.timestamps()), + expected_ts + .signed_duration_since_secs(master_clock.timestamps()), + "gate UseFrame timestamp must be advanced past the trimmed samples" + ); } other => panic!("expected UseFrame when audio leads, got {other:?}"), }