oxihuman_viewer/
timecode_overlay_view.rs1#![allow(dead_code)]
4
5#[derive(Debug, Clone, Copy, PartialEq)]
9pub enum TimecodeFormat {
10 Smpte24,
11 Smpte25,
12 Smpte2997,
13 Smpte30,
14 Frames,
15}
16
17#[derive(Debug, Clone)]
19pub struct TimecodeOverlayView {
20 pub format: TimecodeFormat,
21 pub hours: u32,
22 pub minutes: u32,
23 pub seconds: u32,
24 pub frames: u32,
25 pub position_x: f32,
26 pub position_y: f32,
27 pub text_scale: f32,
28 pub enabled: bool,
29}
30
31impl TimecodeOverlayView {
32 pub fn new() -> Self {
33 Self {
34 format: TimecodeFormat::Smpte24,
35 hours: 0,
36 minutes: 0,
37 seconds: 0,
38 frames: 0,
39 position_x: 0.05,
40 position_y: 0.05,
41 text_scale: 1.0,
42 enabled: true,
43 }
44 }
45}
46
47impl Default for TimecodeOverlayView {
48 fn default() -> Self {
49 Self::new()
50 }
51}
52
53pub fn new_timecode_overlay_view() -> TimecodeOverlayView {
55 TimecodeOverlayView::new()
56}
57
58pub fn tcv_set_timecode(view: &mut TimecodeOverlayView, hh: u32, mm: u32, ss: u32, ff: u32) {
60 view.hours = hh.min(23);
61 view.minutes = mm.min(59);
62 view.seconds = ss.min(59);
63 view.frames = ff;
64}
65
66pub fn tcv_set_format(view: &mut TimecodeOverlayView, format: TimecodeFormat) {
68 view.format = format;
69}
70
71pub fn tcv_set_position(view: &mut TimecodeOverlayView, x: f32, y: f32) {
73 view.position_x = x.clamp(0.0, 1.0);
74 view.position_y = y.clamp(0.0, 1.0);
75}
76
77pub fn tcv_set_enabled(view: &mut TimecodeOverlayView, enabled: bool) {
79 view.enabled = enabled;
80}
81
82pub fn tcv_format_string(view: &TimecodeOverlayView) -> String {
84 format!(
85 "{:02}:{:02}:{:02}:{:02}",
86 view.hours, view.minutes, view.seconds, view.frames
87 )
88}
89
90pub fn tcv_total_frames(view: &TimecodeOverlayView) -> u64 {
92 let fps = match view.format {
93 TimecodeFormat::Smpte24 => 24u64,
94 TimecodeFormat::Smpte25 => 25,
95 TimecodeFormat::Smpte2997 => 30,
96 TimecodeFormat::Smpte30 => 30,
97 TimecodeFormat::Frames => 1,
98 };
99 let total_secs =
100 (view.hours as u64) * 3600 + (view.minutes as u64) * 60 + (view.seconds as u64);
101 total_secs * fps + (view.frames as u64)
102}
103
104pub fn timecode_overlay_view_to_json(view: &TimecodeOverlayView) -> String {
106 format!(
107 r#"{{"timecode":"{}","enabled":{}}}"#,
108 tcv_format_string(view),
109 view.enabled
110 )
111}
112
113#[cfg(test)]
114mod tests {
115 use super::*;
116
117 #[test]
118 fn test_defaults() {
119 let v = new_timecode_overlay_view();
120 assert_eq!(v.hours, 0);
121 assert!(v.enabled);
122 }
123
124 #[test]
125 fn test_set_timecode() {
126 let mut v = new_timecode_overlay_view();
127 tcv_set_timecode(&mut v, 1, 30, 45, 12);
128 assert_eq!(v.hours, 1);
129 assert_eq!(v.frames, 12);
130 }
131
132 #[test]
133 fn test_minutes_clamp() {
134 let mut v = new_timecode_overlay_view();
135 tcv_set_timecode(&mut v, 0, 100, 0, 0);
136 assert_eq!(v.minutes, 59);
137 }
138
139 #[test]
140 fn test_format_string() {
141 let mut v = new_timecode_overlay_view();
142 tcv_set_timecode(&mut v, 1, 2, 3, 4);
143 assert_eq!(tcv_format_string(&v), "01:02:03:04");
144 }
145
146 #[test]
147 fn test_total_frames_zero() {
148 let v = new_timecode_overlay_view();
149 assert_eq!(tcv_total_frames(&v), 0);
150 }
151
152 #[test]
153 fn test_total_frames_one_second() {
154 let mut v = new_timecode_overlay_view();
155 tcv_set_timecode(&mut v, 0, 0, 1, 0);
156 assert_eq!(tcv_total_frames(&v), 24);
157 }
158
159 #[test]
160 fn test_position_clamp() {
161 let mut v = new_timecode_overlay_view();
162 tcv_set_position(&mut v, -1.0, 2.0);
163 assert_eq!(v.position_x, 0.0);
164 assert_eq!(v.position_y, 1.0);
165 }
166
167 #[test]
168 fn test_json() {
169 let v = new_timecode_overlay_view();
170 let s = timecode_overlay_view_to_json(&v);
171 assert!(s.contains("timecode"));
172 }
173
174 #[test]
175 fn test_clone() {
176 let v = new_timecode_overlay_view();
177 let v2 = v.clone();
178 assert_eq!(v2.format, v.format);
179 }
180}