fix: Account for drop-frame LTC in time calculation

Co-authored-by: aider (gemini/gemini-2.5-pro) <aider@aider.chat>
This commit is contained in:
Chris Frankland-Wright 2025-08-03 15:37:25 +01:00
parent d13ffdc057
commit 22dc01e80f
4 changed files with 14 additions and 8 deletions

View file

@ -242,6 +242,7 @@ mod tests {
minutes: 2,
seconds: 3,
frames: 4,
is_drop_frame: false,
frame_rate: Ratio::new(25, 1),
timestamp: Utc::now(),
}),

View file

@ -32,7 +32,7 @@ pub fn start_serial_thread(
let reader = std::io::BufReader::new(port);
let re = Regex::new(
r"\[(LOCK|FREE)\]\s+(\d{2}):(\d{2}):(\d{2})[:;](\d{2})\s+\|\s+([\d.]+)fps",
r"\[(LOCK|FREE)\]\s+(\d{2}):(\d{2}):(\d{2})([:;])(\d{2})\s+\|\s+([\d.]+)fps",
)
.unwrap();
@ -60,11 +60,12 @@ mod tests {
use super::*;
use std::sync::mpsc;
use crate::sync_logic::LtcState;
use num_rational::Ratio;
use regex::Regex;
fn get_ltc_regex() -> Regex {
Regex::new(
r"\[(LOCK|FREE)\]\s+(\d{2}):(\d{2}):(\d{2})[:;](\d{2})\s+\|\s+([\d.]+)fps",
r"\[(LOCK|FREE)\]\s+(\d{2}):(\d{2}):(\d{2})([:;])(\d{2})\s+\|\s+([\d.]+)fps",
).unwrap()
}
@ -119,7 +120,7 @@ mod tests {
assert_eq!(st.free_count, 1);
let received_frame = rx.try_recv().unwrap();
assert_eq!(received_frame.status, "FREE");
assert_eq!(received_frame.frame_rate, 29.97);
assert_eq!(received_frame.frame_rate, Ratio::new(30000, 1001));
}
#[test]

View file

@ -24,6 +24,7 @@ pub struct LtcFrame {
pub minutes: u32,
pub seconds: u32,
pub frames: u32,
pub is_drop_frame: bool,
pub frame_rate: Ratio<i64>,
pub timestamp: DateTime<Utc>, // arrival stamp
}
@ -35,8 +36,9 @@ impl LtcFrame {
hours: caps[2].parse().ok()?,
minutes: caps[3].parse().ok()?,
seconds: caps[4].parse().ok()?,
frames: caps[5].parse().ok()?,
frame_rate: get_frame_rate_ratio(&caps[6])?,
is_drop_frame: &caps[5] == ";",
frames: caps[6].parse().ok()?,
frame_rate: get_frame_rate_ratio(&caps[7])?,
timestamp,
})
}
@ -205,6 +207,7 @@ mod tests {
minutes: m,
seconds: s,
frames: 0,
is_drop_frame: false,
frame_rate: Ratio::new(25, 1),
timestamp: Utc::now(),
}

View file

@ -49,10 +49,10 @@ pub fn calculate_target_time(frame: &LtcFrame, config: &Config) -> DateTime<Loca
let total_duration_secs =
Ratio::new(timecode_secs, 1) + Ratio::new(frame.frames as i64, 1) / frame.frame_rate;
// For fractional frame rates (23.98, 29.97), timecode runs slower than wall clock.
// For non-drop-frame fractional rates (23.98, 29.97), timecode runs slower than wall clock.
// We need to scale the timecode duration up to get wall clock time.
// The scaling factor is 1001/1000.
let scaled_duration_secs = if *frame.frame_rate.denom() == 1001 {
// The scaling factor is 1001/1000. For drop-frame, this isn't necessary.
let scaled_duration_secs = if *frame.frame_rate.denom() == 1001 && !frame.is_drop_frame {
total_duration_secs * Ratio::new(1001, 1000)
} else {
total_duration_secs
@ -196,6 +196,7 @@ mod tests {
minutes: m,
seconds: s,
frames: f,
is_drop_frame: false,
frame_rate: Ratio::new(25, 1),
timestamp: Utc::now(),
}