#![allow(dead_code)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PicStruct {
Frame,
TopField,
BottomField,
TopBottomField,
BottomTopField,
TopBottomTopField,
BottomTopBottomField,
FrameDoubling,
FrameTripling,
}
impl PicStruct {
pub fn progressive_frame_count(self) -> u32 {
match self {
Self::Frame
| Self::TopField
| Self::BottomField
| Self::TopBottomField
| Self::BottomTopField
| Self::TopBottomTopField
| Self::BottomTopBottomField => 1,
Self::FrameDoubling => 2,
Self::FrameTripling => 3,
}
}
pub fn is_progressive(self) -> bool {
matches!(
self,
Self::Frame | Self::FrameDoubling | Self::FrameTripling
)
}
pub fn has_repeated_field(self) -> bool {
matches!(self, Self::TopBottomTopField | Self::BottomTopBottomField)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ClockTimestamp {
pub hours: u8,
pub minutes: u8,
pub seconds: u8,
pub n_frames: u32,
pub discontinuity: bool,
pub ct_type: u8,
pub nuit_field_based: bool,
pub counting_type: u8,
}
impl ClockTimestamp {
pub fn to_ms(self) -> u64 {
let h = self.hours as u64;
let m = self.minutes as u64;
let s = self.seconds as u64;
(h * 3600 + m * 60 + s) * 1000
}
pub fn is_discontinuity(self) -> bool {
self.discontinuity
}
}
#[derive(Debug, Clone)]
pub struct PictureTiming {
pub cpb_dpb_delays_present: bool,
pub cpb_removal_delay: u32,
pub dpb_output_delay: u32,
pub pic_struct: Option<PicStruct>,
pub clock_timestamps: Vec<ClockTimestamp>,
pub repeat_first_field: bool,
}
impl PictureTiming {
pub fn new(cpb_removal_delay: u32, dpb_output_delay: u32) -> Self {
Self {
cpb_dpb_delays_present: true,
cpb_removal_delay,
dpb_output_delay,
pic_struct: None,
clock_timestamps: Vec::new(),
repeat_first_field: false,
}
}
pub fn is_repeat_first_field(&self) -> bool {
self.repeat_first_field
}
pub fn pic_struct(&self) -> Option<PicStruct> {
self.pic_struct
}
pub fn timestamp_count(&self) -> usize {
self.clock_timestamps.len()
}
}
#[derive(Debug, Default)]
pub struct PictureTimingParser {
pic_struct_present: bool,
cpb_dpb_delays_present: bool,
}
impl PictureTimingParser {
pub fn new(pic_struct_present: bool, cpb_dpb_delays_present: bool) -> Self {
Self {
pic_struct_present,
cpb_dpb_delays_present,
}
}
pub fn parse(&self, data: &[u8]) -> Option<PictureTiming> {
if data.is_empty() {
return None;
}
let raw_ps = data[0] & 0x0F;
let pic_struct = if self.pic_struct_present {
Some(Self::decode_pic_struct(raw_ps))
} else {
None
};
let (cpb_removal_delay, dpb_output_delay) =
if self.cpb_dpb_delays_present && data.len() >= 5 {
let cpb = u16::from_be_bytes([data[1], data[2]]) as u32;
let dpb = u16::from_be_bytes([data[3], data[4]]) as u32;
(cpb, dpb)
} else {
(0, 0)
};
let repeat_first_field = data.len() >= 6 && (data[5] & 0x01) != 0;
Some(PictureTiming {
cpb_dpb_delays_present: self.cpb_dpb_delays_present,
cpb_removal_delay,
dpb_output_delay,
pic_struct,
clock_timestamps: Vec::new(),
repeat_first_field,
})
}
fn decode_pic_struct(raw: u8) -> PicStruct {
match raw {
1 => PicStruct::TopField,
2 => PicStruct::BottomField,
3 => PicStruct::TopBottomField,
4 => PicStruct::BottomTopField,
5 => PicStruct::TopBottomTopField,
6 => PicStruct::BottomTopBottomField,
7 => PicStruct::FrameDoubling,
8 => PicStruct::FrameTripling,
_ => PicStruct::Frame,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_pic_struct_progressive_frame_count_frame() {
assert_eq!(PicStruct::Frame.progressive_frame_count(), 1);
}
#[test]
fn test_pic_struct_progressive_frame_count_doubling() {
assert_eq!(PicStruct::FrameDoubling.progressive_frame_count(), 2);
}
#[test]
fn test_pic_struct_progressive_frame_count_tripling() {
assert_eq!(PicStruct::FrameTripling.progressive_frame_count(), 3);
}
#[test]
fn test_pic_struct_field_count_is_one() {
assert_eq!(PicStruct::TopField.progressive_frame_count(), 1);
assert_eq!(PicStruct::BottomField.progressive_frame_count(), 1);
}
#[test]
fn test_pic_struct_is_progressive() {
assert!(PicStruct::Frame.is_progressive());
assert!(PicStruct::FrameDoubling.is_progressive());
assert!(!PicStruct::TopField.is_progressive());
}
#[test]
fn test_pic_struct_has_repeated_field() {
assert!(PicStruct::TopBottomTopField.has_repeated_field());
assert!(PicStruct::BottomTopBottomField.has_repeated_field());
assert!(!PicStruct::TopBottomField.has_repeated_field());
}
#[test]
fn test_clock_timestamp_to_ms_zero() {
let ts = ClockTimestamp {
hours: 0,
minutes: 0,
seconds: 0,
n_frames: 0,
discontinuity: false,
ct_type: 0,
nuit_field_based: false,
counting_type: 0,
};
assert_eq!(ts.to_ms(), 0);
}
#[test]
fn test_clock_timestamp_to_ms_one_hour() {
let ts = ClockTimestamp {
hours: 1,
minutes: 0,
seconds: 0,
n_frames: 0,
discontinuity: false,
ct_type: 0,
nuit_field_based: false,
counting_type: 0,
};
assert_eq!(ts.to_ms(), 3_600_000);
}
#[test]
fn test_clock_timestamp_to_ms_mixed() {
let ts = ClockTimestamp {
hours: 1,
minutes: 30,
seconds: 45,
n_frames: 0,
discontinuity: false,
ct_type: 0,
nuit_field_based: false,
counting_type: 0,
};
assert_eq!(ts.to_ms(), 5_445_000);
}
#[test]
fn test_clock_timestamp_is_discontinuity() {
let ts = ClockTimestamp {
hours: 0,
minutes: 0,
seconds: 0,
n_frames: 0,
discontinuity: true,
ct_type: 0,
nuit_field_based: false,
counting_type: 0,
};
assert!(ts.is_discontinuity());
}
#[test]
fn test_picture_timing_new() {
let pt = PictureTiming::new(100, 200);
assert_eq!(pt.cpb_removal_delay, 100);
assert_eq!(pt.dpb_output_delay, 200);
assert!(!pt.is_repeat_first_field());
assert_eq!(pt.timestamp_count(), 0);
}
#[test]
fn test_picture_timing_repeat_first_field() {
let mut pt = PictureTiming::new(0, 0);
pt.repeat_first_field = true;
assert!(pt.is_repeat_first_field());
}
#[test]
fn test_parser_empty_data_returns_none() {
let parser = PictureTimingParser::new(false, false);
assert!(parser.parse(&[]).is_none());
}
#[test]
fn test_parser_frame_pic_struct() {
let parser = PictureTimingParser::new(true, false);
let pt = parser.parse(&[0x00]).expect("parse should succeed");
assert_eq!(pt.pic_struct, Some(PicStruct::Frame));
}
#[test]
fn test_parser_top_field_pic_struct() {
let parser = PictureTimingParser::new(true, false);
let pt = parser.parse(&[0x01]).expect("parse should succeed");
assert_eq!(pt.pic_struct, Some(PicStruct::TopField));
}
#[test]
fn test_parser_cpb_dpb_delays() {
let parser = PictureTimingParser::new(false, true);
let data = [0x00, 0x00, 0x64, 0x00, 0xC8, 0x00];
let pt = parser.parse(&data).expect("parse should succeed");
assert_eq!(pt.cpb_removal_delay, 100);
assert_eq!(pt.dpb_output_delay, 200);
}
#[test]
fn test_parser_repeat_first_field_flag() {
let parser = PictureTimingParser::new(false, true);
let data = [0x00, 0x00, 0x01, 0x00, 0x02, 0x01];
let pt = parser.parse(&data).expect("parse should succeed");
assert!(pt.is_repeat_first_field());
}
#[test]
fn test_parser_no_pic_struct_when_flag_false() {
let parser = PictureTimingParser::new(false, false);
let pt = parser.parse(&[0x07]).expect("parse should succeed");
assert!(pt.pic_struct.is_none());
}
}