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}