oximedia_timecode/vitc/
mod.rs1pub mod decoder;
23pub mod encoder;
24pub mod smpte309m;
25
26pub use smpte309m::{decode_anc_timecode, encode_anc_timecode, Smpte309mPacket};
27
28use crate::{FrameRate, Timecode, TimecodeError, TimecodeReader, TimecodeWriter};
29
30#[derive(Debug, Clone)]
32pub struct VitcReaderConfig {
33 pub video_standard: VideoStandard,
35 pub frame_rate: FrameRate,
37 pub scan_lines: Vec<u16>,
39 pub field_preference: FieldPreference,
41}
42
43impl Default for VitcReaderConfig {
44 fn default() -> Self {
45 VitcReaderConfig {
46 video_standard: VideoStandard::Pal,
47 frame_rate: FrameRate::Fps25,
48 scan_lines: vec![19, 21], field_preference: FieldPreference::Both,
50 }
51 }
52}
53
54#[derive(Debug, Clone, Copy, PartialEq, Eq)]
56pub enum VideoStandard {
57 Ntsc,
59 Pal,
61}
62
63impl VideoStandard {
64 pub fn total_lines(&self) -> u16 {
66 match self {
67 VideoStandard::Ntsc => 525,
68 VideoStandard::Pal => 625,
69 }
70 }
71
72 pub fn active_lines(&self) -> u16 {
74 match self {
75 VideoStandard::Ntsc => 486,
76 VideoStandard::Pal => 576,
77 }
78 }
79
80 pub fn pixels_per_line(&self) -> u16 {
82 match self {
83 VideoStandard::Ntsc => 720,
84 VideoStandard::Pal => 720,
85 }
86 }
87}
88
89#[derive(Debug, Clone, Copy, PartialEq, Eq)]
91pub enum FieldPreference {
92 Field1,
94 Field2,
96 Both,
98}
99
100pub struct VitcReader {
102 decoder: decoder::VitcDecoder,
103 frame_rate: FrameRate,
104}
105
106impl VitcReader {
107 pub fn new(config: VitcReaderConfig) -> Self {
109 let frame_rate = config.frame_rate;
110 VitcReader {
111 decoder: decoder::VitcDecoder::new(config),
112 frame_rate,
113 }
114 }
115
116 pub fn process_line(
118 &mut self,
119 line_number: u16,
120 field: u8,
121 pixels: &[u8],
122 ) -> Result<Option<Timecode>, TimecodeError> {
123 self.decoder.process_line(line_number, field, pixels)
124 }
125
126 pub fn reset(&mut self) {
128 self.decoder.reset();
129 }
130
131 pub fn crc_errors(&self) -> u32 {
133 self.decoder.crc_errors()
134 }
135}
136
137impl TimecodeReader for VitcReader {
138 fn read_timecode(&mut self) -> Result<Option<Timecode>, TimecodeError> {
139 Ok(None)
141 }
142
143 fn frame_rate(&self) -> FrameRate {
144 self.frame_rate
145 }
146
147 fn is_synchronized(&self) -> bool {
148 self.decoder.is_synchronized()
149 }
150}
151
152#[derive(Debug, Clone)]
154pub struct VitcWriterConfig {
155 pub video_standard: VideoStandard,
157 pub frame_rate: FrameRate,
159 pub scan_lines: Vec<u16>,
161 pub both_fields: bool,
163}
164
165impl Default for VitcWriterConfig {
166 fn default() -> Self {
167 VitcWriterConfig {
168 video_standard: VideoStandard::Pal,
169 frame_rate: FrameRate::Fps25,
170 scan_lines: vec![19, 21],
171 both_fields: true,
172 }
173 }
174}
175
176pub struct VitcWriter {
178 encoder: encoder::VitcEncoder,
179 frame_rate: FrameRate,
180}
181
182impl VitcWriter {
183 pub fn new(config: VitcWriterConfig) -> Self {
185 let frame_rate = config.frame_rate;
186 VitcWriter {
187 encoder: encoder::VitcEncoder::new(config),
188 frame_rate,
189 }
190 }
191
192 pub fn encode_line(
194 &mut self,
195 timecode: &Timecode,
196 field: u8,
197 ) -> Result<Vec<u8>, TimecodeError> {
198 self.encoder.encode_line(timecode, field)
199 }
200
201 pub fn reset(&mut self) {
203 self.encoder.reset();
204 }
205}
206
207impl TimecodeWriter for VitcWriter {
208 fn write_timecode(&mut self, timecode: &Timecode) -> Result<(), TimecodeError> {
209 let _pixels_f1 = self.encode_line(timecode, 1)?;
211 let _pixels_f2 = self.encode_line(timecode, 2)?;
213 Ok(())
215 }
216
217 fn frame_rate(&self) -> FrameRate {
218 self.frame_rate
219 }
220
221 fn flush(&mut self) -> Result<(), TimecodeError> {
222 Ok(())
223 }
224}
225
226pub(crate) mod constants {
228 pub const BITS_PER_LINE: usize = 90;
230
231 pub const DATA_BITS: usize = 82;
233
234 pub const SYNC_START_BITS: usize = 2;
236
237 #[allow(dead_code)]
239 pub const SYNC_END_BITS: usize = 6;
240
241 pub const PIXELS_PER_BIT: usize = 2;
243
244 pub const BLACK_LEVEL: u8 = 16;
246
247 pub const WHITE_LEVEL: u8 = 235;
249}
250
251#[cfg(test)]
252mod tests {
253 use super::*;
254
255 #[test]
256 fn test_vitc_reader_creation() {
257 let config = VitcReaderConfig::default();
258 let _reader = VitcReader::new(config);
259 }
260
261 #[test]
262 fn test_vitc_writer_creation() {
263 let config = VitcWriterConfig::default();
264 let _writer = VitcWriter::new(config);
265 }
266
267 #[test]
268 fn test_video_standard() {
269 assert_eq!(VideoStandard::Ntsc.total_lines(), 525);
270 assert_eq!(VideoStandard::Pal.total_lines(), 625);
271 assert_eq!(VideoStandard::Ntsc.pixels_per_line(), 720);
272 }
273
274 #[test]
275 fn test_constants() {
276 assert_eq!(constants::BITS_PER_LINE, 90);
277 assert_eq!(constants::PIXELS_PER_BIT, 2);
278 }
279}