1use super::constants::*;
11use super::{VideoStandard, VitcWriterConfig};
12use crate::{FrameRate, Timecode, TimecodeError};
13
14pub struct VitcEncoder {
16 #[allow(dead_code)]
18 config: VitcWriterConfig,
19 current_field: u8,
21}
22
23impl VitcEncoder {
24 pub fn new(config: VitcWriterConfig) -> Self {
26 VitcEncoder {
27 config,
28 current_field: 1,
29 }
30 }
31
32 pub fn encode_line(
34 &mut self,
35 timecode: &Timecode,
36 field: u8,
37 ) -> Result<Vec<u8>, TimecodeError> {
38 let bits = self.timecode_to_bits(timecode, field)?;
40
41 let pixels = self.bits_to_pixels(&bits);
43
44 Ok(pixels)
45 }
46
47 fn timecode_to_bits(
49 &self,
50 timecode: &Timecode,
51 field: u8,
52 ) -> Result<[bool; BITS_PER_LINE], TimecodeError> {
53 let mut bits = [false; BITS_PER_LINE];
54
55 bits[0] = true;
57 bits[1] = true;
58
59 let mut data_bits = [false; DATA_BITS];
61
62 let frame_units = timecode.frames % 10;
64 let frame_tens = timecode.frames / 10;
65 let second_units = timecode.seconds % 10;
66 let second_tens = timecode.seconds / 10;
67 let minute_units = timecode.minutes % 10;
68 let minute_tens = timecode.minutes / 10;
69 let hour_units = timecode.hours % 10;
70 let hour_tens = timecode.hours / 10;
71
72 self.encode_bcd(&mut data_bits, 0, frame_units);
74
75 self.encode_nibble(&mut data_bits, 4, (timecode.user_bits & 0xF) as u8);
77
78 self.encode_bcd(&mut data_bits, 8, frame_tens);
80
81 data_bits[10] = timecode.frame_rate.drop_frame;
83
84 data_bits[11] = false;
86
87 self.encode_nibble(&mut data_bits, 12, ((timecode.user_bits >> 4) & 0xF) as u8);
89
90 self.encode_bcd(&mut data_bits, 16, second_units);
92
93 self.encode_nibble(&mut data_bits, 20, ((timecode.user_bits >> 8) & 0xF) as u8);
95
96 self.encode_bcd(&mut data_bits, 24, second_tens);
98
99 data_bits[27] = field == 2;
101
102 self.encode_nibble(&mut data_bits, 28, ((timecode.user_bits >> 12) & 0xF) as u8);
104
105 self.encode_bcd(&mut data_bits, 32, minute_units);
107
108 self.encode_nibble(&mut data_bits, 36, ((timecode.user_bits >> 16) & 0xF) as u8);
110
111 self.encode_bcd(&mut data_bits, 40, minute_tens);
113
114 data_bits[43] = false;
116
117 self.encode_nibble(&mut data_bits, 44, ((timecode.user_bits >> 20) & 0xF) as u8);
119
120 self.encode_bcd(&mut data_bits, 48, hour_units);
122
123 self.encode_nibble(&mut data_bits, 52, ((timecode.user_bits >> 24) & 0xF) as u8);
125
126 self.encode_bcd(&mut data_bits, 56, hour_tens);
128
129 data_bits[58] = false;
131
132 self.encode_nibble(&mut data_bits, 59, ((timecode.user_bits >> 28) & 0xF) as u8);
134
135 let crc = self.calculate_crc(&data_bits[0..72]);
137 for i in 0..8 {
138 data_bits[74 + i] = (crc & (1 << i)) != 0;
139 }
140
141 bits[SYNC_START_BITS..(DATA_BITS + SYNC_START_BITS)]
143 .copy_from_slice(&data_bits[..DATA_BITS]);
144
145 bits[84] = false;
147 bits[85] = false;
148 bits[86] = true;
149 bits[87] = true;
150 bits[88] = true;
151 bits[89] = true;
152
153 Ok(bits)
154 }
155
156 fn encode_bcd(&self, bits: &mut [bool; DATA_BITS], start: usize, value: u8) {
158 for i in 0..4 {
159 if start + i < DATA_BITS {
160 bits[start + i] = (value & (1 << i)) != 0;
161 }
162 }
163 }
164
165 fn encode_nibble(&self, bits: &mut [bool; DATA_BITS], start: usize, value: u8) {
167 for i in 0..4 {
168 if start + i < DATA_BITS {
169 bits[start + i] = (value & (1 << i)) != 0;
170 }
171 }
172 }
173
174 fn calculate_crc(&self, bits: &[bool]) -> u8 {
176 let mut crc = 0u8;
177
178 for &bit in bits {
179 let feedback = ((crc & 0x80) != 0) ^ bit;
180 crc <<= 1;
181 if feedback {
182 crc ^= 0x07; }
184 }
185
186 crc
187 }
188
189 fn bits_to_pixels(&self, bits: &[bool; BITS_PER_LINE]) -> Vec<u8> {
191 let mut pixels = Vec::with_capacity(BITS_PER_LINE * PIXELS_PER_BIT);
192
193 for &bit in bits {
194 let level = if bit { WHITE_LEVEL } else { BLACK_LEVEL };
195
196 for _ in 0..PIXELS_PER_BIT {
198 pixels.push(level);
199 }
200 }
201
202 pixels
203 }
204
205 pub fn reset(&mut self) {
207 self.current_field = 1;
208 }
209
210 pub fn set_field(&mut self, field: u8) {
212 self.current_field = field;
213 }
214
215 pub fn field(&self) -> u8 {
217 self.current_field
218 }
219}
220
221pub struct MultiLineVitcWriter {
223 encoder: VitcEncoder,
225 lines: Vec<u16>,
227}
228
229impl MultiLineVitcWriter {
230 pub fn new(config: VitcWriterConfig) -> Self {
232 let lines = config.scan_lines.clone();
233 MultiLineVitcWriter {
234 encoder: VitcEncoder::new(config),
235 lines,
236 }
237 }
238
239 pub fn encode_all_lines(
241 &mut self,
242 timecode: &Timecode,
243 field: u8,
244 ) -> Result<Vec<(u16, Vec<u8>)>, TimecodeError> {
245 let mut results = Vec::new();
246
247 for &line in &self.lines {
248 let pixels = self.encoder.encode_line(timecode, field)?;
249 results.push((line, pixels));
250 }
251
252 Ok(results)
253 }
254}
255
256pub struct VitcLineBuffer {
258 #[allow(dead_code)]
260 video_standard: VideoStandard,
261 lines: Vec<(u16, u8, Vec<u8>)>, }
264
265impl VitcLineBuffer {
266 pub fn new(video_standard: VideoStandard) -> Self {
268 VitcLineBuffer {
269 video_standard,
270 lines: Vec::new(),
271 }
272 }
273
274 pub fn add_line(&mut self, line_number: u16, field: u8, pixels: Vec<u8>) {
276 self.lines
278 .retain(|(ln, f, _)| !(*ln == line_number && *f == field));
279
280 self.lines.push((line_number, field, pixels));
281 }
282
283 pub fn get_field_lines(&self, field: u8) -> Vec<(u16, &[u8])> {
285 self.lines
286 .iter()
287 .filter(|(_, f, _)| *f == field)
288 .map(|(ln, _, pixels)| (*ln, pixels.as_slice()))
289 .collect()
290 }
291
292 pub fn clear(&mut self) {
294 self.lines.clear();
295 }
296
297 pub fn line_count(&self) -> usize {
299 self.lines.len()
300 }
301}
302
303pub struct PixelLevelAdjuster {
305 black_level: u8,
307 white_level: u8,
309}
310
311impl PixelLevelAdjuster {
312 pub fn new(video_standard: VideoStandard) -> Self {
314 match video_standard {
315 VideoStandard::Ntsc => PixelLevelAdjuster {
316 black_level: 16,
317 white_level: 235,
318 },
319 VideoStandard::Pal => PixelLevelAdjuster {
320 black_level: 16,
321 white_level: 235,
322 },
323 }
324 }
325
326 pub fn with_levels(black_level: u8, white_level: u8) -> Self {
328 PixelLevelAdjuster {
329 black_level,
330 white_level,
331 }
332 }
333
334 pub fn bit_to_pixel(&self, bit: bool) -> u8 {
336 if bit {
337 self.white_level
338 } else {
339 self.black_level
340 }
341 }
342
343 pub fn black_level(&self) -> u8 {
345 self.black_level
346 }
347
348 pub fn white_level(&self) -> u8 {
350 self.white_level
351 }
352}
353
354pub struct RiseTimeShaper {
356 rise_time_pixels: usize,
358}
359
360impl RiseTimeShaper {
361 pub fn new(rise_time_pixels: usize) -> Self {
363 RiseTimeShaper {
364 rise_time_pixels: rise_time_pixels.max(1),
365 }
366 }
367
368 pub fn shape_pixels(&self, pixels: &[u8]) -> Vec<u8> {
370 let mut shaped = Vec::with_capacity(pixels.len());
371
372 if pixels.is_empty() {
373 return shaped;
374 }
375
376 shaped.push(pixels[0]);
377
378 for i in 1..pixels.len() {
379 let current = pixels[i];
380 let prev = pixels[i - 1];
381
382 if current != prev {
383 let diff = current as i16 - prev as i16;
385 for j in 0..self.rise_time_pixels.min(pixels.len() - i) {
386 let progress = (j + 1) as f32 / self.rise_time_pixels as f32;
387 let value = prev as f32 + diff as f32 * progress;
388 shaped.push(value as u8);
389 }
390 for _ in 0..self.rise_time_pixels.min(pixels.len() - i) {
392 if i < pixels.len() {
393 shaped.push(current);
394 }
395 }
396 } else {
397 shaped.push(current);
398 }
399 }
400
401 shaped.truncate(pixels.len());
402 shaped
403 }
404}
405
406pub struct BlankingInserter {
408 blanking_level: u8,
410}
411
412impl BlankingInserter {
413 pub fn new(blanking_level: u8) -> Self {
415 BlankingInserter { blanking_level }
416 }
417
418 pub fn insert_blanking(
420 &self,
421 vitc_pixels: &[u8],
422 total_width: usize,
423 start_offset: usize,
424 ) -> Vec<u8> {
425 let mut full_line = vec![self.blanking_level; total_width];
426
427 let end_offset = (start_offset + vitc_pixels.len()).min(total_width);
429 for (i, &pixel) in vitc_pixels.iter().enumerate() {
430 if start_offset + i < end_offset {
431 full_line[start_offset + i] = pixel;
432 }
433 }
434
435 full_line
436 }
437}
438
439pub struct VitcFrameGenerator {
441 writer: MultiLineVitcWriter,
443 current_timecode: Option<Timecode>,
445 #[allow(dead_code)]
447 frame_rate: FrameRate,
448}
449
450impl VitcFrameGenerator {
451 pub fn new(config: VitcWriterConfig) -> Self {
453 let frame_rate = config.frame_rate;
454 VitcFrameGenerator {
455 writer: MultiLineVitcWriter::new(config),
456 current_timecode: None,
457 frame_rate,
458 }
459 }
460
461 pub fn set_timecode(&mut self, timecode: Timecode) {
463 self.current_timecode = Some(timecode);
464 }
465
466 pub fn generate_frame(&mut self) -> Result<Vec<(u16, u8, Vec<u8>)>, TimecodeError> {
468 if let Some(ref mut tc) = self.current_timecode {
469 let mut results = Vec::new();
470
471 let field1_lines = self.writer.encode_all_lines(tc, 1)?;
473 for (line, pixels) in field1_lines {
474 results.push((line, 1, pixels));
475 }
476
477 let field2_lines = self.writer.encode_all_lines(tc, 2)?;
479 for (line, pixels) in field2_lines {
480 results.push((line, 2, pixels));
481 }
482
483 tc.increment()?;
485
486 Ok(results)
487 } else {
488 Err(TimecodeError::InvalidConfiguration)
489 }
490 }
491
492 pub fn current_timecode(&self) -> Option<&Timecode> {
494 self.current_timecode.as_ref()
495 }
496}
497
498pub struct VitcUserBitsHelper;
500
501impl VitcUserBitsHelper {
502 pub fn validate_user_bits(user_bits: u32) -> bool {
504 let _ = user_bits;
506 true
507 }
508
509 pub fn extract_group(user_bits: u32, group: u8) -> u8 {
511 let shift = (group as u32) * 4;
512 ((user_bits >> shift) & 0xF) as u8
513 }
514
515 pub fn set_group(user_bits: u32, group: u8, value: u8) -> u32 {
517 let shift = (group as u32) * 4;
518 let mask = !(0xF << shift);
519 (user_bits & mask) | ((value as u32 & 0xF) << shift)
520 }
521}
522
523pub struct PixelPatternValidator;
525
526impl PixelPatternValidator {
527 pub fn validate_pattern(pixels: &[u8]) -> bool {
529 if pixels.len() < BITS_PER_LINE * PIXELS_PER_BIT {
530 return false;
531 }
532
533 for &pixel in pixels {
535 if !(16..=235).contains(&pixel) {
536 return false;
537 }
538 }
539
540 true
541 }
542
543 pub fn check_sync_pattern(pixels: &[u8]) -> bool {
545 if pixels.len() < 4 {
546 return false;
547 }
548
549 pixels[0] > 200 && pixels[1] > 200 && pixels[2] > 200 && pixels[3] > 200
551 }
552}
553
554#[cfg(test)]
555mod tests {
556 use super::*;
557
558 #[test]
559 fn test_encoder_creation() {
560 let config = VitcWriterConfig::default();
561 let encoder = VitcEncoder::new(config);
562 assert_eq!(encoder.field(), 1);
563 }
564
565 #[test]
566 fn test_encode_line() {
567 let config = VitcWriterConfig::default();
568 let mut encoder = VitcEncoder::new(config);
569
570 let timecode = Timecode::new(1, 2, 3, 4, FrameRate::Fps25).expect("valid timecode");
571 let pixels = encoder
572 .encode_line(&timecode, 1)
573 .expect("encode should succeed");
574
575 assert_eq!(pixels.len(), BITS_PER_LINE * PIXELS_PER_BIT);
576 }
577
578 #[test]
579 fn test_crc_calculation() {
580 let config = VitcWriterConfig::default();
581 let encoder = VitcEncoder::new(config);
582
583 let bits = [false; 72];
584 let crc = encoder.calculate_crc(&bits);
585 assert_eq!(crc, 0);
586 }
587
588 #[test]
589 fn test_pixel_level_adjuster() {
590 let adjuster = PixelLevelAdjuster::new(VideoStandard::Pal);
591 assert_eq!(adjuster.bit_to_pixel(false), 16);
592 assert_eq!(adjuster.bit_to_pixel(true), 235);
593 }
594
595 #[test]
596 fn test_user_bits_helper() {
597 let user_bits = 0x12345678u32;
598 assert_eq!(VitcUserBitsHelper::extract_group(user_bits, 0), 0x8);
599 assert_eq!(VitcUserBitsHelper::extract_group(user_bits, 1), 0x7);
600
601 let modified = VitcUserBitsHelper::set_group(user_bits, 0, 0xA);
602 assert_eq!(VitcUserBitsHelper::extract_group(modified, 0), 0xA);
603 }
604
605 #[test]
606 fn test_line_buffer() {
607 let mut buffer = VitcLineBuffer::new(VideoStandard::Pal);
608 buffer.add_line(19, 1, vec![16; 180]);
609 buffer.add_line(21, 1, vec![16; 180]);
610
611 assert_eq!(buffer.line_count(), 2);
612
613 let field1_lines = buffer.get_field_lines(1);
614 assert_eq!(field1_lines.len(), 2);
615 }
616}