Skip to content

fix(recording): prevent double mic offset in studio editor after AV start gate trim#1972

Merged
richiemcilroy merged 1 commit into
CapSoftware:mainfrom
ManthanNimodiya:fix/mic-av-sync-double-count
Jul 2, 2026
Merged

fix(recording): prevent double mic offset in studio editor after AV start gate trim#1972
richiemcilroy merged 1 commit into
CapSoftware:mainfrom
ManthanNimodiya:fix/mic-av-sync-double-count

Conversation

@ManthanNimodiya

@ManthanNimodiya ManthanNimodiya commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

When mic audio arrives before the first video frame, apply_video_start_gate trims the leading samples to align audio with video start.
The trimmed AudioFrame kept the original pre-trim timestamp, so mic_start_time in metadata reflected the buffer start rather than the first committed sample.

The editor computes mic_offset = latest_start - mic_start_time and seeks that many seconds into the audio file. With the wrong timestamp, mic_offset was inflated by the trim duration, causing the editor to skip that same gap a second time.
Result: mic audio starts late by the trim amount (typically 20–150ms) in studio recording exports.

Fix: advance frame.timestamp by trim_duration in the UseFrame path so first_timestamp (and therefore mic_start_time) reflects the actual first committed sample. mic_offset then computes to ~0 and the editor plays from the start of the already-trimmed file.

Only affects studio mode MultipleSegments recordings opened in the editor. Instant recordings are unaffected (audio/video are muxed at capture time, editor offset calculation is not involved).

Greptile Summary

This PR fixes a double-offset bug in studio mode recordings where mic audio was delayed in the editor by the same duration that apply_video_start_gate had already trimmed. The root cause was that the trimmed AudioFrame kept the original pre-trim timestamp, so mic_start_time in the recording metadata reflected the buffer start rather than the first committed sample.

  • Production fix: frame.timestamp + trim_duration is now passed to AudioFrame::new in the UseFrame path, so first_timestamp (and therefore mic_start_time) correctly reflects the first kept sample.
  • Test coverage: A new assertion in the existing apply_gate_audio_leads_trims_front test verifies that the returned frame's timestamp equals start + trim_duration, directly exercising the corrected behaviour.

Confidence Score: 5/5

Safe to merge — the change is isolated to the UseFrame branch of apply_video_start_gate and has no impact on Passthrough or DropFrame paths or on instant recordings.

The arithmetic is correct: trim_samples is already guarded against zero (sample_rate == 0 returns Passthrough before this code runs), the multiplication trim_samples * 1_000_000_000 fits comfortably in u64 given the 500 ms alignment limit, and the small rounding residual (~1 sample period at most) is negligible. The fix correctly maps back to wall-clock time after the integer sample-count round-trip. The new test assertion directly validates the changed behaviour.

No files require special attention.

Important Files Changed

Filename Overview
crates/recording/src/output_pipeline/core.rs Advances AudioFrame.timestamp by trim_duration in the UseFrame path of apply_video_start_gate, plus a new test assertion verifying the corrected timestamp value.

Reviews (1): Last reviewed commit: "fix(recording): advance gate frame times..." | Re-trigger Greptile

Comment on lines +204 to +206
let trim_duration = Duration::from_nanos(
trim_samples as u64 * 1_000_000_000 / sample_rate as u64,
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The trim_samples * 1_000_000_000 / sample_rate math is safe here given current buffer sizes, but it’s a bit brittle if this ever changes. Using u128 for the intermediate avoids accidental overflow and makes intent clearer.

Suggested change
let trim_duration = Duration::from_nanos(
trim_samples as u64 * 1_000_000_000 / sample_rate as u64,
);
let trim_duration_ns =
(trim_samples as u128) * 1_000_000_000u128 / sample_rate as u128;
let trim_duration = Duration::from_nanos(trim_duration_ns as u64);

@richiemcilroy richiemcilroy merged commit b0d7abb into CapSoftware:main Jul 2, 2026
10 of 15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants