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