1use crate::error::{Error, Result};
4
5pub const TS_PACKET_SIZE: usize = 188;
7pub const TS_SYNC_BYTE: u8 = 0x47;
9const MAX_SECTION_SIZE: usize = 4098;
14
15const TEI_MASK: u8 = 0x80;
17const PUSI_MASK: u8 = 0x40;
19pub const PID_MASK_HI: u8 = 0x1F;
21pub const SCRAMBLING_MASK: u8 = 0xC0;
23pub const ADAPTATION_FLAG: u8 = 0x20;
25pub const PAYLOAD_FLAG: u8 = 0x10;
27pub const CC_MASK: u8 = 0x0F;
29
30#[derive(Clone, Debug, PartialEq, Eq)]
32#[cfg_attr(feature = "serde", derive(serde::Serialize))]
33pub struct TsHeader {
34 pub tei: bool,
37 pub pusi: bool,
40 pub pid: u16,
42 pub scrambling: u8,
44 pub has_adaptation: bool,
46 pub has_payload: bool,
48 pub continuity_counter: u8,
50}
51
52#[derive(Clone, Debug)]
58#[cfg_attr(feature = "serde", derive(serde::Serialize))]
59pub struct TsPacket<'a> {
60 pub header: TsHeader,
62 pub payload: Option<&'a [u8]>,
65 #[cfg_attr(feature = "serde", serde(skip))]
68 adaptation: Option<&'a [u8]>,
69 #[cfg_attr(feature = "serde", serde(skip))]
71 pub raw: &'a [u8; TS_PACKET_SIZE],
72}
73
74impl TsHeader {
75 pub fn parse(raw4: &[u8]) -> Result<Self> {
77 if raw4.len() < 4 {
78 return Err(Error::BufferTooShort {
79 need: 4,
80 have: raw4.len(),
81 what: "TsHeader",
82 });
83 }
84 let b1 = raw4[1];
85 let b2 = raw4[2];
86 let b3 = raw4[3];
87
88 let tei = (b1 & TEI_MASK) != 0;
89 let pusi = (b1 & PUSI_MASK) != 0;
90 let pid = (((b1 & PID_MASK_HI) as u16) << 8) | (b2 as u16);
91 let scrambling = (b3 & SCRAMBLING_MASK) >> 6;
92 let has_adaptation = (b3 & ADAPTATION_FLAG) != 0;
93 let has_payload = (b3 & PAYLOAD_FLAG) != 0;
94 let continuity_counter = b3 & CC_MASK;
95
96 Ok(Self {
97 tei,
98 pusi,
99 pid,
100 scrambling,
101 has_adaptation,
102 has_payload,
103 continuity_counter,
104 })
105 }
106
107 pub const fn serialized_len() -> usize {
109 4
110 }
111
112 pub fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
114 if buf.len() < 4 {
115 return Err(Error::OutputBufferTooSmall {
116 need: 4,
117 have: buf.len(),
118 });
119 }
120 buf[0] = TS_SYNC_BYTE;
121 buf[1] = 0;
122 if self.tei {
123 buf[1] |= TEI_MASK;
124 }
125 if self.pusi {
126 buf[1] |= PUSI_MASK;
127 }
128 buf[1] |= ((self.pid >> 8) as u8) & PID_MASK_HI;
129 buf[2] = (self.pid & 0xFF) as u8;
130 buf[3] = (self.scrambling << 6) & SCRAMBLING_MASK;
131 if self.has_adaptation {
132 buf[3] |= ADAPTATION_FLAG;
133 }
134 if self.has_payload {
135 buf[3] |= PAYLOAD_FLAG;
136 }
137 buf[3] |= self.continuity_counter & CC_MASK;
138 Ok(4)
139 }
140}
141
142impl<'a> dvb_common::Parse<'a> for TsHeader {
143 type Error = Error;
144
145 fn parse(bytes: &'a [u8]) -> Result<Self> {
146 TsHeader::parse(bytes)
147 }
148}
149
150impl dvb_common::Serialize for TsHeader {
151 type Error = Error;
152
153 fn serialized_len(&self) -> usize {
154 TsHeader::serialized_len()
155 }
156
157 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
158 TsHeader::serialize_into(self, buf)
159 }
160}
161
162impl<'a> TsPacket<'a> {
163 pub fn parse(buf: &'a [u8]) -> Result<Self> {
169 if buf.len() < TS_PACKET_SIZE {
170 return Err(Error::BufferTooShort {
171 need: TS_PACKET_SIZE,
172 have: buf.len(),
173 what: "TsPacket",
174 });
175 }
176 if buf[0] != TS_SYNC_BYTE {
177 return Err(Error::InvalidSyncByte { found: buf[0] });
178 }
179
180 let raw: &[u8; TS_PACKET_SIZE] =
181 buf[..TS_PACKET_SIZE]
182 .try_into()
183 .map_err(|_| Error::BufferTooShort {
184 need: TS_PACKET_SIZE,
185 have: buf.len(),
186 what: "TsPacket::parse (array conversion)",
187 })?;
188
189 let header = TsHeader::parse(&raw[..4])?;
190
191 let mut cursor = 4usize;
192 let mut payload = None;
193 let mut adaptation = None;
194
195 if header.has_adaptation && cursor < TS_PACKET_SIZE {
198 let af_len = raw[cursor] as usize;
199 let af_start = cursor + 1;
200 if af_len > 0 && af_start < TS_PACKET_SIZE {
201 let af_end = (af_start + af_len).min(TS_PACKET_SIZE);
202 adaptation = Some(&raw[af_start..af_end]);
203 }
204 cursor += 1 + af_len;
205 }
206
207 if header.has_payload && cursor < TS_PACKET_SIZE {
208 payload = Some(&raw[cursor..]);
209 }
210
211 Ok(TsPacket {
212 header,
213 payload,
214 adaptation,
215 raw,
216 })
217 }
218
219 pub fn adaptation_field(&self) -> Option<crate::Result<AdaptationField>> {
225 self.adaptation.map(AdaptationField::parse)
226 }
227}
228
229const AF_DISCONTINUITY: u8 = 0x80;
231const AF_RANDOM_ACCESS: u8 = 0x40;
232const AF_ES_PRIORITY: u8 = 0x20;
233const AF_PCR_FLAG: u8 = 0x10;
234const AF_OPCR_FLAG: u8 = 0x08;
235const AF_SPLICING_FLAG: u8 = 0x04;
236const PCR_FIELD_LEN: usize = 6;
238
239#[derive(Clone, Copy, Debug, PartialEq, Eq)]
242#[cfg_attr(feature = "serde", derive(serde::Serialize))]
243pub struct Pcr {
244 pub base: u64,
246 pub extension: u16,
248}
249
250impl Pcr {
251 #[must_use]
253 pub fn as_27mhz(self) -> u64 {
254 self.base * 300 + self.extension as u64
255 }
256
257 fn parse(af: &[u8], at: usize) -> Result<Self> {
259 let b: &[u8; PCR_FIELD_LEN] = af
260 .get(at..at + PCR_FIELD_LEN)
261 .and_then(|s| s.try_into().ok())
262 .ok_or(Error::BufferTooShort {
263 need: at + PCR_FIELD_LEN,
264 have: af.len(),
265 what: "adaptation_field PCR",
266 })?;
267 let base = ((b[0] as u64) << 25)
268 | ((b[1] as u64) << 17)
269 | ((b[2] as u64) << 9)
270 | ((b[3] as u64) << 1)
271 | ((b[4] as u64) >> 7);
272 let extension = (((b[4] & 0x01) as u16) << 8) | (b[5] as u16);
273 Ok(Self { base, extension })
274 }
275}
276
277#[non_exhaustive]
282#[derive(Clone, Copy, Debug, PartialEq, Eq)]
283#[cfg_attr(feature = "serde", derive(serde::Serialize))]
284pub struct AdaptationField {
285 pub discontinuity_indicator: bool,
287 pub random_access_indicator: bool,
289 pub elementary_stream_priority_indicator: bool,
291 pub pcr: Option<Pcr>,
293 pub opcr: Option<Pcr>,
295 pub splice_countdown: Option<i8>,
297}
298
299impl AdaptationField {
300 fn parse(af: &[u8]) -> Result<Self> {
302 let flags = *af.first().ok_or(Error::BufferTooShort {
303 need: 1,
304 have: 0,
305 what: "adaptation_field flags",
306 })?;
307 let mut cursor = 1usize;
308
309 let pcr = if flags & AF_PCR_FLAG != 0 {
310 let p = Pcr::parse(af, cursor)?;
311 cursor += PCR_FIELD_LEN;
312 Some(p)
313 } else {
314 None
315 };
316 let opcr = if flags & AF_OPCR_FLAG != 0 {
317 let p = Pcr::parse(af, cursor)?;
318 cursor += PCR_FIELD_LEN;
319 Some(p)
320 } else {
321 None
322 };
323 let splice_countdown = if flags & AF_SPLICING_FLAG != 0 {
324 let b = *af.get(cursor).ok_or(Error::BufferTooShort {
325 need: cursor + 1,
326 have: af.len(),
327 what: "adaptation_field splice_countdown",
328 })?;
329 Some(b as i8)
330 } else {
331 None
332 };
333
334 Ok(AdaptationField {
335 discontinuity_indicator: flags & AF_DISCONTINUITY != 0,
336 random_access_indicator: flags & AF_RANDOM_ACCESS != 0,
337 elementary_stream_priority_indicator: flags & AF_ES_PRIORITY != 0,
338 pcr,
339 opcr,
340 splice_countdown,
341 })
342 }
343}
344
345#[derive(Default)]
350pub struct SectionReassembler {
351 buf: bytes::BytesMut,
352 ready: std::collections::VecDeque<bytes::Bytes>,
353}
354
355impl SectionReassembler {
356 pub fn feed(&mut self, payload: &[u8], pusi: bool) {
364 if pusi {
365 if payload.is_empty() {
368 self.buf.clear();
369 return;
370 }
371 let pointer = payload[0] as usize;
372
373 if !self.buf.is_empty() && pointer > 0 {
381 let avail = payload.len() - 1;
382 let tail_len = pointer.min(avail);
383 if self.buf.len() + tail_len > MAX_SECTION_SIZE {
384 self.buf.clear();
385 } else {
386 self.buf.extend_from_slice(&payload[1..1 + tail_len]);
387 self.drain_complete_sections();
388 }
389 }
390
391 self.buf.clear();
394
395 let start = 1 + pointer;
396 if start >= payload.len() {
397 return;
399 }
400 let new_data = &payload[start..];
401 if new_data.len() > MAX_SECTION_SIZE {
402 return;
403 }
404 self.buf.extend_from_slice(new_data);
405 } else {
406 if self.buf.is_empty() {
407 return;
408 }
409 if self.buf.len() + payload.len() > MAX_SECTION_SIZE {
410 self.buf.clear();
411 return;
412 }
413 self.buf.extend_from_slice(payload);
414 }
415
416 self.drain_complete_sections();
417 }
418
419 fn drain_complete_sections(&mut self) {
430 loop {
431 if self.buf.len() < 3 {
432 break;
435 }
436 if self.buf[0] == 0xFF {
437 self.buf.clear();
439 break;
440 }
441 let exp = 3 + (((self.buf[1] & 0x0F) as usize) << 8 | self.buf[2] as usize);
442 if self.buf.len() >= exp {
443 let section = self.buf.split_to(exp).freeze();
446 self.ready.push_back(section);
447 } else {
448 break;
450 }
451 }
452 }
453
454 pub fn pop_section(&mut self) -> Option<bytes::Bytes> {
456 self.ready.pop_front()
457 }
458
459 pub fn len(&self) -> usize {
461 self.buf.len()
462 }
463
464 pub fn is_empty(&self) -> bool {
466 self.buf.is_empty()
467 }
468}
469
470#[cfg(test)]
471mod tests {
472 use super::*;
473
474 fn make_packet(b1: u8, b2: u8, b3: u8, payload_data: &[u8]) -> [u8; TS_PACKET_SIZE] {
476 let mut pkt = [0u8; TS_PACKET_SIZE];
477 pkt[0] = TS_SYNC_BYTE;
478 pkt[1] = b1;
479 pkt[2] = b2;
480 pkt[3] = b3;
481 let payload_start = 4;
482 let end = (payload_start + payload_data.len()).min(TS_PACKET_SIZE);
483 let len = (end - payload_start).min(payload_data.len());
484 pkt[payload_start..payload_start + len].copy_from_slice(&payload_data[..len]);
485 pkt
486 }
487
488 #[test]
489 fn parse_rejects_non_0x47_sync_byte() {
490 let mut pkt = [0u8; TS_PACKET_SIZE];
491 pkt[0] = 0x46; let err = TsPacket::parse(&pkt).unwrap_err();
493 match err {
494 Error::InvalidSyncByte { found } => assert_eq!(found, 0x46),
495 other => panic!("expected InvalidSyncByte, got {other:?}"),
496 }
497 }
498
499 #[test]
500 fn parse_extracts_pid_and_continuity_counter() {
501 let pkt = make_packet(0x12, 0x34, 0x05, &[]);
507 let pkt = TsPacket::parse(&pkt).unwrap();
508 assert_eq!(pkt.header.pid, 0x1234);
509 assert_eq!(pkt.header.continuity_counter, 5);
510 }
511
512 #[test]
513 fn payload_unit_start_indicator_flag_extracted() {
514 let pkt1 = make_packet(0x40, 0x00, 0x00, &[]);
516 let pkt1 = TsPacket::parse(&pkt1).unwrap();
517 assert!(pkt1.header.pusi);
518
519 let pkt2 = make_packet(0x00, 0x00, 0x00, &[]);
521 let pkt2 = TsPacket::parse(&pkt2).unwrap();
522 assert!(!pkt2.header.pusi);
523 }
524
525 fn build_pusi_payload(pointer_field: u8, previous_tail: &[u8], section: &[u8]) -> Vec<u8> {
530 assert_eq!(pointer_field as usize, previous_tail.len());
531 let mut v = Vec::with_capacity(1 + previous_tail.len() + section.len());
532 v.push(pointer_field);
533 v.extend_from_slice(previous_tail);
534 v.extend_from_slice(section);
535 v
536 }
537
538 fn build_section(table_id: u8, body_after_length: &[u8]) -> Vec<u8> {
542 let section_length = body_after_length.len() as u16;
543 let mut v = Vec::with_capacity(3 + section_length as usize);
544 v.push(table_id);
545 v.push(0xB0 | ((section_length >> 8) as u8 & 0x0F));
547 v.push((section_length & 0xFF) as u8);
548 v.extend_from_slice(body_after_length);
549 v
550 }
551
552 #[test]
558 fn reassembler_accumulates_multi_packet_section() {
559 let body = vec![0xAAu8; 197];
561 let section = build_section(0x02, &body);
562 assert_eq!(section.len(), 200);
563
564 let first_chunk = 100;
565 let payload1 = build_pusi_payload(0, &[], §ion[..first_chunk]);
566 let payload2 = section[first_chunk..].to_vec();
567
568 let mut reasm = SectionReassembler::default();
569 reasm.feed(&payload1, true);
570 reasm.feed(&payload2, false);
571
572 let out = reasm.pop_section().expect("section should be ready");
573 assert_eq!(out.len(), 200);
574 assert_eq!(out.as_ref(), §ion[..]);
575 }
576
577 #[test]
578 fn reassembler_yields_complete_section_once_length_satisfied() {
579 let section = build_section(0x42, &[0xAA]);
581 assert_eq!(section.len(), 4);
582 let payload = build_pusi_payload(0, &[], §ion);
583
584 let mut reasm = SectionReassembler::default();
585 reasm.feed(&payload, true);
586
587 let out = reasm
588 .pop_section()
589 .expect("single-packet section should pop");
590 assert_eq!(out.as_ref(), §ion[..]);
591 }
592
593 #[test]
594 fn reassembler_extracts_all_concatenated_sections_in_one_payload() {
595 let s1 = build_section(0x42, &[0x11, 0x22]); let s2 = build_section(0x46, &[0x33]); let s3 = build_section(0x4A, &[0x44, 0x55, 0x66]); let mut concat = Vec::new();
604 concat.extend_from_slice(&s1);
605 concat.extend_from_slice(&s2);
606 concat.extend_from_slice(&s3);
607 let payload = build_pusi_payload(0, &[], &concat);
608
609 let mut reasm = SectionReassembler::default();
610 reasm.feed(&payload, true);
611
612 let got: Vec<_> = std::iter::from_fn(|| reasm.pop_section()).collect();
614 assert_eq!(got.len(), 3, "all three concatenated sections must pop");
615 assert_eq!(got[0].as_ref(), &s1[..]);
616 assert_eq!(got[1].as_ref(), &s2[..]);
617 assert_eq!(got[2].as_ref(), &s3[..]);
618 }
619
620 #[test]
621 fn reassembler_stops_at_stuffing_after_concatenated_sections() {
622 let s1 = build_section(0x42, &[0xAA]); let s2 = build_section(0x46, &[0xBB, 0xCC]); let mut concat = Vec::new();
628 concat.extend_from_slice(&s1);
629 concat.extend_from_slice(&s2);
630 concat.extend_from_slice(&[0xFF, 0xFF, 0xFF, 0xFF]); let payload = build_pusi_payload(0, &[], &concat);
632
633 let mut reasm = SectionReassembler::default();
634 reasm.feed(&payload, true);
635
636 let got: Vec<_> = std::iter::from_fn(|| reasm.pop_section()).collect();
637 assert_eq!(got.len(), 2);
638 assert_eq!(got[0].as_ref(), &s1[..]);
639 assert_eq!(got[1].as_ref(), &s2[..]);
640 assert!(
641 reasm.is_empty(),
642 "stuffing tail must be discarded, not buffered"
643 );
644 }
645
646 #[test]
647 fn reassembler_concatenated_then_spanning_tail() {
648 let s1 = build_section(0x42, &[0x01, 0x02]); let s2 = build_section(0x46, &[0x09u8; 60]); let split = 30;
654
655 let mut head = Vec::new();
656 head.extend_from_slice(&s1);
657 head.extend_from_slice(&s2[..split]);
658 let payload1 = build_pusi_payload(0, &[], &head);
659 let payload2 = s2[split..].to_vec();
660
661 let mut reasm = SectionReassembler::default();
662 reasm.feed(&payload1, true);
663 let first = reasm.pop_section().expect("first section pops at once");
664 assert_eq!(first.as_ref(), &s1[..]);
665 assert!(reasm.pop_section().is_none(), "second is still partial");
666
667 reasm.feed(&payload2, false);
668 let second = reasm.pop_section().expect("second pops after continuation");
669 assert_eq!(second.as_ref(), &s2[..]);
670 }
671
672 #[test]
673 fn reassembler_completes_section_spanning_into_pusi_packet() {
674 let spanning = build_section(0x42, &[0x5Au8; 62]); let head = 41;
682 let tail = &spanning[head..]; assert_eq!(tail.len(), 24);
684
685 let next = build_section(0x46, &[0x77, 0x88]); let payload_a = build_pusi_payload(0, &[], &spanning[..head]);
690 let payload_b = build_pusi_payload(24, tail, &next);
692
693 let mut reasm = SectionReassembler::default();
694 reasm.feed(&payload_a, true);
695 assert!(reasm.pop_section().is_none(), "head alone is incomplete");
696
697 reasm.feed(&payload_b, true);
698 let got: Vec<_> = std::iter::from_fn(|| reasm.pop_section()).collect();
699 assert_eq!(got.len(), 2, "spanning section + new section must both pop");
700 assert_eq!(
701 got[0].as_ref(),
702 &spanning[..],
703 "spanning section completed from B's pointer tail"
704 );
705 assert_eq!(got[1].as_ref(), &next[..]);
706 }
707
708 #[test]
709 fn reassembler_pusi_pointer_spans_whole_payload() {
710 let spanning = build_section(0x42, &[0x33u8; 40]); let head = 20;
715 let payload_a = build_pusi_payload(0, &[], &spanning[..head]);
716 let tail = &spanning[head..]; let mut reasm = SectionReassembler::default();
719 reasm.feed(&payload_a, true);
720 reasm.feed(&build_pusi_payload_pointer_spanning_all(tail), true);
722
723 let out = reasm.pop_section().expect("spanning section completes");
724 assert_eq!(out.as_ref(), &spanning[..]);
725 assert!(reasm.pop_section().is_none());
726 }
727
728 fn build_pusi_payload_pointer_spanning_all(tail: &[u8]) -> Vec<u8> {
731 let mut v = Vec::with_capacity(1 + tail.len());
732 v.push(tail.len() as u8);
733 v.extend_from_slice(tail);
734 v
735 }
736
737 #[test]
738 fn reassembler_discards_on_buffer_overflow() {
739 let mut section = Vec::with_capacity(3 + 4095);
743 section.push(0x00); section.push(0xB0 | ((4095u16 >> 8) as u8 & 0x0F));
745 section.push(0xFF);
746 section.extend_from_slice(&[0u8; 160]);
747 let payload1 = build_pusi_payload(0, &[], §ion);
748
749 let mut reasm = SectionReassembler::default();
750 reasm.feed(&payload1, true);
751 assert!(reasm.pop_section().is_none());
752
753 let filler = vec![0u8; 180];
755 for _ in 0..(MAX_SECTION_SIZE / 180 + 1) {
756 reasm.feed(&filler, false);
757 }
758 assert!(
759 reasm.pop_section().is_none(),
760 "no section should pop after overflow reset"
761 );
762
763 let valid_section = build_section(0x00, &[0xAA]);
765 let payload2 = build_pusi_payload(0, &[], &valid_section);
766 reasm.feed(&payload2, true);
767 let out = reasm
768 .pop_section()
769 .expect("fresh section should pop after reset");
770 assert_eq!(out.as_ref(), &valid_section[..]);
771 }
772
773 #[test]
774 fn reassembler_handles_pusi_with_nonzero_pointer_field() {
775 let prior_tail = vec![0x11, 0x22, 0x33];
777 let new_section = build_section(0x02, &[0xBB]);
778 assert_eq!(new_section.len(), 4);
779 let payload = build_pusi_payload(3, &prior_tail, &new_section);
780
781 let mut reasm = SectionReassembler::default();
782 reasm.feed(&payload, true);
783
784 let out = reasm
785 .pop_section()
786 .expect("section after pointer_field skip should pop");
787 assert_eq!(out.as_ref(), &new_section[..]);
788 }
789
790 #[test]
791 fn reassembler_ignores_continuation_before_pusi() {
792 let pkt = make_packet(0x00, 0x00, PAYLOAD_FLAG, &[0xAA, 0xBB, 0xCC]);
795
796 let mut reasm = SectionReassembler::default();
797 reasm.feed(&pkt[4..], false); assert!(
800 reasm.pop_section().is_none(),
801 "no section should appear without prior PUSI"
802 );
803 assert!(
804 reasm.pop_section().is_none(),
805 "second pop should also be none"
806 );
807 }
808
809 #[test]
812 fn reassembler_empty_pusi_payload_does_not_panic() {
813 let mut reasm = SectionReassembler::default();
814 reasm.feed(&[], true);
815 assert!(reasm.pop_section().is_none());
816 let payload = vec![0x00u8, 0x72, 0x70, 0x01, 0x00];
818 reasm.feed(&payload, true);
819 assert!(reasm.pop_section().is_some());
820 }
821
822 #[test]
826 fn reassembler_accepts_maximal_private_section() {
827 let mut section = vec![0x80u8, 0x7F, 0xFF]; section.resize(3 + 0xFFF, 0xAB);
829
830 let mut reasm = SectionReassembler::default();
831 let mut first = vec![0x00];
833 first.extend_from_slice(§ion[..183]);
834 reasm.feed(&first, true);
835 for chunk in section[183..].chunks(184) {
836 reasm.feed(chunk, false);
837 }
838 let out = reasm.pop_section().expect("4098-byte section should pop");
839 assert_eq!(out.len(), 4098);
840 assert_eq!(out.as_ref(), §ion[..]);
841 }
842
843 #[test]
846 fn pcr_as_27mhz_known_value() {
847 assert_eq!(
848 Pcr {
849 base: 10_000,
850 extension: 0
851 }
852 .as_27mhz(),
853 3_000_000
854 );
855 assert_eq!(
857 Pcr {
858 base: 1,
859 extension: 100
860 }
861 .as_27mhz(),
862 400
863 );
864 }
865
866 #[test]
867 fn pcr_decode_from_bytes() {
868 let af = [0x10u8, 0x00, 0x00, 0x13, 0x88, 0x7E, 0x00];
870 let pcr = Pcr::parse(&af, 1).expect("6 bytes present");
871 assert_eq!(
872 pcr,
873 Pcr {
874 base: 10_000,
875 extension: 0
876 }
877 );
878 assert_eq!(pcr.as_27mhz(), 3_000_000);
879 }
880
881 #[test]
882 fn adaptation_field_flags_and_pcr() {
883 let mut raw = [0xAAu8; TS_PACKET_SIZE];
884 raw[0] = TS_SYNC_BYTE;
885 raw[1] = 0x01; raw[2] = 0x00;
887 raw[3] = ADAPTATION_FLAG | PAYLOAD_FLAG;
888 raw[4] = 7; raw[5] = AF_DISCONTINUITY | AF_PCR_FLAG;
890 raw[6..12].copy_from_slice(&[0x00, 0x00, 0x13, 0x88, 0x7E, 0x00]);
891 let pkt = TsPacket::parse(&raw).expect("valid packet");
894 let af = pkt
895 .adaptation_field()
896 .expect("has adaptation field")
897 .expect("adaptation field parses");
898 assert!(af.discontinuity_indicator);
899 assert!(!af.random_access_indicator);
900 assert_eq!(
901 af.pcr,
902 Some(Pcr {
903 base: 10_000,
904 extension: 0
905 })
906 );
907 assert_eq!(af.pcr.unwrap().as_27mhz(), 3_000_000);
908 assert!(af.opcr.is_none());
909 assert!(af.splice_countdown.is_none());
910 let payload = pkt.payload.expect("payload present");
912 assert_eq!(payload.len(), TS_PACKET_SIZE - 12);
913 assert_eq!(payload[0], 0xAA);
914 }
915
916 #[test]
917 fn no_adaptation_returns_none() {
918 let mut raw = [0x00u8; TS_PACKET_SIZE];
919 raw[0] = TS_SYNC_BYTE;
920 raw[1] = 0x01;
921 raw[3] = PAYLOAD_FLAG; let pkt = TsPacket::parse(&raw).expect("valid");
923 assert!(pkt.adaptation_field().is_none());
924 assert!(pkt.adaptation.is_none());
925 }
926
927 #[test]
928 fn adaptation_field_splice_countdown_negative() {
929 let mut raw = [0xAAu8; TS_PACKET_SIZE];
930 raw[0] = TS_SYNC_BYTE;
931 raw[1] = 0x01;
932 raw[2] = 0x00;
933 raw[3] = ADAPTATION_FLAG | PAYLOAD_FLAG;
934 raw[4] = 2; raw[5] = AF_SPLICING_FLAG;
936 raw[6] = 0xFB; let pkt = TsPacket::parse(&raw).expect("valid");
938 let af = pkt.adaptation_field().unwrap().unwrap();
939 assert_eq!(af.splice_countdown, Some(-5));
940 assert!(af.pcr.is_none());
941 }
942}