1#![allow(dead_code)]
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum PicStruct {
12 Frame,
14 TopField,
16 BottomField,
18 TopBottomField,
20 BottomTopField,
22 TopBottomTopField,
24 BottomTopBottomField,
26 FrameDoubling,
28 FrameTripling,
30}
31
32impl PicStruct {
33 pub fn progressive_frame_count(self) -> u32 {
37 match self {
38 Self::Frame
39 | Self::TopField
40 | Self::BottomField
41 | Self::TopBottomField
42 | Self::BottomTopField
43 | Self::TopBottomTopField
44 | Self::BottomTopBottomField => 1,
45 Self::FrameDoubling => 2,
46 Self::FrameTripling => 3,
47 }
48 }
49
50 pub fn is_progressive(self) -> bool {
52 matches!(
53 self,
54 Self::Frame | Self::FrameDoubling | Self::FrameTripling
55 )
56 }
57
58 pub fn has_repeated_field(self) -> bool {
60 matches!(self, Self::TopBottomTopField | Self::BottomTopBottomField)
61 }
62}
63
64#[derive(Debug, Clone, Copy, PartialEq, Eq)]
66pub struct ClockTimestamp {
67 pub hours: u8,
69 pub minutes: u8,
71 pub seconds: u8,
73 pub n_frames: u32,
75 pub discontinuity: bool,
77 pub ct_type: u8,
79 pub nuit_field_based: bool,
81 pub counting_type: u8,
83}
84
85impl ClockTimestamp {
86 pub fn to_ms(self) -> u64 {
90 let h = self.hours as u64;
91 let m = self.minutes as u64;
92 let s = self.seconds as u64;
93 (h * 3600 + m * 60 + s) * 1000
94 }
95
96 pub fn is_discontinuity(self) -> bool {
98 self.discontinuity
99 }
100}
101
102#[derive(Debug, Clone)]
104pub struct PictureTiming {
105 pub cpb_dpb_delays_present: bool,
107 pub cpb_removal_delay: u32,
109 pub dpb_output_delay: u32,
111 pub pic_struct: Option<PicStruct>,
113 pub clock_timestamps: Vec<ClockTimestamp>,
115 pub repeat_first_field: bool,
117}
118
119impl PictureTiming {
120 pub fn new(cpb_removal_delay: u32, dpb_output_delay: u32) -> Self {
122 Self {
123 cpb_dpb_delays_present: true,
124 cpb_removal_delay,
125 dpb_output_delay,
126 pic_struct: None,
127 clock_timestamps: Vec::new(),
128 repeat_first_field: false,
129 }
130 }
131
132 pub fn is_repeat_first_field(&self) -> bool {
134 self.repeat_first_field
135 }
136
137 pub fn pic_struct(&self) -> Option<PicStruct> {
139 self.pic_struct
140 }
141
142 pub fn timestamp_count(&self) -> usize {
144 self.clock_timestamps.len()
145 }
146}
147
148#[derive(Debug, Default)]
150pub struct PictureTimingParser {
151 pic_struct_present: bool,
152 cpb_dpb_delays_present: bool,
153}
154
155impl PictureTimingParser {
156 pub fn new(pic_struct_present: bool, cpb_dpb_delays_present: bool) -> Self {
161 Self {
162 pic_struct_present,
163 cpb_dpb_delays_present,
164 }
165 }
166
167 pub fn parse(&self, data: &[u8]) -> Option<PictureTiming> {
171 if data.is_empty() {
172 return None;
173 }
174 let raw_ps = data[0] & 0x0F;
176 let pic_struct = if self.pic_struct_present {
177 Some(Self::decode_pic_struct(raw_ps))
178 } else {
179 None
180 };
181
182 let (cpb_removal_delay, dpb_output_delay) =
183 if self.cpb_dpb_delays_present && data.len() >= 5 {
184 let cpb = u16::from_be_bytes([data[1], data[2]]) as u32;
185 let dpb = u16::from_be_bytes([data[3], data[4]]) as u32;
186 (cpb, dpb)
187 } else {
188 (0, 0)
189 };
190
191 let repeat_first_field = data.len() >= 6 && (data[5] & 0x01) != 0;
192
193 Some(PictureTiming {
194 cpb_dpb_delays_present: self.cpb_dpb_delays_present,
195 cpb_removal_delay,
196 dpb_output_delay,
197 pic_struct,
198 clock_timestamps: Vec::new(),
199 repeat_first_field,
200 })
201 }
202
203 fn decode_pic_struct(raw: u8) -> PicStruct {
204 match raw {
205 1 => PicStruct::TopField,
206 2 => PicStruct::BottomField,
207 3 => PicStruct::TopBottomField,
208 4 => PicStruct::BottomTopField,
209 5 => PicStruct::TopBottomTopField,
210 6 => PicStruct::BottomTopBottomField,
211 7 => PicStruct::FrameDoubling,
212 8 => PicStruct::FrameTripling,
213 _ => PicStruct::Frame,
214 }
215 }
216}
217
218#[cfg(test)]
219mod tests {
220 use super::*;
221
222 #[test]
223 fn test_pic_struct_progressive_frame_count_frame() {
224 assert_eq!(PicStruct::Frame.progressive_frame_count(), 1);
225 }
226
227 #[test]
228 fn test_pic_struct_progressive_frame_count_doubling() {
229 assert_eq!(PicStruct::FrameDoubling.progressive_frame_count(), 2);
230 }
231
232 #[test]
233 fn test_pic_struct_progressive_frame_count_tripling() {
234 assert_eq!(PicStruct::FrameTripling.progressive_frame_count(), 3);
235 }
236
237 #[test]
238 fn test_pic_struct_field_count_is_one() {
239 assert_eq!(PicStruct::TopField.progressive_frame_count(), 1);
240 assert_eq!(PicStruct::BottomField.progressive_frame_count(), 1);
241 }
242
243 #[test]
244 fn test_pic_struct_is_progressive() {
245 assert!(PicStruct::Frame.is_progressive());
246 assert!(PicStruct::FrameDoubling.is_progressive());
247 assert!(!PicStruct::TopField.is_progressive());
248 }
249
250 #[test]
251 fn test_pic_struct_has_repeated_field() {
252 assert!(PicStruct::TopBottomTopField.has_repeated_field());
253 assert!(PicStruct::BottomTopBottomField.has_repeated_field());
254 assert!(!PicStruct::TopBottomField.has_repeated_field());
255 }
256
257 #[test]
258 fn test_clock_timestamp_to_ms_zero() {
259 let ts = ClockTimestamp {
260 hours: 0,
261 minutes: 0,
262 seconds: 0,
263 n_frames: 0,
264 discontinuity: false,
265 ct_type: 0,
266 nuit_field_based: false,
267 counting_type: 0,
268 };
269 assert_eq!(ts.to_ms(), 0);
270 }
271
272 #[test]
273 fn test_clock_timestamp_to_ms_one_hour() {
274 let ts = ClockTimestamp {
275 hours: 1,
276 minutes: 0,
277 seconds: 0,
278 n_frames: 0,
279 discontinuity: false,
280 ct_type: 0,
281 nuit_field_based: false,
282 counting_type: 0,
283 };
284 assert_eq!(ts.to_ms(), 3_600_000);
285 }
286
287 #[test]
288 fn test_clock_timestamp_to_ms_mixed() {
289 let ts = ClockTimestamp {
290 hours: 1,
291 minutes: 30,
292 seconds: 45,
293 n_frames: 0,
294 discontinuity: false,
295 ct_type: 0,
296 nuit_field_based: false,
297 counting_type: 0,
298 };
299 assert_eq!(ts.to_ms(), 5_445_000);
301 }
302
303 #[test]
304 fn test_clock_timestamp_is_discontinuity() {
305 let ts = ClockTimestamp {
306 hours: 0,
307 minutes: 0,
308 seconds: 0,
309 n_frames: 0,
310 discontinuity: true,
311 ct_type: 0,
312 nuit_field_based: false,
313 counting_type: 0,
314 };
315 assert!(ts.is_discontinuity());
316 }
317
318 #[test]
319 fn test_picture_timing_new() {
320 let pt = PictureTiming::new(100, 200);
321 assert_eq!(pt.cpb_removal_delay, 100);
322 assert_eq!(pt.dpb_output_delay, 200);
323 assert!(!pt.is_repeat_first_field());
324 assert_eq!(pt.timestamp_count(), 0);
325 }
326
327 #[test]
328 fn test_picture_timing_repeat_first_field() {
329 let mut pt = PictureTiming::new(0, 0);
330 pt.repeat_first_field = true;
331 assert!(pt.is_repeat_first_field());
332 }
333
334 #[test]
335 fn test_parser_empty_data_returns_none() {
336 let parser = PictureTimingParser::new(false, false);
337 assert!(parser.parse(&[]).is_none());
338 }
339
340 #[test]
341 fn test_parser_frame_pic_struct() {
342 let parser = PictureTimingParser::new(true, false);
343 let pt = parser.parse(&[0x00]).expect("parse should succeed");
345 assert_eq!(pt.pic_struct, Some(PicStruct::Frame));
346 }
347
348 #[test]
349 fn test_parser_top_field_pic_struct() {
350 let parser = PictureTimingParser::new(true, false);
351 let pt = parser.parse(&[0x01]).expect("parse should succeed");
352 assert_eq!(pt.pic_struct, Some(PicStruct::TopField));
353 }
354
355 #[test]
356 fn test_parser_cpb_dpb_delays() {
357 let parser = PictureTimingParser::new(false, true);
358 let data = [0x00, 0x00, 0x64, 0x00, 0xC8, 0x00];
360 let pt = parser.parse(&data).expect("parse should succeed");
361 assert_eq!(pt.cpb_removal_delay, 100);
362 assert_eq!(pt.dpb_output_delay, 200);
363 }
364
365 #[test]
366 fn test_parser_repeat_first_field_flag() {
367 let parser = PictureTimingParser::new(false, true);
368 let data = [0x00, 0x00, 0x01, 0x00, 0x02, 0x01];
369 let pt = parser.parse(&data).expect("parse should succeed");
370 assert!(pt.is_repeat_first_field());
371 }
372
373 #[test]
374 fn test_parser_no_pic_struct_when_flag_false() {
375 let parser = PictureTimingParser::new(false, false);
376 let pt = parser.parse(&[0x07]).expect("parse should succeed");
377 assert!(pt.pic_struct.is_none());
378 }
379}