Skip to main content

talw_timecode/
format.rs

1use crate::dropframe::frames_to_components;
2use crate::framerate::FrameRate;
3
4pub fn format_timecode(total_frames: i64, rate: FrameRate) -> [u8; 11] {
5    let (h, m, s, f) = frames_to_components(total_frames, rate);
6    let sep = if rate.is_drop_frame() { b';' } else { b':' };
7
8    [
9        b'0' + h / 10,
10        b'0' + h % 10,
11        b':',
12        b'0' + m / 10,
13        b'0' + m % 10,
14        b':',
15        b'0' + s / 10,
16        b'0' + s % 10,
17        sep,
18        b'0' + f / 10,
19        b'0' + f % 10,
20    ]
21}
22
23pub fn format_timecode_str(total_frames: i64, rate: FrameRate, buf: &mut [u8; 11]) {
24    *buf = format_timecode(total_frames, rate);
25}
26
27#[cfg(test)]
28mod tests {
29    use super::*;
30
31    fn fmt(frames: i64, rate: FrameRate) -> &'static str {
32        let bytes = format_timecode(frames, rate);
33        let s: &str = core::str::from_utf8(&bytes).unwrap();
34        // Leak for test convenience
35        Box::leak(s.to_string().into_boxed_str())
36    }
37
38    #[test]
39    fn zero() {
40        assert_eq!(fmt(0, FrameRate::Fps24), "00:00:00:00");
41    }
42
43    #[test]
44    fn one_hour() {
45        assert_eq!(fmt(86400, FrameRate::Fps24), "01:00:00:00");
46    }
47
48    #[test]
49    fn half_second_24fps() {
50        assert_eq!(fmt(12, FrameRate::Fps24), "00:00:00:12");
51    }
52
53    #[test]
54    fn drop_frame_uses_semicolon() {
55        let bytes = format_timecode(1800, FrameRate::Fps29_97Df);
56        assert_eq!(bytes[8], b';');
57    }
58
59    #[test]
60    fn non_drop_uses_colon() {
61        let bytes = format_timecode(0, FrameRate::Fps24);
62        assert_eq!(bytes[8], b':');
63    }
64}