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]) -> Option<Self> {
79 if raw4.len() < 4 {
80 return None;
81 }
82 let b1 = raw4[1];
83 let b2 = raw4[2];
84 let b3 = raw4[3];
85
86 let tei = (b1 & TEI_MASK) != 0;
87 let pusi = (b1 & PUSI_MASK) != 0;
88 let pid = (((b1 & PID_MASK_HI) as u16) << 8) | (b2 as u16);
89 let scrambling = (b3 & SCRAMBLING_MASK) >> 6;
90 let has_adaptation = (b3 & ADAPTATION_FLAG) != 0;
91 let has_payload = (b3 & PAYLOAD_FLAG) != 0;
92 let continuity_counter = b3 & CC_MASK;
93
94 Some(Self {
95 tei,
96 pusi,
97 pid,
98 scrambling,
99 has_adaptation,
100 has_payload,
101 continuity_counter,
102 })
103 }
104
105 pub fn serialize_into(&self, buf: &mut [u8]) {
109 assert!(
110 buf.len() >= 4,
111 "buffer must have at least 4 bytes for TS header"
112 );
113 buf[0] = TS_SYNC_BYTE;
114 buf[1] = 0;
115 if self.tei {
116 buf[1] |= TEI_MASK;
117 }
118 if self.pusi {
119 buf[1] |= PUSI_MASK;
120 }
121 buf[1] |= ((self.pid >> 8) as u8) & PID_MASK_HI;
122 buf[2] = (self.pid & 0xFF) as u8;
123 buf[3] = (self.scrambling << 6) & SCRAMBLING_MASK;
124 if self.has_adaptation {
125 buf[3] |= ADAPTATION_FLAG;
126 }
127 if self.has_payload {
128 buf[3] |= PAYLOAD_FLAG;
129 }
130 buf[3] |= self.continuity_counter & CC_MASK;
131 }
132}
133
134impl<'a> TsPacket<'a> {
135 pub fn parse(buf: &'a [u8]) -> Result<Self> {
141 if buf.len() < TS_PACKET_SIZE {
142 return Err(Error::BufferTooShort {
143 need: TS_PACKET_SIZE,
144 have: buf.len(),
145 what: "TsPacket::parse",
146 });
147 }
148 if buf[0] != TS_SYNC_BYTE {
149 return Err(Error::InvalidSyncByte { found: buf[0] });
150 }
151
152 let raw: &[u8; TS_PACKET_SIZE] =
153 buf[..TS_PACKET_SIZE]
154 .try_into()
155 .map_err(|_| Error::BufferTooShort {
156 need: TS_PACKET_SIZE,
157 have: buf.len(),
158 what: "TsPacket::parse (array conversion)",
159 })?;
160
161 let header = TsHeader::parse(&raw[..4])
162 .expect("raw is 188 bytes so first 4 bytes are always present");
163
164 let mut cursor = 4usize;
165 let mut payload = None;
166 let mut adaptation = None;
167
168 if header.has_adaptation && cursor < TS_PACKET_SIZE {
171 let af_len = raw[cursor] as usize;
172 let af_start = cursor + 1;
173 if af_len > 0 && af_start < TS_PACKET_SIZE {
174 let af_end = (af_start + af_len).min(TS_PACKET_SIZE);
175 adaptation = Some(&raw[af_start..af_end]);
176 }
177 cursor += 1 + af_len;
178 }
179
180 if header.has_payload && cursor < TS_PACKET_SIZE {
181 payload = Some(&raw[cursor..]);
182 }
183
184 Ok(TsPacket {
185 header,
186 payload,
187 adaptation,
188 raw,
189 })
190 }
191
192 pub fn adaptation_field(&self) -> Option<crate::Result<AdaptationField>> {
198 self.adaptation.map(AdaptationField::parse)
199 }
200}
201
202const AF_DISCONTINUITY: u8 = 0x80;
204const AF_RANDOM_ACCESS: u8 = 0x40;
205const AF_ES_PRIORITY: u8 = 0x20;
206const AF_PCR_FLAG: u8 = 0x10;
207const AF_OPCR_FLAG: u8 = 0x08;
208const AF_SPLICING_FLAG: u8 = 0x04;
209const PCR_FIELD_LEN: usize = 6;
211
212#[derive(Clone, Copy, Debug, PartialEq, Eq)]
215#[cfg_attr(feature = "serde", derive(serde::Serialize))]
216pub struct Pcr {
217 pub base: u64,
219 pub extension: u16,
221}
222
223impl Pcr {
224 #[must_use]
226 pub fn as_27mhz(self) -> u64 {
227 self.base * 300 + self.extension as u64
228 }
229
230 fn parse(af: &[u8], at: usize) -> Result<Self> {
232 let b: &[u8; PCR_FIELD_LEN] = af
233 .get(at..at + PCR_FIELD_LEN)
234 .and_then(|s| s.try_into().ok())
235 .ok_or(Error::BufferTooShort {
236 need: at + PCR_FIELD_LEN,
237 have: af.len(),
238 what: "adaptation_field PCR",
239 })?;
240 let base = ((b[0] as u64) << 25)
241 | ((b[1] as u64) << 17)
242 | ((b[2] as u64) << 9)
243 | ((b[3] as u64) << 1)
244 | ((b[4] as u64) >> 7);
245 let extension = (((b[4] & 0x01) as u16) << 8) | (b[5] as u16);
246 Ok(Self { base, extension })
247 }
248}
249
250#[derive(Clone, Copy, Debug, PartialEq, Eq)]
254#[cfg_attr(feature = "serde", derive(serde::Serialize))]
255pub struct AdaptationField {
256 pub discontinuity_indicator: bool,
258 pub random_access_indicator: bool,
260 pub elementary_stream_priority_indicator: bool,
262 pub pcr: Option<Pcr>,
264 pub opcr: Option<Pcr>,
266 pub splice_countdown: Option<i8>,
268}
269
270impl AdaptationField {
271 fn parse(af: &[u8]) -> Result<Self> {
273 let flags = *af.first().ok_or(Error::BufferTooShort {
274 need: 1,
275 have: 0,
276 what: "adaptation_field flags",
277 })?;
278 let mut cursor = 1usize;
279
280 let pcr = if flags & AF_PCR_FLAG != 0 {
281 let p = Pcr::parse(af, cursor)?;
282 cursor += PCR_FIELD_LEN;
283 Some(p)
284 } else {
285 None
286 };
287 let opcr = if flags & AF_OPCR_FLAG != 0 {
288 let p = Pcr::parse(af, cursor)?;
289 cursor += PCR_FIELD_LEN;
290 Some(p)
291 } else {
292 None
293 };
294 let splice_countdown = if flags & AF_SPLICING_FLAG != 0 {
295 let b = *af.get(cursor).ok_or(Error::BufferTooShort {
296 need: cursor + 1,
297 have: af.len(),
298 what: "adaptation_field splice_countdown",
299 })?;
300 Some(b as i8)
301 } else {
302 None
303 };
304
305 Ok(AdaptationField {
306 discontinuity_indicator: flags & AF_DISCONTINUITY != 0,
307 random_access_indicator: flags & AF_RANDOM_ACCESS != 0,
308 elementary_stream_priority_indicator: flags & AF_ES_PRIORITY != 0,
309 pcr,
310 opcr,
311 splice_countdown,
312 })
313 }
314}
315
316#[derive(Default)]
321pub struct SectionReassembler {
322 buf: bytes::BytesMut,
323 ready: std::collections::VecDeque<bytes::Bytes>,
324}
325
326impl SectionReassembler {
327 pub fn feed(&mut self, payload: &[u8], pusi: bool) {
335 if pusi {
336 if payload.is_empty() {
339 self.buf.clear();
340 return;
341 }
342 let pointer = payload[0] as usize;
343
344 if !self.buf.is_empty() && pointer > 0 {
352 let avail = payload.len() - 1;
353 let tail_len = pointer.min(avail);
354 if self.buf.len() + tail_len > MAX_SECTION_SIZE {
355 self.buf.clear();
356 } else {
357 self.buf.extend_from_slice(&payload[1..1 + tail_len]);
358 self.drain_complete_sections();
359 }
360 }
361
362 self.buf.clear();
365
366 let start = 1 + pointer;
367 if start >= payload.len() {
368 return;
370 }
371 let new_data = &payload[start..];
372 if new_data.len() > MAX_SECTION_SIZE {
373 return;
374 }
375 self.buf.extend_from_slice(new_data);
376 } else {
377 if self.buf.is_empty() {
378 return;
379 }
380 if self.buf.len() + payload.len() > MAX_SECTION_SIZE {
381 self.buf.clear();
382 return;
383 }
384 self.buf.extend_from_slice(payload);
385 }
386
387 self.drain_complete_sections();
388 }
389
390 fn drain_complete_sections(&mut self) {
401 loop {
402 if self.buf.len() < 3 {
403 break;
406 }
407 if self.buf[0] == 0xFF {
408 self.buf.clear();
410 break;
411 }
412 let exp = 3 + (((self.buf[1] & 0x0F) as usize) << 8 | self.buf[2] as usize);
413 if self.buf.len() >= exp {
414 let section = self.buf.split_to(exp).freeze();
417 self.ready.push_back(section);
418 } else {
419 break;
421 }
422 }
423 }
424
425 pub fn pop_section(&mut self) -> Option<bytes::Bytes> {
427 self.ready.pop_front()
428 }
429
430 pub fn len(&self) -> usize {
432 self.buf.len()
433 }
434
435 pub fn is_empty(&self) -> bool {
437 self.buf.is_empty()
438 }
439}
440
441#[cfg(test)]
442mod tests {
443 use super::*;
444
445 fn make_packet(b1: u8, b2: u8, b3: u8, payload_data: &[u8]) -> [u8; TS_PACKET_SIZE] {
447 let mut pkt = [0u8; TS_PACKET_SIZE];
448 pkt[0] = TS_SYNC_BYTE;
449 pkt[1] = b1;
450 pkt[2] = b2;
451 pkt[3] = b3;
452 let payload_start = 4;
453 let end = (payload_start + payload_data.len()).min(TS_PACKET_SIZE);
454 let len = (end - payload_start).min(payload_data.len());
455 pkt[payload_start..payload_start + len].copy_from_slice(&payload_data[..len]);
456 pkt
457 }
458
459 #[test]
460 fn parse_rejects_non_0x47_sync_byte() {
461 let mut pkt = [0u8; TS_PACKET_SIZE];
462 pkt[0] = 0x46; let err = TsPacket::parse(&pkt).unwrap_err();
464 match err {
465 Error::InvalidSyncByte { found } => assert_eq!(found, 0x46),
466 other => panic!("expected InvalidSyncByte, got {other:?}"),
467 }
468 }
469
470 #[test]
471 fn parse_extracts_pid_and_continuity_counter() {
472 let pkt = make_packet(0x12, 0x34, 0x05, &[]);
479 let pkt = TsPacket::parse(&pkt).unwrap();
480 assert_eq!(pkt.header.pid, 0x1234);
481 assert_eq!(pkt.header.continuity_counter, 5);
482 }
483
484 #[test]
485 fn payload_unit_start_indicator_flag_extracted() {
486 let pkt1 = make_packet(0x40, 0x00, 0x00, &[]);
488 let pkt1 = TsPacket::parse(&pkt1).unwrap();
489 assert!(pkt1.header.pusi);
490
491 let pkt2 = make_packet(0x00, 0x00, 0x00, &[]);
493 let pkt2 = TsPacket::parse(&pkt2).unwrap();
494 assert!(!pkt2.header.pusi);
495 }
496
497 fn build_pusi_payload(pointer_field: u8, previous_tail: &[u8], section: &[u8]) -> Vec<u8> {
502 assert_eq!(pointer_field as usize, previous_tail.len());
503 let mut v = Vec::with_capacity(1 + previous_tail.len() + section.len());
504 v.push(pointer_field);
505 v.extend_from_slice(previous_tail);
506 v.extend_from_slice(section);
507 v
508 }
509
510 fn build_section(table_id: u8, body_after_length: &[u8]) -> Vec<u8> {
514 let section_length = body_after_length.len() as u16;
515 let mut v = Vec::with_capacity(3 + section_length as usize);
516 v.push(table_id);
517 v.push(0xB0 | ((section_length >> 8) as u8 & 0x0F));
519 v.push((section_length & 0xFF) as u8);
520 v.extend_from_slice(body_after_length);
521 v
522 }
523
524 #[test]
530 fn reassembler_accumulates_multi_packet_section() {
531 let body = vec![0xAAu8; 197];
533 let section = build_section(0x02, &body);
534 assert_eq!(section.len(), 200);
535
536 let first_chunk = 100;
537 let payload1 = build_pusi_payload(0, &[], §ion[..first_chunk]);
538 let payload2 = section[first_chunk..].to_vec();
539
540 let mut reasm = SectionReassembler::default();
541 reasm.feed(&payload1, true);
542 reasm.feed(&payload2, false);
543
544 let out = reasm.pop_section().expect("section should be ready");
545 assert_eq!(out.len(), 200);
546 assert_eq!(out.as_ref(), §ion[..]);
547 }
548
549 #[test]
550 fn reassembler_yields_complete_section_once_length_satisfied() {
551 let section = build_section(0x42, &[0xAA]);
553 assert_eq!(section.len(), 4);
554 let payload = build_pusi_payload(0, &[], §ion);
555
556 let mut reasm = SectionReassembler::default();
557 reasm.feed(&payload, true);
558
559 let out = reasm
560 .pop_section()
561 .expect("single-packet section should pop");
562 assert_eq!(out.as_ref(), §ion[..]);
563 }
564
565 #[test]
566 fn reassembler_extracts_all_concatenated_sections_in_one_payload() {
567 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();
576 concat.extend_from_slice(&s1);
577 concat.extend_from_slice(&s2);
578 concat.extend_from_slice(&s3);
579 let payload = build_pusi_payload(0, &[], &concat);
580
581 let mut reasm = SectionReassembler::default();
582 reasm.feed(&payload, true);
583
584 let got: Vec<_> = std::iter::from_fn(|| reasm.pop_section()).collect();
586 assert_eq!(got.len(), 3, "all three concatenated sections must pop");
587 assert_eq!(got[0].as_ref(), &s1[..]);
588 assert_eq!(got[1].as_ref(), &s2[..]);
589 assert_eq!(got[2].as_ref(), &s3[..]);
590 }
591
592 #[test]
593 fn reassembler_stops_at_stuffing_after_concatenated_sections() {
594 let s1 = build_section(0x42, &[0xAA]); let s2 = build_section(0x46, &[0xBB, 0xCC]); let mut concat = Vec::new();
600 concat.extend_from_slice(&s1);
601 concat.extend_from_slice(&s2);
602 concat.extend_from_slice(&[0xFF, 0xFF, 0xFF, 0xFF]); let payload = build_pusi_payload(0, &[], &concat);
604
605 let mut reasm = SectionReassembler::default();
606 reasm.feed(&payload, true);
607
608 let got: Vec<_> = std::iter::from_fn(|| reasm.pop_section()).collect();
609 assert_eq!(got.len(), 2);
610 assert_eq!(got[0].as_ref(), &s1[..]);
611 assert_eq!(got[1].as_ref(), &s2[..]);
612 assert!(
613 reasm.is_empty(),
614 "stuffing tail must be discarded, not buffered"
615 );
616 }
617
618 #[test]
619 fn reassembler_concatenated_then_spanning_tail() {
620 let s1 = build_section(0x42, &[0x01, 0x02]); let s2 = build_section(0x46, &[0x09u8; 60]); let split = 30;
626
627 let mut head = Vec::new();
628 head.extend_from_slice(&s1);
629 head.extend_from_slice(&s2[..split]);
630 let payload1 = build_pusi_payload(0, &[], &head);
631 let payload2 = s2[split..].to_vec();
632
633 let mut reasm = SectionReassembler::default();
634 reasm.feed(&payload1, true);
635 let first = reasm.pop_section().expect("first section pops at once");
636 assert_eq!(first.as_ref(), &s1[..]);
637 assert!(reasm.pop_section().is_none(), "second is still partial");
638
639 reasm.feed(&payload2, false);
640 let second = reasm.pop_section().expect("second pops after continuation");
641 assert_eq!(second.as_ref(), &s2[..]);
642 }
643
644 #[test]
645 fn reassembler_completes_section_spanning_into_pusi_packet() {
646 let spanning = build_section(0x42, &[0x5Au8; 62]); let head = 41;
654 let tail = &spanning[head..]; assert_eq!(tail.len(), 24);
656
657 let next = build_section(0x46, &[0x77, 0x88]); let payload_a = build_pusi_payload(0, &[], &spanning[..head]);
662 let payload_b = build_pusi_payload(24, tail, &next);
664
665 let mut reasm = SectionReassembler::default();
666 reasm.feed(&payload_a, true);
667 assert!(reasm.pop_section().is_none(), "head alone is incomplete");
668
669 reasm.feed(&payload_b, true);
670 let got: Vec<_> = std::iter::from_fn(|| reasm.pop_section()).collect();
671 assert_eq!(got.len(), 2, "spanning section + new section must both pop");
672 assert_eq!(
673 got[0].as_ref(),
674 &spanning[..],
675 "spanning section completed from B's pointer tail"
676 );
677 assert_eq!(got[1].as_ref(), &next[..]);
678 }
679
680 #[test]
681 fn reassembler_pusi_pointer_spans_whole_payload() {
682 let spanning = build_section(0x42, &[0x33u8; 40]); let head = 20;
687 let payload_a = build_pusi_payload(0, &[], &spanning[..head]);
688 let tail = &spanning[head..]; let mut reasm = SectionReassembler::default();
691 reasm.feed(&payload_a, true);
692 reasm.feed(&payload_b_pointer_only(tail), true);
694
695 let out = reasm.pop_section().expect("spanning section completes");
696 assert_eq!(out.as_ref(), &spanning[..]);
697 assert!(reasm.pop_section().is_none());
698 }
699
700 fn payload_b_pointer_only(tail: &[u8]) -> Vec<u8> {
703 let mut v = Vec::with_capacity(1 + tail.len());
704 v.push(tail.len() as u8);
705 v.extend_from_slice(tail);
706 v
707 }
708
709 #[test]
710 fn reassembler_discards_on_buffer_overflow() {
711 let mut section = Vec::with_capacity(3 + 4095);
715 section.push(0x00); section.push(0xB0 | ((4095u16 >> 8) as u8 & 0x0F));
717 section.push(0xFF);
718 section.extend_from_slice(&[0u8; 160]);
719 let payload1 = build_pusi_payload(0, &[], §ion);
720
721 let mut reasm = SectionReassembler::default();
722 reasm.feed(&payload1, true);
723 assert!(reasm.pop_section().is_none());
724
725 let filler = vec![0u8; 180];
727 for _ in 0..(MAX_SECTION_SIZE / 180 + 1) {
728 reasm.feed(&filler, false);
729 }
730 assert!(
731 reasm.pop_section().is_none(),
732 "no section should pop after overflow reset"
733 );
734
735 let valid_section = build_section(0x00, &[0xAA]);
737 let payload2 = build_pusi_payload(0, &[], &valid_section);
738 reasm.feed(&payload2, true);
739 let out = reasm
740 .pop_section()
741 .expect("fresh section should pop after reset");
742 assert_eq!(out.as_ref(), &valid_section[..]);
743 }
744
745 #[test]
746 fn reassembler_handles_pusi_with_nonzero_pointer_field() {
747 let prior_tail = vec![0x11, 0x22, 0x33];
749 let new_section = build_section(0x02, &[0xBB]);
750 assert_eq!(new_section.len(), 4);
751 let payload = build_pusi_payload(3, &prior_tail, &new_section);
752
753 let mut reasm = SectionReassembler::default();
754 reasm.feed(&payload, true);
755
756 let out = reasm
757 .pop_section()
758 .expect("section after pointer_field skip should pop");
759 assert_eq!(out.as_ref(), &new_section[..]);
760 }
761
762 #[test]
763 fn reassembler_ignores_continuation_before_pusi() {
764 let pkt = make_packet(0x00, 0x00, PAYLOAD_FLAG, &[0xAA, 0xBB, 0xCC]);
767
768 let mut reasm = SectionReassembler::default();
769 reasm.feed(&pkt[4..], false); assert!(
772 reasm.pop_section().is_none(),
773 "no section should appear without prior PUSI"
774 );
775 assert!(
776 reasm.pop_section().is_none(),
777 "second pop should also be none"
778 );
779 }
780
781 #[test]
784 fn reassembler_empty_pusi_payload_does_not_panic() {
785 let mut reasm = SectionReassembler::default();
786 reasm.feed(&[], true);
787 assert!(reasm.pop_section().is_none());
788 let mut payload = vec![0x00u8, 0x72, 0x70, 0x01, 0x00];
790 payload.resize(5, 0);
791 reasm.feed(&payload, true);
792 assert!(reasm.pop_section().is_some());
793 }
794
795 #[test]
799 fn reassembler_accepts_maximal_private_section() {
800 let mut section = vec![0x80u8, 0x7F, 0xFF]; section.resize(3 + 0xFFF, 0xAB);
802
803 let mut reasm = SectionReassembler::default();
804 let mut first = vec![0x00];
806 first.extend_from_slice(§ion[..183]);
807 reasm.feed(&first, true);
808 for chunk in section[183..].chunks(184) {
809 reasm.feed(chunk, false);
810 }
811 let out = reasm.pop_section().expect("4098-byte section should pop");
812 assert_eq!(out.len(), 4098);
813 assert_eq!(out.as_ref(), §ion[..]);
814 }
815
816 #[test]
819 fn pcr_as_27mhz_known_value() {
820 assert_eq!(
821 Pcr {
822 base: 10_000,
823 extension: 0
824 }
825 .as_27mhz(),
826 3_000_000
827 );
828 assert_eq!(
830 Pcr {
831 base: 1,
832 extension: 100
833 }
834 .as_27mhz(),
835 400
836 );
837 }
838
839 #[test]
840 fn pcr_decode_from_bytes() {
841 let af = [0x10u8, 0x00, 0x00, 0x13, 0x88, 0x7E, 0x00];
843 let pcr = Pcr::parse(&af, 1).expect("6 bytes present");
844 assert_eq!(
845 pcr,
846 Pcr {
847 base: 10_000,
848 extension: 0
849 }
850 );
851 assert_eq!(pcr.as_27mhz(), 3_000_000);
852 }
853
854 #[test]
855 fn adaptation_field_flags_and_pcr() {
856 let mut raw = [0xAAu8; TS_PACKET_SIZE];
857 raw[0] = TS_SYNC_BYTE;
858 raw[1] = 0x01; raw[2] = 0x00;
860 raw[3] = ADAPTATION_FLAG | PAYLOAD_FLAG;
861 raw[4] = 7; raw[5] = AF_DISCONTINUITY | AF_PCR_FLAG;
863 raw[6..12].copy_from_slice(&[0x00, 0x00, 0x13, 0x88, 0x7E, 0x00]);
864 let pkt = TsPacket::parse(&raw).expect("valid packet");
867 let af = pkt
868 .adaptation_field()
869 .expect("has adaptation field")
870 .expect("adaptation field parses");
871 assert!(af.discontinuity_indicator);
872 assert!(!af.random_access_indicator);
873 assert_eq!(
874 af.pcr,
875 Some(Pcr {
876 base: 10_000,
877 extension: 0
878 })
879 );
880 assert_eq!(af.pcr.unwrap().as_27mhz(), 3_000_000);
881 assert!(af.opcr.is_none());
882 assert!(af.splice_countdown.is_none());
883 let payload = pkt.payload.expect("payload present");
885 assert_eq!(payload.len(), TS_PACKET_SIZE - 12);
886 assert_eq!(payload[0], 0xAA);
887 }
888
889 #[test]
890 fn no_adaptation_returns_none() {
891 let mut raw = [0x00u8; TS_PACKET_SIZE];
892 raw[0] = TS_SYNC_BYTE;
893 raw[1] = 0x01;
894 raw[3] = PAYLOAD_FLAG; let pkt = TsPacket::parse(&raw).expect("valid");
896 assert!(pkt.adaptation_field().is_none());
897 assert!(pkt.adaptation.is_none());
898 }
899
900 #[test]
901 fn adaptation_field_splice_countdown_negative() {
902 let mut raw = [0xAAu8; TS_PACKET_SIZE];
903 raw[0] = TS_SYNC_BYTE;
904 raw[1] = 0x01;
905 raw[2] = 0x00;
906 raw[3] = ADAPTATION_FLAG | PAYLOAD_FLAG;
907 raw[4] = 2; raw[5] = AF_SPLICING_FLAG;
909 raw[6] = 0xFB; let pkt = TsPacket::parse(&raw).expect("valid");
911 let af = pkt.adaptation_field().unwrap().unwrap();
912 assert_eq!(af.splice_countdown, Some(-5));
913 assert!(af.pcr.is_none());
914 }
915}