timecode_coder/
lib.rs

1#![cfg_attr(not(test), no_std)]
2extern crate core;
3
4use core::fmt::{Debug, Display, Formatter};
5
6pub mod ltc_frame;
7#[cfg(feature = "decode_ltc")]
8pub mod ltc_decoder;
9
10#[derive(PartialEq, Eq, Clone)]
11pub struct TimecodeFrame {
12    pub hours: u8,
13    pub minutes: u8,
14    pub seconds: u8,
15    pub frames: u8,
16    pub frames_per_second: FramesPerSecond,
17}
18
19impl TimecodeFrame {
20    pub fn add_frame(&mut self) {
21        self.frames += 1;
22        match self.frames_per_second {
23            FramesPerSecond::Unknown => {}
24            FramesPerSecond::TwentyFour => {
25                if self.frames >= 24 {
26                    self.frames = 0;
27                    self.seconds += 1;
28                }
29            }
30            FramesPerSecond::TwentyFive => {
31                if self.frames >= 25 {
32                    self.frames = 0;
33                    self.seconds += 1;
34                }
35            }
36            FramesPerSecond::Thirty => {
37                if self.frames >= 30 {
38                    self.frames = 0;
39                    self.seconds += 1;
40                }
41            }
42        }
43        if self.seconds > 59 {
44            self.seconds = 0;
45            self.minutes += 1;
46        }
47        if self.minutes > 59 {
48            self.minutes = 0;
49            self.hours += 1;
50        }
51    }
52}
53
54#[cfg(feature = "debug")]
55impl Display for TimecodeFrame {
56    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
57        write!(f, "{:02}:{:02}:{:02}:{:02} fps:{:#?}", self.hours, self.minutes, self.seconds, self.frames, self.frames_per_second)
58    }
59}
60
61#[cfg(feature = "debug")]
62impl Debug for TimecodeFrame {
63    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
64        Display::fmt(self, f)
65    }
66}
67
68impl TimecodeFrame {
69    pub fn new_from_duration(hours: u8, minutes: u8, seconds: u8, frames: u8, duration_for_frame_without_syncword_in_s: f32) -> Self {
70        Self {
71            hours,
72            minutes,
73            seconds,
74            frames,
75            frames_per_second: FramesPerSecond::from_frame_duration_without_syncword_in_s(duration_for_frame_without_syncword_in_s),
76        }
77    }
78    pub fn new(hours: u8, minutes: u8, seconds: u8, frames: u8, frames_per_second: FramesPerSecond) -> Self {
79        Self {
80            hours,
81            minutes,
82            seconds,
83            frames,
84            frames_per_second,
85        }
86    }
87}
88
89#[derive(PartialEq, Eq, Clone, Debug)]
90pub enum FramesPerSecond {
91    Unknown,
92    TwentyFour,
93    TwentyFive,
94    Thirty,
95}
96
97impl FramesPerSecond {
98    const DURATION_THIRTY_FULL_FRAME_IN_S: f32 = 0.033_333_33;
99    const DURATION_TWENTY_FIVE_FULL_FRAME_IN_S: f32 = 0.04;
100    const DURATION_TWENTY_FOUR_FULL_FRAME_IN_S: f32 = 0.041_666_66;
101
102    const DURATION_TWENTY_FOUR_WITHOUT_SYNC_WORD_IN_S: f32 = Self::DURATION_TWENTY_FOUR_FULL_FRAME_IN_S * 64.0 / 80.0;
103    const DURATION_TWENTY_FIVE_WITHOUT_SYNC_WORD_IN_S: f32 = Self::DURATION_TWENTY_FIVE_FULL_FRAME_IN_S * 64.0 / 80.0;
104    const DURATION_THIRTY_WITHOUT_SYNC_WORD_IN_S: f32 = Self::DURATION_THIRTY_FULL_FRAME_IN_S * 64.0 / 80.0;
105
106    const DURATION_BOUND_TWENTY_FOUR_WITHOUT_SYNC_WORD_IN_S: (f32, f32) = (Self::DURATION_TWENTY_FOUR_WITHOUT_SYNC_WORD_IN_S * 0.98, Self::DURATION_TWENTY_FOUR_WITHOUT_SYNC_WORD_IN_S * 1.02);
107    const DURATION_BOUND_THWENTY_FIVE_WITHOUT_SYNC_WORD_IN_S: (f32, f32) = (Self::DURATION_TWENTY_FIVE_WITHOUT_SYNC_WORD_IN_S * 0.98, Self::DURATION_TWENTY_FIVE_WITHOUT_SYNC_WORD_IN_S * 1.02);
108    const DURATION_BOUND_THIRTY_WITHOUT_SYNC_WORD_IN_S: (f32, f32) = (Self::DURATION_THIRTY_WITHOUT_SYNC_WORD_IN_S * 0.98, Self::DURATION_THIRTY_WITHOUT_SYNC_WORD_IN_S * 1.02);
109
110    fn from_frame_duration_without_syncword_in_s(frames_duration_s: f32) -> FramesPerSecond {
111        if Self::is_in_duration_bounds(frames_duration_s, Self::DURATION_BOUND_TWENTY_FOUR_WITHOUT_SYNC_WORD_IN_S) {
112            return FramesPerSecond::TwentyFour;
113        }
114        if Self::is_in_duration_bounds(frames_duration_s, Self::DURATION_BOUND_THWENTY_FIVE_WITHOUT_SYNC_WORD_IN_S) {
115            return FramesPerSecond::TwentyFive;
116        }
117        if Self::is_in_duration_bounds(frames_duration_s, Self::DURATION_BOUND_THIRTY_WITHOUT_SYNC_WORD_IN_S) {
118            return FramesPerSecond::Thirty;
119        }
120        FramesPerSecond::Unknown
121    }
122
123    fn is_in_duration_bounds(frames_duration_s: f32, bounds: (f32, f32)) -> bool {
124        frames_duration_s > bounds.0 && frames_duration_s < bounds.1
125    }
126}