1use crate::error::{Error, Result};
7use broadcast_common::{Parse, Serialize};
8
9pub const SEGMENT_TYPE: u8 = 0x15;
11pub const HEADER_LEN: usize = 6;
13pub const FIXED_LEN: usize = 2;
15
16#[derive(Debug, Clone, PartialEq, Eq)]
18#[cfg_attr(feature = "serde", derive(serde::Serialize))]
19pub struct DisparityShiftUpdateSequence {
20 pub interval_duration: u32,
22 pub intervals: alloc::vec::Vec<DisparityShiftInterval>,
28}
29
30#[derive(Debug, Clone, PartialEq, Eq)]
32#[cfg_attr(feature = "serde", derive(serde::Serialize))]
33pub struct DisparityShiftInterval {
34 pub interval_count: u8,
36 pub disparity_shift_integer: i8,
38}
39
40impl DisparityShiftUpdateSequence {
41 fn serialized_len(&self) -> usize {
42 1 + 3 + 1 + self.intervals.len() * 2
43 }
44
45 fn serialize_into(&self, buf: &mut [u8]) -> Result<()> {
46 let body_len = 3 + 1 + self.intervals.len() * 2;
47 buf[0] = broadcast_common::len::fit_u8(body_len, "disparity_shift_update_sequence_length")?;
48 broadcast_common::len::fit_bits(
49 u64::from(self.interval_duration),
50 24,
51 "interval_duration",
52 )?;
53 buf[1] = (self.interval_duration >> 16) as u8;
54 buf[2] = (self.interval_duration >> 8) as u8;
55 buf[3] = self.interval_duration as u8;
56 buf[4] = broadcast_common::len::fit_u8(self.intervals.len(), "division_period_count")?;
57 for (i, interval) in self.intervals.iter().enumerate() {
58 let off = 5 + i * 2;
59 buf[off] = interval.interval_count;
60 buf[off + 1] = interval.disparity_shift_integer as u8;
61 }
62 Ok(())
63 }
64
65 fn parse(bytes: &[u8]) -> Result<(Self, usize)> {
66 if bytes.len() < 5 {
67 return Err(Error::BufferTooShort {
68 need: 5,
69 have: bytes.len(),
70 what: "disparity_shift_update_sequence",
71 });
72 }
73 let seq_len = bytes[0] as usize;
74 if bytes.len() < 1 + seq_len {
75 return Err(Error::BufferTooShort {
76 need: 1 + seq_len,
77 have: bytes.len(),
78 what: "disparity_shift_update_sequence data",
79 });
80 }
81 let dur = ((bytes[1] as u32) << 16) | ((bytes[2] as u32) << 8) | (bytes[3] as u32);
82 let count = bytes[4];
83 let total = 1 + seq_len;
84 let mut intervals = alloc::vec::Vec::new();
85 let mut ipos = 5;
86 for _ in 0..count {
87 if ipos + 2 > total {
88 return Err(Error::BufferTooShort {
89 need: ipos + 2,
90 have: total,
91 what: "disparity_shift_update_sequence interval",
92 });
93 }
94 intervals.push(DisparityShiftInterval {
95 interval_count: bytes[ipos],
96 disparity_shift_integer: bytes[ipos + 1] as i8,
97 });
98 ipos += 2;
99 }
100 Ok((
101 DisparityShiftUpdateSequence {
102 interval_duration: dur,
103 intervals,
104 },
105 total,
106 ))
107 }
108}
109
110#[derive(Debug, Clone, PartialEq, Eq)]
112#[cfg_attr(feature = "serde", derive(serde::Serialize))]
113pub struct Subregion {
114 pub subregion_horizontal_position: Option<u16>,
116 pub subregion_width: Option<u16>,
118 pub subregion_disparity_shift_integer: i8,
120 pub subregion_disparity_shift_fractional: u8,
122 pub reserved: u8,
124 pub update_sequence: Option<DisparityShiftUpdateSequence>,
126}
127
128impl Subregion {
129 fn serialized_len(&self) -> usize {
130 let base = if self.subregion_horizontal_position.is_some() {
131 4 + 2
132 } else {
133 2
134 };
135 base + self
136 .update_sequence
137 .as_ref()
138 .map_or(0, |u| u.serialized_len())
139 }
140
141 fn serialize_into(&self, buf: &mut [u8], has_pos: bool) -> Result<()> {
148 let stored_has_pos =
149 self.subregion_horizontal_position.is_some() && self.subregion_width.is_some();
150 if stored_has_pos != has_pos {
151 return Err(Error::SubregionPositionMismatch);
152 }
153 let mut off = 0;
154 if let (Some(h), Some(w)) = (self.subregion_horizontal_position, self.subregion_width) {
155 buf[0..2].copy_from_slice(&h.to_be_bytes());
156 buf[2..4].copy_from_slice(&w.to_be_bytes());
157 off = 4;
158 }
159 buf[off] = self.subregion_disparity_shift_integer as u8;
160 buf[off + 1] = (broadcast_common::len::fit_mask(
161 self.subregion_disparity_shift_fractional,
162 0x0F,
163 "subregion_disparity_shift_fractional",
164 )? << 4)
165 | broadcast_common::len::fit_mask(self.reserved, 0x0F, "reserved")?;
166 off += 2;
167 if let Some(ref seq) = self.update_sequence {
168 seq.serialize_into(&mut buf[off..])?;
169 }
170 Ok(())
171 }
172}
173
174#[derive(Debug, Clone, PartialEq, Eq)]
176#[cfg_attr(feature = "serde", derive(serde::Serialize))]
177pub struct DisparityRegion {
178 pub region_id: u8,
180 pub update_sequence_region_flag: bool,
182 pub reserved_flags: u8,
184 pub subregions: alloc::vec::Vec<Subregion>,
186}
187
188impl DisparityRegion {
189 fn serialized_len(&self) -> usize {
190 2 + self
191 .subregions
192 .iter()
193 .map(|s| s.serialized_len())
194 .sum::<usize>()
195 }
196
197 fn serialize_into(&self, buf: &mut [u8]) -> Result<()> {
198 let count = self.subregions.len();
199 if count == 0 || count > 4 {
203 return Err(Error::InvalidSubregionCount(count));
204 }
205 let num = (count - 1) as u8;
206 buf[0] = self.region_id;
207 buf[1] = (u8::from(self.update_sequence_region_flag) << 7)
208 | broadcast_common::len::fit_mask(self.reserved_flags, 0x7C, "reserved_flags")?
209 | (num & 0x03);
210 let has_pos = count > 1;
214 let mut off = 2;
215 for sub in &self.subregions {
216 sub.serialize_into(&mut buf[off..], has_pos)?;
217 off += sub.serialized_len();
218 }
219 Ok(())
220 }
221}
222
223#[derive(Debug, Clone, PartialEq, Eq)]
225#[cfg_attr(feature = "serde", derive(serde::Serialize))]
226pub struct DisparitySignallingSegment {
227 pub page_id: u16,
229 pub dss_version_number: u8,
231 pub update_sequence_page_flag: bool,
233 pub reserved: u8,
235 pub page_default_disparity_shift: i8,
237 pub page_update_sequence: Option<DisparityShiftUpdateSequence>,
239 pub regions: alloc::vec::Vec<DisparityRegion>,
241}
242
243impl<'a> Parse<'a> for DisparitySignallingSegment {
244 type Error = Error;
245
246 fn parse(bytes: &'a [u8]) -> Result<Self> {
247 if bytes.len() < HEADER_LEN + FIXED_LEN {
248 return Err(Error::BufferTooShort {
249 need: HEADER_LEN + FIXED_LEN,
250 have: bytes.len(),
251 what: "disparity_signalling_segment",
252 });
253 }
254 if bytes[0] != crate::pes_data_field::SYNC_BYTE {
255 return Err(Error::BadSyncByte(bytes[0]));
256 }
257 if bytes[1] != SEGMENT_TYPE {
258 return Err(Error::UnknownSegmentType(bytes[1]));
259 }
260 let page_id = u16::from_be_bytes([bytes[2], bytes[3]]);
261 let segment_length = u16::from_be_bytes([bytes[4], bytes[5]]) as usize;
262 let total = HEADER_LEN + segment_length;
263 if bytes.len() < total {
264 return Err(Error::BufferTooShort {
265 need: total,
266 have: bytes.len(),
267 what: "disparity_signalling_segment data",
268 });
269 }
270 let body = &bytes[HEADER_LEN..HEADER_LEN + segment_length];
271 if body.len() < FIXED_LEN {
272 return Err(Error::BufferTooShort {
273 need: FIXED_LEN,
274 have: body.len(),
275 what: "disparity_signalling_segment body",
276 });
277 }
278 let dss_version_number = body[0] >> 4;
279 let update_sequence_page_flag = (body[0] & 0x08) != 0;
280 let reserved = body[0] & 0x07;
281 let page_default_disparity_shift = body[1] as i8;
282
283 let mut pos = FIXED_LEN;
284 let page_update_sequence = if update_sequence_page_flag {
285 let (seq, seq_len) = DisparityShiftUpdateSequence::parse(&body[pos..])?;
286 pos += seq_len;
287 Some(seq)
288 } else {
289 None
290 };
291
292 let mut regions = alloc::vec::Vec::new();
293 while pos < body.len() {
294 if pos + 2 > body.len() {
299 return Err(Error::BufferTooShort {
300 need: pos + 2,
301 have: body.len(),
302 what: "disparity_signalling_segment region header",
303 });
304 }
305 let region_id = body[pos];
306 pos += 1;
307 let update_flag = (body[pos] & 0x80) != 0;
308 let reserved_flags = body[pos] & 0x7C;
309 let num_subregions_minus_1 = body[pos] & 0x03;
310 pos += 1;
311
312 let mut subregions = alloc::vec::Vec::new();
313 for _n in 0..=num_subregions_minus_1 {
314 let has_pos = num_subregions_minus_1 > 0;
315 let entry_len = if has_pos { 4 + 2 } else { 2 };
316 if pos + entry_len > body.len() {
317 return Err(Error::BufferTooShort {
318 need: pos + entry_len,
319 have: body.len(),
320 what: "subregion entry",
321 });
322 }
323
324 let (hpos, width) = if has_pos {
325 let h = u16::from_be_bytes([body[pos], body[pos + 1]]);
326 let w = u16::from_be_bytes([body[pos + 2], body[pos + 3]]);
327 pos += 4;
328 (Some(h), Some(w))
329 } else {
330 (None, None)
331 };
332
333 let shift_int = body[pos] as i8;
334 let shift_frac = body[pos + 1] >> 4;
335 let reserved = body[pos + 1] & 0x0F;
336 pos += 2;
337
338 let sub_update = if update_flag {
339 let (seq, seq_len) = DisparityShiftUpdateSequence::parse(&body[pos..])?;
340 pos += seq_len;
341 Some(seq)
342 } else {
343 None
344 };
345
346 subregions.push(Subregion {
347 subregion_horizontal_position: hpos,
348 subregion_width: width,
349 subregion_disparity_shift_integer: shift_int,
350 subregion_disparity_shift_fractional: shift_frac,
351 reserved,
352 update_sequence: sub_update,
353 });
354 }
355
356 regions.push(DisparityRegion {
357 region_id,
358 update_sequence_region_flag: update_flag,
359 reserved_flags,
360 subregions,
361 });
362 }
363
364 Ok(DisparitySignallingSegment {
365 page_id,
366 dss_version_number,
367 update_sequence_page_flag,
368 reserved,
369 page_default_disparity_shift,
370 page_update_sequence,
371 regions,
372 })
373 }
374}
375
376impl Serialize for DisparitySignallingSegment {
377 type Error = Error;
378
379 fn serialized_len(&self) -> usize {
380 HEADER_LEN
381 + FIXED_LEN
382 + self
383 .page_update_sequence
384 .as_ref()
385 .map_or(0, |s| s.serialized_len())
386 + self
387 .regions
388 .iter()
389 .map(|r| r.serialized_len())
390 .sum::<usize>()
391 }
392
393 fn serialize_into(&self, buf: &mut [u8]) -> core::result::Result<usize, Self::Error> {
394 let len = self.serialized_len();
395 if buf.len() < len {
396 return Err(Error::BufferTooShort {
397 need: len,
398 have: buf.len(),
399 what: "disparity_signalling_segment serialize",
400 });
401 }
402 buf[0] = 0x0F;
403 buf[1] = SEGMENT_TYPE;
404 buf[2..4].copy_from_slice(&self.page_id.to_be_bytes());
405 let seg_len = crate::segments::check_segment_length(len - HEADER_LEN)?;
406 buf[4..6].copy_from_slice(&seg_len.to_be_bytes());
407
408 buf[6] =
409 (broadcast_common::len::fit_mask(self.dss_version_number, 0x0F, "dss_version_number")?
410 << 4)
411 | (u8::from(self.update_sequence_page_flag) << 3)
412 | broadcast_common::len::fit_mask(self.reserved, 0x07, "reserved")?;
413 buf[7] = self.page_default_disparity_shift as u8;
414
415 let mut off = HEADER_LEN + FIXED_LEN;
416 if let Some(ref seq) = self.page_update_sequence {
417 seq.serialize_into(&mut buf[off..])?;
418 off += seq.serialized_len();
419 }
420 for region in &self.regions {
421 region.serialize_into(&mut buf[off..])?;
422 off += region.serialized_len();
423 }
424 Ok(len)
425 }
426}
427
428#[cfg(test)]
429mod tests {
430 use super::*;
431 use broadcast_common::{Parse, Serialize};
432
433 #[test]
434 fn round_trip_simple() {
435 let bytes = [
436 0x0F, 0x15, 0x00, 0x01, 0x00, 0x06, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
437 ];
438 let seg = DisparitySignallingSegment::parse(&bytes).unwrap();
439 assert_eq!(seg.dss_version_number, 0);
440 assert!(!seg.update_sequence_page_flag);
441 assert_eq!(seg.regions.len(), 1);
442 assert_eq!(seg.regions[0].region_id, 1);
443 assert_eq!(seg.regions[0].subregions.len(), 1);
444 let out = seg.try_to_bytes().unwrap();
445 assert_eq!(out, bytes);
446
447 let mut seg2 = seg.clone();
449 seg2.regions[0].region_id = 5;
450 let out2 = seg2.try_to_bytes().unwrap();
451 assert_ne!(out2, bytes);
452 let reparse = DisparitySignallingSegment::parse(&out2).unwrap();
453 assert_eq!(reparse.regions[0].region_id, 5);
454 }
455
456 #[test]
458 fn interval_duration_over_24_bits_is_rejected() {
459 let mut seq = DisparityShiftUpdateSequence {
460 interval_duration: 0x0100_0000,
461 intervals: alloc::vec![],
462 };
463 let mut buf = [0u8; 16];
464 assert!(matches!(
465 seq.serialize_into(&mut buf),
466 Err(Error::FieldOverflow(_))
467 ));
468 seq.interval_duration = 0x00FF_FFFF;
469 assert!(seq.serialize_into(&mut buf).is_ok());
470 assert_eq!(&buf[1..4], &[0xFF, 0xFF, 0xFF]);
471 }
472
473 #[test]
475 fn rejects_bad_sync_byte() {
476 let bytes = [
477 0x00, 0x15, 0x00, 0x01, 0x00, 0x06, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
478 ];
479 assert!(matches!(
480 DisparitySignallingSegment::parse(&bytes),
481 Err(Error::BadSyncByte(0x00))
482 ));
483 }
484
485 #[test]
489 fn rejects_trailing_partial_region() {
490 let bytes = [
491 0x0F, 0x15, 0x00, 0x01, 0x00, 0x07, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xFF,
492 ];
493 assert!(matches!(
494 DisparitySignallingSegment::parse(&bytes),
495 Err(Error::BufferTooShort { .. })
496 ));
497 }
498
499 #[test]
500 fn round_trip_with_page_update_seq() {
501 let bytes = [
502 0x0F, 0x15, 0x00, 0x01, 0x00, 0x0B, 0x88, 0x05, 0x08, 0x00, 0x00, 0x0A, 0x02, 0x01,
503 0x10, 0x03, 0x20,
504 ];
505 let seg = DisparitySignallingSegment::parse(&bytes).unwrap();
506 assert_eq!(seg.dss_version_number, 8);
507 assert!(seg.update_sequence_page_flag);
508 assert_eq!(seg.regions.len(), 0);
509 let out = seg.try_to_bytes().unwrap();
510 assert_eq!(out, bytes);
511
512 let mut seg2 = seg.clone();
514 seg2.page_default_disparity_shift = 10;
515 let out2 = seg2.try_to_bytes().unwrap();
516 assert_ne!(out2, bytes);
517 let reparse = DisparitySignallingSegment::parse(&out2).unwrap();
518 assert_eq!(reparse.page_default_disparity_shift, 10);
519 }
520
521 #[test]
522 fn round_trip_with_update_sequence() {
523 let bytes = [
524 0x0F, 0x15, 0x00, 0x01, 0x00, 0x16, 0x88, 0x05, 0x08, 0x00, 0x00, 0x0A, 0x02, 0x01,
525 0x10, 0x03, 0x20, 0x01, 0x80, 0x10, 0x00, 0x06, 0x00, 0x00, 0x14, 0x01, 0x01, 0x08,
526 ];
527 let seg = DisparitySignallingSegment::parse(&bytes).unwrap();
528 assert_eq!(seg.dss_version_number, 8);
529 assert!(seg.update_sequence_page_flag);
530 assert_eq!(seg.page_default_disparity_shift, 5);
531 let seq = seg.page_update_sequence.as_ref().unwrap();
532 assert_eq!(seq.intervals.len(), 2);
533 assert_eq!(seg.regions.len(), 1);
534 assert_eq!(seg.regions[0].region_id, 1);
535 assert!(seg.regions[0].update_sequence_region_flag);
536 assert_eq!(seg.regions[0].subregions.len(), 1);
537 let out = seg.try_to_bytes().unwrap();
538 assert_eq!(out, bytes);
539
540 let mut seg2 = seg.clone();
542 seg2.dss_version_number = 3;
543 let out2 = seg2.try_to_bytes().unwrap();
544 assert_ne!(out2, bytes);
545 let reparse = DisparitySignallingSegment::parse(&out2).unwrap();
546 assert_eq!(reparse.dss_version_number, 3);
547 }
548
549 #[test]
553 fn division_period_count_tracks_intervals_len() {
554 let bytes = [
555 0x0F, 0x15, 0x00, 0x01, 0x00, 0x0B, 0x88, 0x05, 0x08, 0x00, 0x00, 0x0A, 0x02, 0x01,
556 0x10, 0x03, 0x20,
557 ];
558 let seg = DisparitySignallingSegment::parse(&bytes).unwrap();
559 let mut seg2 = seg.clone();
560 seg2.page_update_sequence
561 .as_mut()
562 .unwrap()
563 .intervals
564 .push(DisparityShiftInterval {
565 interval_count: 5,
566 disparity_shift_integer: -1,
567 });
568 let out2 = seg2.try_to_bytes().unwrap();
569 assert_ne!(out2, bytes);
570 let seq_off = HEADER_LEN + FIXED_LEN;
573 assert_eq!(
574 out2[seq_off + 4],
575 3,
576 "division_period_count must track intervals.len()"
577 );
578 let reparse = DisparitySignallingSegment::parse(&out2).unwrap();
579 assert_eq!(reparse.page_update_sequence.unwrap().intervals.len(), 3);
580 }
581
582 #[test]
586 fn rejects_out_of_range_subregion_count() {
587 let region = DisparityRegion {
588 region_id: 1,
589 update_sequence_region_flag: false,
590 reserved_flags: 0,
591 subregions: alloc::vec::Vec::new(),
592 };
593 let mut buf = [0u8; 16];
594 assert!(matches!(
595 region.serialize_into(&mut buf),
596 Err(Error::InvalidSubregionCount(0))
597 ));
598
599 let sub = Subregion {
600 subregion_horizontal_position: None,
601 subregion_width: None,
602 subregion_disparity_shift_integer: 0,
603 subregion_disparity_shift_fractional: 0,
604 reserved: 0,
605 update_sequence: None,
606 };
607 let region5 = DisparityRegion {
608 region_id: 1,
609 update_sequence_region_flag: false,
610 reserved_flags: 0,
611 subregions: alloc::vec![sub; 5],
612 };
613 let mut buf = [0u8; 64];
614 assert!(matches!(
615 region5.serialize_into(&mut buf),
616 Err(Error::InvalidSubregionCount(5))
617 ));
618 }
619
620 #[test]
624 fn rejects_inconsistent_subregion_position() {
625 let with_pos = Subregion {
626 subregion_horizontal_position: Some(10),
627 subregion_width: Some(20),
628 subregion_disparity_shift_integer: 0,
629 subregion_disparity_shift_fractional: 0,
630 reserved: 0,
631 update_sequence: None,
632 };
633 let without_pos = Subregion {
634 subregion_horizontal_position: None,
635 subregion_width: None,
636 subregion_disparity_shift_integer: 0,
637 subregion_disparity_shift_fractional: 0,
638 reserved: 0,
639 update_sequence: None,
640 };
641 let region = DisparityRegion {
642 region_id: 1,
643 update_sequence_region_flag: false,
644 reserved_flags: 0,
645 subregions: alloc::vec![with_pos, without_pos],
646 };
647 let mut buf = [0u8; 32];
648 assert!(matches!(
649 region.serialize_into(&mut buf),
650 Err(Error::SubregionPositionMismatch)
651 ));
652 }
653}