pub mod decoder;
pub mod encoder;
use crate::{FrameRate, Timecode, TimecodeError, TimecodeReader, TimecodeWriter};
#[derive(Debug, Clone)]
pub struct VitcReaderConfig {
pub video_standard: VideoStandard,
pub frame_rate: FrameRate,
pub scan_lines: Vec<u16>,
pub field_preference: FieldPreference,
}
impl Default for VitcReaderConfig {
fn default() -> Self {
VitcReaderConfig {
video_standard: VideoStandard::Pal,
frame_rate: FrameRate::Fps25,
scan_lines: vec![19, 21], field_preference: FieldPreference::Both,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum VideoStandard {
Ntsc,
Pal,
}
impl VideoStandard {
pub fn total_lines(&self) -> u16 {
match self {
VideoStandard::Ntsc => 525,
VideoStandard::Pal => 625,
}
}
pub fn active_lines(&self) -> u16 {
match self {
VideoStandard::Ntsc => 486,
VideoStandard::Pal => 576,
}
}
pub fn pixels_per_line(&self) -> u16 {
match self {
VideoStandard::Ntsc => 720,
VideoStandard::Pal => 720,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FieldPreference {
Field1,
Field2,
Both,
}
pub struct VitcReader {
decoder: decoder::VitcDecoder,
frame_rate: FrameRate,
}
impl VitcReader {
pub fn new(config: VitcReaderConfig) -> Self {
let frame_rate = config.frame_rate;
VitcReader {
decoder: decoder::VitcDecoder::new(config),
frame_rate,
}
}
pub fn process_line(
&mut self,
line_number: u16,
field: u8,
pixels: &[u8],
) -> Result<Option<Timecode>, TimecodeError> {
self.decoder.process_line(line_number, field, pixels)
}
pub fn reset(&mut self) {
self.decoder.reset();
}
pub fn crc_errors(&self) -> u32 {
self.decoder.crc_errors()
}
}
impl TimecodeReader for VitcReader {
fn read_timecode(&mut self) -> Result<Option<Timecode>, TimecodeError> {
Ok(None)
}
fn frame_rate(&self) -> FrameRate {
self.frame_rate
}
fn is_synchronized(&self) -> bool {
self.decoder.is_synchronized()
}
}
#[derive(Debug, Clone)]
pub struct VitcWriterConfig {
pub video_standard: VideoStandard,
pub frame_rate: FrameRate,
pub scan_lines: Vec<u16>,
pub both_fields: bool,
}
impl Default for VitcWriterConfig {
fn default() -> Self {
VitcWriterConfig {
video_standard: VideoStandard::Pal,
frame_rate: FrameRate::Fps25,
scan_lines: vec![19, 21],
both_fields: true,
}
}
}
pub struct VitcWriter {
encoder: encoder::VitcEncoder,
frame_rate: FrameRate,
}
impl VitcWriter {
pub fn new(config: VitcWriterConfig) -> Self {
let frame_rate = config.frame_rate;
VitcWriter {
encoder: encoder::VitcEncoder::new(config),
frame_rate,
}
}
pub fn encode_line(
&mut self,
timecode: &Timecode,
field: u8,
) -> Result<Vec<u8>, TimecodeError> {
self.encoder.encode_line(timecode, field)
}
pub fn reset(&mut self) {
self.encoder.reset();
}
}
impl TimecodeWriter for VitcWriter {
fn write_timecode(&mut self, timecode: &Timecode) -> Result<(), TimecodeError> {
let _pixels_f1 = self.encode_line(timecode, 1)?;
let _pixels_f2 = self.encode_line(timecode, 2)?;
Ok(())
}
fn frame_rate(&self) -> FrameRate {
self.frame_rate
}
fn flush(&mut self) -> Result<(), TimecodeError> {
Ok(())
}
}
pub(crate) mod constants {
pub const BITS_PER_LINE: usize = 90;
pub const DATA_BITS: usize = 82;
pub const SYNC_START_BITS: usize = 2;
#[allow(dead_code)]
pub const SYNC_END_BITS: usize = 6;
pub const PIXELS_PER_BIT: usize = 2;
pub const BLACK_LEVEL: u8 = 16;
pub const WHITE_LEVEL: u8 = 235;
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_vitc_reader_creation() {
let config = VitcReaderConfig::default();
let _reader = VitcReader::new(config);
}
#[test]
fn test_vitc_writer_creation() {
let config = VitcWriterConfig::default();
let _writer = VitcWriter::new(config);
}
#[test]
fn test_video_standard() {
assert_eq!(VideoStandard::Ntsc.total_lines(), 525);
assert_eq!(VideoStandard::Pal.total_lines(), 625);
assert_eq!(VideoStandard::Ntsc.pixels_per_line(), 720);
}
#[test]
fn test_constants() {
assert_eq!(constants::BITS_PER_LINE, 90);
assert_eq!(constants::PIXELS_PER_BIT, 2);
}
}