1#![allow(dead_code)]
6#![allow(clippy::cast_precision_loss)]
7
8use crate::{FrameRate, Timecode, TimecodeError};
9
10#[derive(Debug, Clone, PartialEq, Eq)]
12pub struct TimecodegGap {
13 pub before: Timecode,
15 pub after: Timecode,
17 pub gap_frames: i64,
19}
20
21#[derive(Debug, Clone, PartialEq, Eq)]
23pub enum ContinuityResult {
24 Continuous,
26 Gap(u64),
28 Overlap(u64),
30 Repeat,
32}
33
34pub fn check_continuity(prev: &Timecode, next: &Timecode) -> ContinuityResult {
36 let prev_f = prev.to_frames();
37 let next_f = next.to_frames();
38
39 match next_f.cmp(&(prev_f + 1)) {
40 std::cmp::Ordering::Equal => ContinuityResult::Continuous,
41 std::cmp::Ordering::Greater => ContinuityResult::Gap(next_f - prev_f - 1),
42 std::cmp::Ordering::Less => {
43 if next_f == prev_f {
44 ContinuityResult::Repeat
45 } else {
46 ContinuityResult::Overlap(prev_f + 1 - next_f)
47 }
48 }
49 }
50}
51
52#[derive(Debug, Clone)]
54pub struct ContinuityMonitor {
55 frame_rate: FrameRate,
56 last_tc: Option<Timecode>,
57 gaps: Vec<TimecodegGap>,
58 gap_count: u32,
59 overlap_count: u32,
60 repeat_count: u32,
61 frame_count: u64,
62}
63
64impl ContinuityMonitor {
65 pub fn new(frame_rate: FrameRate) -> Self {
67 Self {
68 frame_rate,
69 last_tc: None,
70 gaps: Vec::new(),
71 gap_count: 0,
72 overlap_count: 0,
73 repeat_count: 0,
74 frame_count: 0,
75 }
76 }
77
78 pub fn feed(&mut self, tc: Timecode) -> ContinuityResult {
80 self.frame_count += 1;
81 let result = if let Some(ref last) = self.last_tc {
82 let r = check_continuity(last, &tc);
83 match &r {
84 ContinuityResult::Gap(n) => {
85 self.gap_count += 1;
86 self.gaps.push(TimecodegGap {
87 before: *last,
88 after: tc,
89 gap_frames: *n as i64,
90 });
91 }
92 ContinuityResult::Overlap(n) => {
93 self.overlap_count += 1;
94 self.gaps.push(TimecodegGap {
95 before: *last,
96 after: tc,
97 gap_frames: -(*n as i64),
98 });
99 }
100 ContinuityResult::Repeat => {
101 self.repeat_count += 1;
102 }
103 ContinuityResult::Continuous => {}
104 }
105 r
106 } else {
107 ContinuityResult::Continuous
108 };
109 self.last_tc = Some(tc);
110 result
111 }
112
113 pub fn gap_count(&self) -> u32 {
115 self.gap_count
116 }
117
118 pub fn overlap_count(&self) -> u32 {
120 self.overlap_count
121 }
122
123 pub fn repeat_count(&self) -> u32 {
125 self.repeat_count
126 }
127
128 pub fn frame_count(&self) -> u64 {
130 self.frame_count
131 }
132
133 pub fn gaps(&self) -> &[TimecodegGap] {
135 &self.gaps
136 }
137
138 pub fn reset(&mut self) {
140 self.last_tc = None;
141 self.gaps.clear();
142 self.gap_count = 0;
143 self.overlap_count = 0;
144 self.repeat_count = 0;
145 self.frame_count = 0;
146 }
147
148 pub fn last_timecode(&self) -> Option<&Timecode> {
150 self.last_tc.as_ref()
151 }
152
153 pub fn report(&self) -> String {
155 format!(
156 "Frames: {}, Gaps: {}, Overlaps: {}, Repeats: {}",
157 self.frame_count, self.gap_count, self.overlap_count, self.repeat_count
158 )
159 }
160}
161
162pub fn expected_frame_count(start: &Timecode, end: &Timecode) -> u64 {
164 let s = start.to_frames();
165 let e = end.to_frames();
166 e.saturating_sub(s)
167}
168
169pub fn find_gaps(timecodes: &[Timecode]) -> Vec<TimecodegGap> {
171 let mut gaps = Vec::new();
172 for window in timecodes.windows(2) {
173 let result = check_continuity(&window[0], &window[1]);
174 match result {
175 ContinuityResult::Gap(n) => {
176 gaps.push(TimecodegGap {
177 before: window[0],
178 after: window[1],
179 gap_frames: n as i64,
180 });
181 }
182 ContinuityResult::Overlap(n) => {
183 gaps.push(TimecodegGap {
184 before: window[0],
185 after: window[1],
186 gap_frames: -(n as i64),
187 });
188 }
189 _ => {}
190 }
191 }
192 gaps
193}
194
195#[derive(Debug, Clone, PartialEq, Eq)]
197pub struct TimecodeRange {
198 pub start: Timecode,
200 pub end: Timecode,
202}
203
204impl TimecodeRange {
205 pub fn new(start: Timecode, end: Timecode) -> Result<Self, TimecodeError> {
207 if end.to_frames() < start.to_frames() {
208 return Err(TimecodeError::InvalidConfiguration);
209 }
210 Ok(Self { start, end })
211 }
212
213 pub fn duration_frames(&self) -> u64 {
215 self.end.to_frames().saturating_sub(self.start.to_frames()) + 1
216 }
217
218 pub fn contains(&self, tc: &Timecode) -> bool {
220 let f = tc.to_frames();
221 f >= self.start.to_frames() && f <= self.end.to_frames()
222 }
223
224 pub fn overlaps(&self, other: &TimecodeRange) -> bool {
226 self.start.to_frames() <= other.end.to_frames()
227 && other.start.to_frames() <= self.end.to_frames()
228 }
229}
230
231pub fn split_into_segments(timecodes: &[Timecode]) -> Vec<Vec<Timecode>> {
233 if timecodes.is_empty() {
234 return Vec::new();
235 }
236
237 let mut segments = Vec::new();
238 let mut current = vec![timecodes[0]];
239
240 for window in timecodes.windows(2) {
241 match check_continuity(&window[0], &window[1]) {
242 ContinuityResult::Continuous => {
243 current.push(window[1]);
244 }
245 _ => {
246 segments.push(current);
247 current = vec![window[1]];
248 }
249 }
250 }
251 segments.push(current);
252 segments
253}
254
255#[cfg(test)]
256mod tests {
257 use super::*;
258
259 fn tc(h: u8, m: u8, s: u8, f: u8) -> Timecode {
260 Timecode::new(h, m, s, f, FrameRate::Fps25).expect("valid timecode")
261 }
262
263 #[test]
264 fn test_check_continuity_continuous() {
265 let a = tc(0, 0, 0, 0);
266 let b = tc(0, 0, 0, 1);
267 assert_eq!(check_continuity(&a, &b), ContinuityResult::Continuous);
268 }
269
270 #[test]
271 fn test_check_continuity_gap() {
272 let a = tc(0, 0, 0, 0);
273 let b = tc(0, 0, 0, 5);
274 assert_eq!(check_continuity(&a, &b), ContinuityResult::Gap(4));
275 }
276
277 #[test]
278 fn test_check_continuity_overlap() {
279 let a = tc(0, 0, 0, 5);
280 let b = tc(0, 0, 0, 3);
281 assert_eq!(check_continuity(&a, &b), ContinuityResult::Overlap(3));
282 }
283
284 #[test]
285 fn test_check_continuity_repeat() {
286 let a = tc(0, 0, 0, 5);
287 let b = tc(0, 0, 0, 5);
288 assert_eq!(check_continuity(&a, &b), ContinuityResult::Repeat);
289 }
290
291 #[test]
292 fn test_monitor_continuous() {
293 let mut mon = ContinuityMonitor::new(FrameRate::Fps25);
294 for f in 0u8..10 {
295 let t = tc(0, 0, 0, f);
296 mon.feed(t);
297 }
298 assert_eq!(mon.gap_count(), 0);
299 assert_eq!(mon.frame_count(), 10);
300 }
301
302 #[test]
303 fn test_monitor_gap_detection() {
304 let mut mon = ContinuityMonitor::new(FrameRate::Fps25);
305 mon.feed(tc(0, 0, 0, 0));
306 mon.feed(tc(0, 0, 0, 5)); assert_eq!(mon.gap_count(), 1);
308 assert_eq!(mon.gaps()[0].gap_frames, 4);
309 }
310
311 #[test]
312 fn test_monitor_reset() {
313 let mut mon = ContinuityMonitor::new(FrameRate::Fps25);
314 mon.feed(tc(0, 0, 0, 0));
315 mon.feed(tc(0, 0, 0, 5));
316 mon.reset();
317 assert_eq!(mon.gap_count(), 0);
318 assert_eq!(mon.frame_count(), 0);
319 assert!(mon.last_timecode().is_none());
320 }
321
322 #[test]
323 fn test_monitor_report() {
324 let mut mon = ContinuityMonitor::new(FrameRate::Fps25);
325 mon.feed(tc(0, 0, 0, 0));
326 let report = mon.report();
327 assert!(report.contains("Frames: 1"));
328 }
329
330 #[test]
331 fn test_find_gaps() {
332 let tcs = vec![
333 tc(0, 0, 0, 0),
334 tc(0, 0, 0, 1),
335 tc(0, 0, 0, 5),
336 tc(0, 0, 0, 6),
337 ];
338 let gaps = find_gaps(&tcs);
339 assert_eq!(gaps.len(), 1);
340 assert_eq!(gaps[0].gap_frames, 3);
341 }
342
343 #[test]
344 fn test_timecode_range_contains() {
345 let start = tc(0, 0, 0, 0);
346 let end = tc(0, 0, 1, 0);
347 let range = TimecodeRange::new(start, end).expect("valid timecode range");
348 assert!(range.contains(&tc(0, 0, 0, 10)));
349 assert!(!range.contains(&tc(0, 0, 2, 0)));
350 }
351
352 #[test]
353 fn test_timecode_range_duration() {
354 let start = tc(0, 0, 0, 0);
355 let end = tc(0, 0, 0, 24);
356 let range = TimecodeRange::new(start, end).expect("valid timecode range");
357 assert_eq!(range.duration_frames(), 25);
358 }
359
360 #[test]
361 fn test_timecode_range_overlaps() {
362 let r1 = TimecodeRange::new(tc(0, 0, 0, 0), tc(0, 0, 0, 10)).expect("valid timecode range");
363 let r2 = TimecodeRange::new(tc(0, 0, 0, 5), tc(0, 0, 0, 20)).expect("valid timecode range");
364 let r3 = TimecodeRange::new(tc(0, 0, 1, 0), tc(0, 0, 1, 10)).expect("valid timecode range");
365 assert!(r1.overlaps(&r2));
366 assert!(!r1.overlaps(&r3));
367 }
368
369 #[test]
370 fn test_split_into_segments() {
371 let tcs = vec![
372 tc(0, 0, 0, 0),
373 tc(0, 0, 0, 1),
374 tc(0, 0, 0, 2),
375 tc(0, 0, 1, 0),
376 tc(0, 0, 1, 1),
377 ];
378 let segments = split_into_segments(&tcs);
379 assert_eq!(segments.len(), 2);
380 assert_eq!(segments[0].len(), 3);
381 assert_eq!(segments[1].len(), 2);
382 }
383
384 #[test]
385 fn test_expected_frame_count() {
386 let start = tc(0, 0, 0, 0);
387 let end = tc(0, 0, 1, 0);
388 assert_eq!(expected_frame_count(&start, &end), 25);
389 }
390}