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