use super::constants::*;
use super::{FieldPreference, VitcReaderConfig};
use crate::{FrameRate, Timecode, TimecodeError};
pub struct VitcDecoder {
config: VitcReaderConfig,
last_timecode: Option<Timecode>,
crc_error_count: u32,
sync_count: u32,
threshold: u8,
}
impl VitcDecoder {
pub fn new(config: VitcReaderConfig) -> Self {
VitcDecoder {
config,
last_timecode: None,
crc_error_count: 0,
sync_count: 0,
threshold: 128,
}
}
pub fn process_line(
&mut self,
line_number: u16,
field: u8,
pixels: &[u8],
) -> Result<Option<Timecode>, TimecodeError> {
if !self.config.scan_lines.contains(&line_number) {
return Ok(None);
}
match self.config.field_preference {
FieldPreference::Field1 if field != 1 => return Ok(None),
FieldPreference::Field2 if field != 2 => return Ok(None),
_ => {}
}
let bits = self.pixels_to_bits(pixels)?;
match self.decode_vitc_bits(&bits, field) {
Ok(timecode) => {
self.sync_count = self.sync_count.saturating_add(1);
self.last_timecode = Some(timecode);
Ok(Some(timecode))
}
Err(e) => {
if e == TimecodeError::CrcError {
self.crc_error_count += 1;
}
Err(e)
}
}
}
fn pixels_to_bits(&mut self, pixels: &[u8]) -> Result<[bool; BITS_PER_LINE], TimecodeError> {
if pixels.len() < BITS_PER_LINE * PIXELS_PER_BIT {
return Err(TimecodeError::BufferTooSmall);
}
self.adjust_threshold(pixels);
let mut bits = [false; BITS_PER_LINE];
for (i, bit) in bits.iter_mut().enumerate().take(BITS_PER_LINE) {
let pixel_index = i * PIXELS_PER_BIT + PIXELS_PER_BIT / 2;
if pixel_index < pixels.len() {
*bit = pixels[pixel_index] > self.threshold;
}
}
Ok(bits)
}
fn adjust_threshold(&mut self, pixels: &[u8]) {
let mut min_level = 255u8;
let mut max_level = 0u8;
for &pixel in pixels.iter().take(BITS_PER_LINE * PIXELS_PER_BIT) {
min_level = min_level.min(pixel);
max_level = max_level.max(pixel);
}
self.threshold = ((min_level as u16 + max_level as u16) / 2) as u8;
}
fn decode_vitc_bits(
&self,
bits: &[bool; BITS_PER_LINE],
field: u8,
) -> Result<Timecode, TimecodeError> {
self.validate_sync_bits(bits)?;
let mut data_bits = [false; DATA_BITS];
data_bits[..DATA_BITS]
.copy_from_slice(&bits[SYNC_START_BITS..(DATA_BITS + SYNC_START_BITS)]);
self.validate_crc(&data_bits)?;
self.decode_timecode_from_bits(&data_bits, field)
}
fn validate_sync_bits(&self, bits: &[bool; BITS_PER_LINE]) -> Result<(), TimecodeError> {
if !bits[0] || !bits[1] {
return Err(TimecodeError::SyncNotFound);
}
if bits[84] || bits[85] || !bits[86] || !bits[87] || !bits[88] || !bits[89] {
return Err(TimecodeError::SyncNotFound);
}
Ok(())
}
fn validate_crc(&self, data_bits: &[bool; DATA_BITS]) -> Result<(), TimecodeError> {
let received_crc = self.extract_crc(data_bits);
let calculated_crc = self.calculate_crc(&data_bits[0..72]);
if received_crc != calculated_crc {
return Err(TimecodeError::CrcError);
}
Ok(())
}
fn extract_crc(&self, data_bits: &[bool; DATA_BITS]) -> u8 {
let mut crc = 0u8;
for i in 0..8 {
if data_bits[74 + i] {
crc |= 1 << i;
}
}
crc
}
fn calculate_crc(&self, bits: &[bool]) -> u8 {
let mut crc = 0u8;
for &bit in bits {
let feedback = ((crc & 0x80) != 0) ^ bit;
crc <<= 1;
if feedback {
crc ^= 0x07; }
}
crc
}
fn decode_timecode_from_bits(
&self,
data_bits: &[bool; DATA_BITS],
field: u8,
) -> Result<Timecode, TimecodeError> {
let frame_units = self.bits_to_u8(&data_bits[0..4]);
let _user_bits_1 = self.bits_to_u8(&data_bits[4..8]);
let frame_tens = self.bits_to_u8(&data_bits[8..10]);
let drop_frame = data_bits[10];
let _color_frame = data_bits[11];
let _user_bits_2 = self.bits_to_u8(&data_bits[12..16]);
let second_units = self.bits_to_u8(&data_bits[16..20]);
let _user_bits_3 = self.bits_to_u8(&data_bits[20..24]);
let second_tens = self.bits_to_u8(&data_bits[24..27]);
let field_mark = data_bits[27];
if (field_mark && field != 2) || (!field_mark && field != 1) {
}
let _user_bits_4 = self.bits_to_u8(&data_bits[28..32]);
let minute_units = self.bits_to_u8(&data_bits[32..36]);
let _user_bits_5 = self.bits_to_u8(&data_bits[36..40]);
let minute_tens = self.bits_to_u8(&data_bits[40..43]);
let _binary_group = data_bits[43];
let _user_bits_6 = self.bits_to_u8(&data_bits[44..48]);
let hour_units = self.bits_to_u8(&data_bits[48..52]);
let _user_bits_7 = self.bits_to_u8(&data_bits[52..56]);
let hour_tens = self.bits_to_u8(&data_bits[56..58]);
let frames = frame_tens * 10 + frame_units;
let seconds = second_tens * 10 + second_units;
let minutes = minute_tens * 10 + minute_units;
let hours = hour_tens * 10 + hour_units;
let frame_rate = if drop_frame && self.config.frame_rate == FrameRate::Fps2997NDF {
FrameRate::Fps2997DF
} else {
self.config.frame_rate
};
let timecode = Timecode::new(hours, minutes, seconds, frames, frame_rate)?;
Ok(timecode)
}
fn bits_to_u8(&self, bits: &[bool]) -> u8 {
let mut value = 0u8;
for (i, &bit) in bits.iter().enumerate() {
if bit {
value |= 1 << i;
}
}
value
}
#[allow(dead_code)]
fn extract_user_bits(&self, data_bits: &[bool; DATA_BITS]) -> u32 {
let mut user_bits = 0u32;
user_bits |= self.bits_to_u8(&data_bits[4..8]) as u32;
user_bits |= (self.bits_to_u8(&data_bits[12..16]) as u32) << 4;
user_bits |= (self.bits_to_u8(&data_bits[20..24]) as u32) << 8;
user_bits |= (self.bits_to_u8(&data_bits[28..32]) as u32) << 12;
user_bits |= (self.bits_to_u8(&data_bits[36..40]) as u32) << 16;
user_bits |= (self.bits_to_u8(&data_bits[44..48]) as u32) << 20;
user_bits |= (self.bits_to_u8(&data_bits[52..56]) as u32) << 24;
user_bits
}
pub fn reset(&mut self) {
self.last_timecode = None;
self.crc_error_count = 0;
self.sync_count = 0;
self.threshold = 128;
}
pub fn is_synchronized(&self) -> bool {
self.sync_count >= 5 && self.last_timecode.is_some()
}
pub fn crc_errors(&self) -> u32 {
self.crc_error_count
}
}
#[allow(dead_code)]
struct BitPatternAnalyzer {
run_lengths: Vec<usize>,
current_run: usize,
current_bit: bool,
}
impl BitPatternAnalyzer {
#[allow(dead_code)]
fn new() -> Self {
BitPatternAnalyzer {
run_lengths: Vec::new(),
current_run: 0,
current_bit: false,
}
}
#[allow(dead_code)]
fn add_bit(&mut self, bit: bool) {
if bit == self.current_bit {
self.current_run += 1;
} else {
if self.current_run > 0 {
self.run_lengths.push(self.current_run);
}
self.current_bit = bit;
self.current_run = 1;
}
}
#[allow(dead_code)]
fn finish(&mut self) {
if self.current_run > 0 {
self.run_lengths.push(self.current_run);
}
}
#[allow(dead_code)]
fn get_run_lengths(&self) -> &[usize] {
&self.run_lengths
}
#[allow(dead_code)]
fn reset(&mut self) {
self.run_lengths.clear();
self.current_run = 0;
self.current_bit = false;
}
}
pub struct LineQualityAnalyzer {
min_pixel: u8,
max_pixel: u8,
pixel_count: usize,
pixel_sum: u64,
}
impl Default for LineQualityAnalyzer {
fn default() -> Self {
Self::new()
}
}
impl LineQualityAnalyzer {
pub fn new() -> Self {
LineQualityAnalyzer {
min_pixel: 255,
max_pixel: 0,
pixel_count: 0,
pixel_sum: 0,
}
}
pub fn analyze(&mut self, pixels: &[u8]) {
for &pixel in pixels {
self.min_pixel = self.min_pixel.min(pixel);
self.max_pixel = self.max_pixel.max(pixel);
self.pixel_sum += pixel as u64;
self.pixel_count += 1;
}
}
pub fn get_snr_estimate(&self) -> f32 {
let dynamic_range = (self.max_pixel as i16 - self.min_pixel as i16).abs() as f32;
if dynamic_range > 0.0 {
20.0 * (dynamic_range / 255.0).log10()
} else {
0.0
}
}
pub fn get_average(&self) -> f32 {
if self.pixel_count > 0 {
self.pixel_sum as f32 / self.pixel_count as f32
} else {
0.0
}
}
pub fn get_dynamic_range(&self) -> u8 {
self.max_pixel.saturating_sub(self.min_pixel)
}
pub fn reset(&mut self) {
self.min_pixel = 255;
self.max_pixel = 0;
self.pixel_count = 0;
self.pixel_sum = 0;
}
}
pub struct FieldDetector {
field_history: Vec<bool>,
max_history: usize,
}
impl FieldDetector {
pub fn new(max_history: usize) -> Self {
FieldDetector {
field_history: Vec::with_capacity(max_history),
max_history,
}
}
pub fn add_field_mark(&mut self, field_mark: bool) {
self.field_history.push(field_mark);
if self.field_history.len() > self.max_history {
self.field_history.remove(0);
}
}
pub fn get_predominant_field(&self) -> Option<u8> {
if self.field_history.is_empty() {
return None;
}
let field2_count = self.field_history.iter().filter(|&&f| f).count();
let field1_count = self.field_history.len() - field2_count;
if field2_count > field1_count {
Some(2)
} else {
Some(1)
}
}
pub fn reset(&mut self) {
self.field_history.clear();
}
}
pub struct MultiLineVitcReader {
decoders: Vec<(u16, VitcDecoder)>,
}
impl MultiLineVitcReader {
pub fn new(config: VitcReaderConfig) -> Self {
let mut decoders = Vec::new();
for &line in &config.scan_lines {
let mut line_config = config.clone();
line_config.scan_lines = vec![line];
decoders.push((line, VitcDecoder::new(line_config)));
}
MultiLineVitcReader { decoders }
}
pub fn process_line(
&mut self,
line_number: u16,
field: u8,
pixels: &[u8],
) -> Vec<(u16, Result<Option<Timecode>, TimecodeError>)> {
let mut results = Vec::new();
for (line, decoder) in &mut self.decoders {
if *line == line_number {
let result = decoder.process_line(line_number, field, pixels);
results.push((*line, result));
}
}
results
}
pub fn get_best_timecode(
&self,
results: &[(u16, Result<Option<Timecode>, TimecodeError>)],
) -> Option<Timecode> {
for (_line, result) in results {
if let Ok(Some(tc)) = result {
return Some(*tc);
}
}
None
}
}
pub struct VitcErrorCorrector {
history: Vec<Timecode>,
max_history: usize,
}
impl VitcErrorCorrector {
pub fn new(max_history: usize) -> Self {
VitcErrorCorrector {
history: Vec::with_capacity(max_history),
max_history,
}
}
pub fn add_timecode(&mut self, timecode: Timecode) {
self.history.push(timecode);
if self.history.len() > self.max_history {
self.history.remove(0);
}
}
pub fn correct_timecode(&self, timecode: &Timecode) -> Option<Timecode> {
if self.history.is_empty() {
return Some(*timecode);
}
if let Some(last) = self.history.last() {
let mut expected = *last;
if expected.increment().is_ok() {
let diff = (timecode.to_frames() as i64 - expected.to_frames() as i64).abs();
if diff <= 2 {
return Some(*timecode);
}
}
}
Some(*timecode)
}
pub fn reset(&mut self) {
self.history.clear();
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_decoder_creation() {
let config = VitcReaderConfig::default();
let decoder = VitcDecoder::new(config);
assert!(!decoder.is_synchronized());
}
#[test]
fn test_crc_calculation() {
let config = VitcReaderConfig::default();
let decoder = VitcDecoder::new(config);
let bits = [false; 72];
let crc = decoder.calculate_crc(&bits);
assert_eq!(crc, 0);
}
#[test]
fn test_bits_to_u8() {
let config = VitcReaderConfig::default();
let decoder = VitcDecoder::new(config);
let bits = [true, false, true, false]; assert_eq!(decoder.bits_to_u8(&bits), 5);
}
#[test]
fn test_line_quality_analyzer() {
let mut analyzer = LineQualityAnalyzer::new();
let pixels = vec![16, 235, 16, 235];
analyzer.analyze(&pixels);
assert!(analyzer.get_dynamic_range() > 200);
}
#[test]
fn test_field_detector() {
let mut detector = FieldDetector::new(10);
detector.add_field_mark(true);
detector.add_field_mark(true);
detector.add_field_mark(false);
assert_eq!(detector.get_predominant_field(), Some(2));
}
}