1use crate::PACKET_START_CODE_PREFIX;
4use crate::error::{Error, Result};
5use crate::stream_id::StreamId;
6use crate::timestamp::{self, Dts, Pts};
7
8const MIN_LEN: usize = 6; const HEADER_FIXED: usize = 3; const PES_HEADER_STUFFING_BYTE: u8 = 0xFF;
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
22#[cfg_attr(feature = "serde", derive(serde::Serialize))]
23pub struct Escr {
24 pub base: u64,
26 pub extension: u16,
28}
29
30impl Escr {
31 #[must_use]
33 pub fn as_27mhz(self) -> u64 {
34 self.base * 300 + self.extension as u64
35 }
36
37 #[must_use]
39 pub fn from_27mhz(ticks: u64) -> Self {
40 const BASE_MASK: u64 = 0x1_FFFF_FFFF;
41 const EXT_MASK: u16 = 0x1FF;
42 Self {
43 base: (ticks / 300) & BASE_MASK,
44 extension: ((ticks % 300) as u16) & EXT_MASK,
45 }
46 }
47
48 pub fn from_field_bytes(b: &[u8; 6]) -> Result<Self> {
58 let base = ((((b[0] >> 3) & 0x07) as u64) << 30) | (((b[0] & 0x03) as u64) << 28) | ((b[1] as u64) << 20) | ((((b[2] >> 3) & 0x1F) as u64) << 15) | (((b[2] & 0x03) as u64) << 13) | ((b[3] as u64) << 5) | (((b[4] >> 3) & 0x1F) as u64); let extension = ((((b[4] & 0x03) as u16) << 7) | ((b[5] >> 1) as u16)) & 0x1FF;
66 Ok(Self { base, extension })
67 }
68
69 #[must_use]
74 pub fn to_field_bytes(self) -> [u8; 6] {
75 let b = self.base & 0x1_FFFF_FFFF;
76 let e = (self.extension & 0x1FF) as u64;
77 [
78 0xC0 | (((b >> 30) & 0x07) as u8) << 3 | 0x04 | ((b >> 28) & 0x03) as u8,
80 ((b >> 20) & 0xFF) as u8,
82 (((b >> 15) & 0x1F) as u8) << 3 | 0x04 | ((b >> 13) & 0x03) as u8,
84 ((b >> 5) & 0xFF) as u8,
86 (((b & 0x1F) as u8) << 3) | 0x04 | ((e >> 7) & 0x03) as u8,
88 (((e & 0x7F) as u8) << 1) | 0x01,
90 ]
91 }
92}
93
94#[derive(Debug, Clone, Copy, PartialEq, Eq)]
99#[cfg_attr(feature = "serde", derive(serde::Serialize))]
100#[non_exhaustive]
101pub enum TrickMode {
102 FastForward {
104 field_id: u8,
106 intra_slice_refresh: bool,
108 frequency_truncation: u8,
110 },
111 SlowMotion {
113 rep_cntrl: u8,
115 },
116 FreezeFrame {
118 field_id: u8,
120 },
121 FastReverse {
123 field_id: u8,
125 intra_slice_refresh: bool,
127 frequency_truncation: u8,
129 },
130 SlowReverse {
132 rep_cntrl: u8,
134 },
135 Reserved {
137 trick_mode_control: u8,
139 data: u8,
141 },
142}
143
144impl TrickMode {
145 pub fn from_byte(b: u8) -> Self {
147 let control = (b >> 5) & 0x07;
148 let data = b & 0x1F;
149 match control {
150 0b000 => TrickMode::FastForward {
151 field_id: (data >> 3) & 0x03,
152 intra_slice_refresh: (data >> 2) & 0x01 != 0,
153 frequency_truncation: data & 0x03,
154 },
155 0b001 => TrickMode::SlowMotion { rep_cntrl: data },
156 0b010 => TrickMode::FreezeFrame {
157 field_id: (data >> 3) & 0x03,
158 },
159 0b011 => TrickMode::FastReverse {
160 field_id: (data >> 3) & 0x03,
161 intra_slice_refresh: (data >> 2) & 0x01 != 0,
162 frequency_truncation: data & 0x03,
163 },
164 0b100 => TrickMode::SlowReverse { rep_cntrl: data },
165 _ => TrickMode::Reserved {
166 trick_mode_control: control,
167 data,
168 },
169 }
170 }
171
172 pub fn to_byte(self) -> u8 {
174 match self {
175 TrickMode::FastForward {
176 field_id,
177 intra_slice_refresh,
178 frequency_truncation,
179 } => {
180 ((field_id & 0x03) << 3)
181 | ((intra_slice_refresh as u8) << 2)
182 | (frequency_truncation & 0x03)
183 }
184 TrickMode::SlowMotion { rep_cntrl } => (0b001 << 5) | (rep_cntrl & 0x1F),
185 TrickMode::FreezeFrame { field_id } => (0b010 << 5) | ((field_id & 0x03) << 3),
186 TrickMode::FastReverse {
187 field_id,
188 intra_slice_refresh,
189 frequency_truncation,
190 } => {
191 (0b011 << 5)
192 | ((field_id & 0x03) << 3)
193 | ((intra_slice_refresh as u8) << 2)
194 | (frequency_truncation & 0x03)
195 }
196 TrickMode::SlowReverse { rep_cntrl } => (0b100 << 5) | (rep_cntrl & 0x1F),
197 TrickMode::Reserved {
198 trick_mode_control,
199 data,
200 } => ((trick_mode_control & 0x07) << 5) | (data & 0x1F),
201 }
202 }
203}
204
205#[derive(Debug, Clone, Copy, PartialEq, Eq)]
209#[cfg_attr(feature = "serde", derive(serde::Serialize))]
210pub struct ProgramPacketSequenceCounter {
211 pub counter: u8,
213 pub mpeg1_mpeg2_identifier: bool,
215 pub original_stuff_length: u8,
217}
218
219#[derive(Debug, Clone, Copy, PartialEq, Eq)]
221#[cfg_attr(feature = "serde", derive(serde::Serialize))]
222pub struct PStdBuffer {
223 pub scale: bool,
225 pub size: u16,
227}
228
229#[derive(Debug, Clone, PartialEq, Eq)]
232#[cfg_attr(feature = "serde", derive(serde::Serialize))]
233pub struct PesExtension<'a> {
234 pub pes_private_data: Option<[u8; 16]>,
236 pub pack_header: Option<&'a [u8]>,
238 pub program_packet_sequence_counter: Option<ProgramPacketSequenceCounter>,
240 pub p_std_buffer: Option<PStdBuffer>,
242 pub pes_extension_field: Option<&'a [u8]>,
244}
245
246impl<'a> PesExtension<'a> {
247 fn parse(data: &'a [u8]) -> Result<Self> {
248 if data.is_empty() {
249 return Err(Error::BufferTooShort {
250 need: 1,
251 have: 0,
252 what: "PES_extension flags byte",
253 });
254 }
255 let flags = data[0];
256 let mut cursor = 1usize;
257
258 let pes_private_data = if flags & 0x80 != 0 {
259 let end = cursor + 16;
260 let arr: [u8; 16] = data
261 .get(cursor..end)
262 .and_then(|s| s.try_into().ok())
263 .ok_or(Error::BufferTooShort {
264 need: end,
265 have: data.len(),
266 what: "PES_private_data",
267 })?;
268 cursor = end;
269 Some(arr)
270 } else {
271 None
272 };
273
274 let pack_header = if flags & 0x40 != 0 {
275 let pack_len = *data.get(cursor).ok_or(Error::BufferTooShort {
276 need: cursor + 1,
277 have: data.len(),
278 what: "pack_field_length",
279 })? as usize;
280 cursor += 1;
281 let end = cursor + pack_len;
282 let slice = data.get(cursor..end).ok_or(Error::BufferTooShort {
283 need: end,
284 have: data.len(),
285 what: "pack_header",
286 })?;
287 cursor = end;
288 Some(slice)
289 } else {
290 None
291 };
292
293 let program_packet_sequence_counter = if flags & 0x20 != 0 {
294 if data.len() < cursor + 2 {
295 return Err(Error::BufferTooShort {
296 need: cursor + 2,
297 have: data.len(),
298 what: "program_packet_sequence_counter",
299 });
300 }
301 let b0 = data[cursor];
302 let b1 = data[cursor + 1];
303 cursor += 2;
304 Some(ProgramPacketSequenceCounter {
305 counter: b0 & 0x7F,
306 mpeg1_mpeg2_identifier: (b1 & 0x40) != 0,
307 original_stuff_length: b1 & 0x3F,
308 })
309 } else {
310 None
311 };
312
313 let p_std_buffer = if flags & 0x10 != 0 {
314 if data.len() < cursor + 2 {
315 return Err(Error::BufferTooShort {
316 need: cursor + 2,
317 have: data.len(),
318 what: "P-STD_buffer",
319 });
320 }
321 let b0 = data[cursor];
322 let b1 = data[cursor + 1];
323 cursor += 2;
324 Some(PStdBuffer {
325 scale: (b0 & 0x20) != 0,
326 size: (((b0 & 0x1F) as u16) << 8) | (b1 as u16),
327 })
328 } else {
329 None
330 };
331
332 let pes_extension_field = if flags & 0x01 != 0 {
333 let ext_len = *data.get(cursor).ok_or(Error::BufferTooShort {
334 need: cursor + 1,
335 have: data.len(),
336 what: "PES_extension_field_length",
337 })? as usize;
338 cursor += 1;
339 let end = cursor + ext_len;
340 let slice = data.get(cursor..end).ok_or(Error::BufferTooShort {
341 need: end,
342 have: data.len(),
343 what: "PES_extension_field",
344 })?;
345 cursor = end;
346 Some(slice)
347 } else {
348 None
349 };
350 let _ = cursor;
351
352 Ok(PesExtension {
353 pes_private_data,
354 pack_header,
355 program_packet_sequence_counter,
356 p_std_buffer,
357 pes_extension_field,
358 })
359 }
360
361 pub fn serialized_len(&self) -> usize {
363 let mut n = 1usize; if self.pes_private_data.is_some() {
365 n += 16;
366 }
367 if let Some(ph) = self.pack_header {
368 n += 1 + ph.len();
369 }
370 if self.program_packet_sequence_counter.is_some() {
371 n += 2;
372 }
373 if self.p_std_buffer.is_some() {
374 n += 2;
375 }
376 if let Some(ef) = self.pes_extension_field {
377 n += 1 + ef.len();
378 }
379 n
380 }
381
382 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
383 let need = self.serialized_len();
384 if buf.len() < need {
385 return Err(Error::BufferTooShort {
386 need,
387 have: buf.len(),
388 what: "PES_extension serialize output",
389 });
390 }
391
392 let mut flags = 0u8;
393 if self.pes_private_data.is_some() {
394 flags |= 0x80;
395 }
396 if self.pack_header.is_some() {
397 flags |= 0x40;
398 }
399 if self.program_packet_sequence_counter.is_some() {
400 flags |= 0x20;
401 }
402 if self.p_std_buffer.is_some() {
403 flags |= 0x10;
404 }
405 if self.pes_extension_field.is_some() {
406 flags |= 0x01;
407 }
408 buf[0] = flags;
409 let mut cursor = 1usize;
410
411 if let Some(pd) = &self.pes_private_data {
412 buf[cursor..cursor + 16].copy_from_slice(pd);
413 cursor += 16;
414 }
415 if let Some(ph) = self.pack_header {
416 buf[cursor] = ph.len() as u8;
417 cursor += 1;
418 buf[cursor..cursor + ph.len()].copy_from_slice(ph);
419 cursor += ph.len();
420 }
421 if let Some(ppsc) = self.program_packet_sequence_counter {
422 buf[cursor] = 0x80 | (ppsc.counter & 0x7F);
424 buf[cursor + 1] = 0x80
426 | ((ppsc.mpeg1_mpeg2_identifier as u8) << 6)
427 | (ppsc.original_stuff_length & 0x3F);
428 cursor += 2;
429 }
430 if let Some(ps) = self.p_std_buffer {
431 buf[cursor] = 0x40 | ((ps.scale as u8) << 5) | ((ps.size >> 8) as u8 & 0x1F);
433 buf[cursor + 1] = (ps.size & 0xFF) as u8;
434 cursor += 2;
435 }
436 if let Some(ef) = self.pes_extension_field {
437 buf[cursor] = ef.len() as u8;
438 cursor += 1;
439 buf[cursor..cursor + ef.len()].copy_from_slice(ef);
440 cursor += ef.len();
441 }
442 Ok(cursor)
443 }
444}
445
446#[non_exhaustive]
453#[derive(Debug, Clone, PartialEq, Eq)]
454#[cfg_attr(feature = "serde", derive(serde::Serialize))]
455pub struct PesHeader<'a> {
456 pub scrambling_control: u8,
458 pub pes_priority: bool,
460 pub data_alignment_indicator: bool,
462 pub copyright: bool,
464 pub original_or_copy: bool,
466 pub pts: Option<Pts>,
468 pub dts: Option<Dts>,
470 pub escr: Option<Escr>,
472 pub es_rate: Option<u32>,
474 pub dsm_trick_mode: Option<TrickMode>,
476 pub additional_copy_info: Option<u8>,
478 pub pes_crc: Option<u16>,
480 pub pes_extension: Option<PesExtension<'a>>,
482 pub header_stuffing_len: usize,
492}
493
494impl PesHeader<'_> {
495 fn optional_len(&self) -> usize {
498 let mut n = 0usize;
499 if self.pts.is_some() {
500 n += 5;
501 }
502 if self.dts.is_some() {
503 n += 5;
504 }
505 if self.escr.is_some() {
506 n += 6;
507 }
508 if self.es_rate.is_some() {
509 n += 3;
510 }
511 if self.dsm_trick_mode.is_some() {
512 n += 1;
513 }
514 if self.additional_copy_info.is_some() {
515 n += 1;
516 }
517 if self.pes_crc.is_some() {
518 n += 2;
519 }
520 if let Some(ref ext) = self.pes_extension {
521 n += ext.serialized_len();
522 }
523 n += self.header_stuffing_len;
524 n
525 }
526}
527
528#[derive(Debug, Clone, PartialEq, Eq)]
530#[cfg_attr(feature = "serde", derive(serde::Serialize))]
531pub struct PesPacket<'a> {
532 pub stream_id: StreamId,
534 pub pes_packet_length: u16,
536 pub header: Option<PesHeader<'a>>,
538 #[cfg_attr(feature = "serde", serde(skip))]
540 pub payload: &'a [u8],
541}
542
543impl<'a> PesPacket<'a> {
544 pub fn parse(b: &'a [u8]) -> Result<Self> {
546 if b.len() < MIN_LEN {
547 return Err(Error::BufferTooShort {
548 need: MIN_LEN,
549 have: b.len(),
550 what: "PES packet header",
551 });
552 }
553 if b[0..3] != PACKET_START_CODE_PREFIX {
554 return Err(Error::BadStartCode(
555 (u32::from(b[0]) << 16) | (u32::from(b[1]) << 8) | u32::from(b[2]),
556 ));
557 }
558 let stream_id = StreamId(b[3]);
559 let pes_packet_length = u16::from_be_bytes([b[4], b[5]]);
560 let payload_end = if pes_packet_length == 0 {
562 b.len()
563 } else {
564 (MIN_LEN + pes_packet_length as usize).min(b.len())
565 };
566
567 if !stream_id.has_optional_header() {
568 return Ok(PesPacket {
569 stream_id,
570 pes_packet_length,
571 header: None,
572 payload: &b[MIN_LEN..payload_end],
573 });
574 }
575
576 if b.len() < MIN_LEN + HEADER_FIXED {
577 return Err(Error::BufferTooShort {
578 need: MIN_LEN + HEADER_FIXED,
579 have: b.len(),
580 what: "PES optional header",
581 });
582 }
583 let f1 = b[6];
584 let f2 = b[7];
585 let hdl = usize::from(b[8]);
586 let hdr_start = MIN_LEN + HEADER_FIXED; let hdr_end = hdr_start + hdl;
588 if b.len() < hdr_end {
589 return Err(Error::BufferTooShort {
590 need: hdr_end,
591 have: b.len(),
592 what: "PES_header_data_length",
593 });
594 }
595 let opt = &b[hdr_start..hdr_end];
596 let mut cursor = 0usize;
597
598 let pts_dts_flags = (f2 >> 6) & 0x03;
600 let (pts, dts) = match pts_dts_flags {
601 0b10 => {
602 if opt.len() < cursor + 5 {
603 return Err(Error::BufferTooShort {
604 need: cursor + 5,
605 have: opt.len(),
606 what: "PTS",
607 });
608 }
609 let pts = Pts(timestamp::read(&opt[cursor..], 0b0010, "PTS")?);
610 cursor += 5;
611 (Some(pts), None)
612 }
613 0b11 => {
614 if opt.len() < cursor + 10 {
615 return Err(Error::BufferTooShort {
616 need: cursor + 10,
617 have: opt.len(),
618 what: "PTS+DTS",
619 });
620 }
621 let pts = Pts(timestamp::read(&opt[cursor..], 0b0011, "PTS")?);
622 cursor += 5;
623 let dts = Dts(timestamp::read(&opt[cursor..], 0b0001, "DTS")?);
624 cursor += 5;
625 (Some(pts), Some(dts))
626 }
627 _ => (None, None),
628 };
629
630 let escr = if f2 & 0x20 != 0 {
632 if opt.len() < cursor + 6 {
633 return Err(Error::BufferTooShort {
634 need: cursor + 6,
635 have: opt.len(),
636 what: "ESCR",
637 });
638 }
639 let arr: &[u8; 6] = opt[cursor..cursor + 6].try_into().unwrap();
640 let e = Escr::from_field_bytes(arr)?;
641 cursor += 6;
642 Some(e)
643 } else {
644 None
645 };
646
647 let es_rate = if f2 & 0x10 != 0 {
649 if opt.len() < cursor + 3 {
650 return Err(Error::BufferTooShort {
651 need: cursor + 3,
652 have: opt.len(),
653 what: "ES_rate",
654 });
655 }
656 let rate = (((opt[cursor] & 0x7F) as u32) << 15)
657 | ((opt[cursor + 1] as u32) << 7)
658 | ((opt[cursor + 2] >> 1) as u32);
659 cursor += 3;
660 Some(rate)
661 } else {
662 None
663 };
664
665 let dsm_trick_mode = if f2 & 0x08 != 0 {
667 if opt.len() < cursor + 1 {
668 return Err(Error::BufferTooShort {
669 need: cursor + 1,
670 have: opt.len(),
671 what: "trick_mode",
672 });
673 }
674 let tm = TrickMode::from_byte(opt[cursor]);
675 cursor += 1;
676 Some(tm)
677 } else {
678 None
679 };
680
681 let additional_copy_info = if f2 & 0x04 != 0 {
683 if opt.len() < cursor + 1 {
684 return Err(Error::BufferTooShort {
685 need: cursor + 1,
686 have: opt.len(),
687 what: "additional_copy_info",
688 });
689 }
690 let v = opt[cursor] & 0x7F;
691 cursor += 1;
692 Some(v)
693 } else {
694 None
695 };
696
697 let pes_crc = if f2 & 0x02 != 0 {
699 if opt.len() < cursor + 2 {
700 return Err(Error::BufferTooShort {
701 need: cursor + 2,
702 have: opt.len(),
703 what: "PES_CRC",
704 });
705 }
706 let crc = u16::from_be_bytes([opt[cursor], opt[cursor + 1]]);
707 cursor += 2;
708 Some(crc)
709 } else {
710 None
711 };
712
713 let pes_extension = if f2 & 0x01 != 0 {
715 let ext = PesExtension::parse(&opt[cursor..])?;
716 cursor += ext.serialized_len();
717 Some(ext)
718 } else {
719 None
720 };
721
722 let header_stuffing_len = hdl.saturating_sub(cursor);
726
727 let header = PesHeader {
728 scrambling_control: (f1 >> 4) & 0x03,
729 pes_priority: f1 & 0x08 != 0,
730 data_alignment_indicator: f1 & 0x04 != 0,
731 copyright: f1 & 0x02 != 0,
732 original_or_copy: f1 & 0x01 != 0,
733 pts,
734 dts,
735 escr,
736 es_rate,
737 dsm_trick_mode,
738 additional_copy_info,
739 pes_crc,
740 pes_extension,
741 header_stuffing_len,
742 };
743
744 Ok(PesPacket {
745 stream_id,
746 pes_packet_length,
747 header: Some(header),
748 payload: &b[hdr_end.min(payload_end)..payload_end],
749 })
750 }
751
752 #[must_use]
754 pub fn serialized_len(&self) -> usize {
755 let hdr = self
756 .header
757 .as_ref()
758 .map_or(0, |h| HEADER_FIXED + h.optional_len());
759 MIN_LEN + hdr + self.payload.len()
760 }
761
762 pub fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
764 let len = self.serialized_len();
765 if buf.len() < len {
766 return Err(Error::BufferTooShort {
767 need: len,
768 have: buf.len(),
769 what: "PES serialize output",
770 });
771 }
772 buf[0..3].copy_from_slice(&PACKET_START_CODE_PREFIX);
773 buf[3] = self.stream_id.0;
774 buf[4..6].copy_from_slice(&self.pes_packet_length.to_be_bytes());
775 let payload_at = match &self.header {
776 None => MIN_LEN,
777 Some(h) => {
778 let opt_len = h.optional_len();
779 if opt_len > 255 {
780 return Err(Error::OptionalFieldsTooLarge(opt_len));
781 }
782
783 let f1 = 0x80
785 | ((h.scrambling_control & 0x03) << 4)
786 | (u8::from(h.pes_priority) << 3)
787 | (u8::from(h.data_alignment_indicator) << 2)
788 | (u8::from(h.copyright) << 1)
789 | u8::from(h.original_or_copy);
790
791 let pts_dts_flags = match (h.pts.is_some(), h.dts.is_some()) {
794 (true, true) => 0b11u8,
795 (true, false) => 0b10,
796 _ => 0b00,
797 };
798 let f2 = (pts_dts_flags << 6)
799 | (u8::from(h.escr.is_some()) << 5)
800 | (u8::from(h.es_rate.is_some()) << 4)
801 | (u8::from(h.dsm_trick_mode.is_some()) << 3)
802 | (u8::from(h.additional_copy_info.is_some()) << 2)
803 | (u8::from(h.pes_crc.is_some()) << 1)
804 | u8::from(h.pes_extension.is_some());
805
806 buf[6] = f1;
807 buf[7] = f2;
808 buf[8] = opt_len as u8;
809
810 let mut cursor = MIN_LEN + HEADER_FIXED; if let Some(pts) = h.pts {
814 let prefix = if h.dts.is_some() { 0b0011u8 } else { 0b0010u8 };
815 buf[cursor..cursor + 5].copy_from_slice(×tamp::write(pts.0, prefix));
816 cursor += 5;
817 }
818 if let Some(dts) = h.dts {
819 buf[cursor..cursor + 5].copy_from_slice(×tamp::write(dts.0, 0b0001));
820 cursor += 5;
821 }
822 if let Some(escr) = h.escr {
824 buf[cursor..cursor + 6].copy_from_slice(&escr.to_field_bytes());
825 cursor += 6;
826 }
827 if let Some(rate) = h.es_rate {
829 buf[cursor] = 0x80 | ((rate >> 15) as u8 & 0x7F);
830 buf[cursor + 1] = ((rate >> 7) & 0xFF) as u8;
831 buf[cursor + 2] = (((rate & 0x7F) as u8) << 1) | 0x01;
832 cursor += 3;
833 }
834 if let Some(tm) = h.dsm_trick_mode {
836 buf[cursor] = tm.to_byte();
837 cursor += 1;
838 }
839 if let Some(aci) = h.additional_copy_info {
841 buf[cursor] = 0x80 | (aci & 0x7F);
842 cursor += 1;
843 }
844 if let Some(crc) = h.pes_crc {
846 buf[cursor..cursor + 2].copy_from_slice(&crc.to_be_bytes());
847 cursor += 2;
848 }
849 if let Some(ref ext) = h.pes_extension {
851 let written = ext.serialize_into(&mut buf[cursor..])?;
852 cursor += written;
853 }
854
855 for b in buf[cursor..cursor + h.header_stuffing_len].iter_mut() {
858 *b = PES_HEADER_STUFFING_BYTE;
859 }
860 cursor += h.header_stuffing_len;
861
862 cursor
863 }
864 };
865 buf[payload_at..len].copy_from_slice(self.payload);
866 Ok(len)
867 }
868}
869
870#[cfg(test)]
871mod tests {
872 use super::*;
873 extern crate alloc;
874 use alloc::vec;
875
876 fn round_trip(b: &[u8]) {
877 let pkt = PesPacket::parse(b).unwrap();
878 let mut out = vec![0u8; pkt.serialized_len()];
879 pkt.serialize_into(&mut out).unwrap();
880 assert_eq!(&out[..], b, "round-trip mismatch");
881 let re = PesPacket::parse(&out).unwrap();
882 let mut re_out = vec![0u8; re.serialized_len()];
884 re.serialize_into(&mut re_out).unwrap();
885 assert_eq!(out, re_out, "re-parse mismatch");
886 }
887
888 #[test]
889 fn video_pts_only() {
890 let b = [
892 0x00, 0x00, 0x01, 0xE0, 0x00, 0x0A, 0x80, 0x80, 0x05, 0x21, 0x00, 0x01, 0x00, 0x01,
893 0xAA, 0xBB,
894 ];
895 let pkt = PesPacket::parse(&b).unwrap();
896 assert_eq!(pkt.stream_id, StreamId(0xE0));
897 let h = pkt.header.as_ref().unwrap();
898 assert_eq!(h.pts, Some(Pts(0)));
899 assert!(h.dts.is_none());
900 assert_eq!(pkt.payload, &[0xAA, 0xBB]);
901 round_trip(&b);
902 }
903
904 #[test]
905 fn pts_and_dts() {
906 let b = [
908 0x00, 0x00, 0x01, 0xE0, 0x00, 0x0F, 0x80, 0xC0, 0x0A, 0x31, 0x00, 0x03, 0x00, 0x01,
909 0x11, 0x00, 0x05, 0x00, 0x01, 0xCC,
910 ];
911 let pkt = PesPacket::parse(&b).unwrap();
912 let h = pkt.header.as_ref().unwrap();
913 assert!(h.pts.is_some());
914 assert!(h.dts.is_some());
915 round_trip(&b);
916 }
917
918 #[test]
919 fn pes_header_stuffing_round_trip() {
920 let b = [
923 0x00, 0x00, 0x01, 0xE0, 0x00, 0x0C, 0x80, 0x80, 0x08, 0x21, 0x00, 0x01, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xAA, ];
928 let pkt = PesPacket::parse(&b).unwrap();
929 let h = pkt.header.as_ref().unwrap();
930 assert!(h.pts.is_some());
931 assert_eq!(
932 h.header_stuffing_len, 3,
933 "3 stuffing bytes after the 5-byte PTS"
934 );
935 assert_eq!(pkt.serialized_len(), b.len());
937 round_trip(&b); }
939
940 #[test]
941 fn special_stream_no_header() {
942 let b = [0x00, 0x00, 0x01, 0xBE, 0x00, 0x03, 0xFF, 0xFF, 0xFF];
944 let pkt = PesPacket::parse(&b).unwrap();
945 assert!(pkt.header.is_none());
946 assert_eq!(pkt.payload, &[0xFF, 0xFF, 0xFF]);
947 round_trip(&b);
948 }
949
950 #[test]
951 fn unbounded_length_zero() {
952 let b = [
954 0x00, 0x00, 0x01, 0xE0, 0x00, 0x00, 0x80, 0x80, 0x05, 0x21, 0x00, 0x01, 0x00, 0x01,
955 0x01, 0x02, 0x03,
956 ];
957 let pkt = PesPacket::parse(&b).unwrap();
958 assert_eq!(pkt.pes_packet_length, 0);
959 assert_eq!(pkt.payload, &[0x01, 0x02, 0x03]);
960 round_trip(&b);
961 }
962
963 #[test]
964 fn rejects_bad_start_code() {
965 let err = PesPacket::parse(&[0x00, 0x00, 0x02, 0xE0, 0x00, 0x00]).unwrap_err();
966 assert!(matches!(err, Error::BadStartCode(0x000002)));
967 }
968
969 #[test]
970 fn rejects_short() {
971 let err = PesPacket::parse(&[0x00, 0x00, 0x01]).unwrap_err();
972 assert!(matches!(err, Error::BufferTooShort { .. }));
973 }
974
975 #[test]
976 fn serialize_rejects_oversized_optional_fields() {
977 let _ = Error::OptionalFieldsTooLarge(256);
983 }
984
985 fn build_pes(h: PesHeader<'_>, payload: &[u8]) -> alloc::vec::Vec<u8> {
988 let pkt = PesPacket {
989 stream_id: StreamId(0xE0),
990 pes_packet_length: 0, header: Some(h),
992 payload,
993 };
994 let mut out = vec![0u8; pkt.serialized_len()];
995 pkt.serialize_into(&mut out).unwrap();
996 out
997 }
998
999 fn empty_header<'a>() -> PesHeader<'a> {
1000 PesHeader {
1001 scrambling_control: 0,
1002 pes_priority: false,
1003 data_alignment_indicator: false,
1004 copyright: false,
1005 original_or_copy: false,
1006 pts: None,
1007 dts: None,
1008 escr: None,
1009 es_rate: None,
1010 dsm_trick_mode: None,
1011 additional_copy_info: None,
1012 pes_crc: None,
1013 pes_extension: None,
1014 header_stuffing_len: 0,
1015 }
1016 }
1017
1018 #[test]
1020 fn pes_header_escr_round_trip() {
1021 let escr = Escr {
1022 base: 90_000,
1023 extension: 150,
1024 };
1025 let h = PesHeader {
1026 escr: Some(escr),
1027 ..empty_header()
1028 };
1029 let bytes = build_pes(h, &[0xAA]);
1030 let pkt = PesPacket::parse(&bytes).unwrap();
1031 assert_eq!(pkt.header.unwrap().escr, Some(escr));
1032 }
1033
1034 #[test]
1036 fn escr_27mhz_round_trip() {
1037 for ticks in [0u64, 1, 300, 27_000_000, 8_589_934_591] {
1038 let e = Escr::from_27mhz(ticks);
1039 assert_eq!(e.as_27mhz(), ticks, "ticks={ticks}");
1040 }
1041 }
1042
1043 #[test]
1045 fn escr_field_bytes_round_trip() {
1046 for (base, ext) in [
1047 (0u64, 0u16),
1048 (10_000, 0),
1049 (0x1_FFFF_FFFF, 0x1FF),
1050 (1234, 56),
1051 ] {
1052 let e = Escr {
1053 base,
1054 extension: ext,
1055 };
1056 let bytes = e.to_field_bytes();
1057 let decoded = Escr::from_field_bytes(&bytes).unwrap();
1058 assert_eq!(decoded, e, "base={base} ext={ext}");
1059 }
1060 }
1061
1062 #[test]
1064 fn pes_header_es_rate_round_trip() {
1065 let h = PesHeader {
1066 es_rate: Some(0x3FFFFF),
1067 ..empty_header()
1068 };
1069 let bytes = build_pes(h, &[]);
1070 let pkt = PesPacket::parse(&bytes).unwrap();
1071 assert_eq!(pkt.header.unwrap().es_rate, Some(0x3FFFFF));
1072 }
1073
1074 #[test]
1076 fn trick_mode_all_variants_round_trip() {
1077 let cases = [
1078 TrickMode::FastForward {
1079 field_id: 0x2,
1080 intra_slice_refresh: true,
1081 frequency_truncation: 0x3,
1082 },
1083 TrickMode::SlowMotion { rep_cntrl: 0x1F },
1084 TrickMode::FreezeFrame { field_id: 0x1 },
1085 TrickMode::FastReverse {
1086 field_id: 0x0,
1087 intra_slice_refresh: false,
1088 frequency_truncation: 0x1,
1089 },
1090 TrickMode::SlowReverse { rep_cntrl: 0 },
1091 TrickMode::Reserved {
1092 trick_mode_control: 0b101,
1093 data: 0x1A,
1094 },
1095 ];
1096 for tm in cases {
1097 let b = tm.to_byte();
1098 let decoded = TrickMode::from_byte(b);
1099 assert_eq!(decoded, tm, "tm={tm:?}");
1100 }
1101 }
1102
1103 #[test]
1105 fn pes_header_trick_mode_round_trip() {
1106 let tm = TrickMode::FastForward {
1107 field_id: 1,
1108 intra_slice_refresh: false,
1109 frequency_truncation: 2,
1110 };
1111 let h = PesHeader {
1112 dsm_trick_mode: Some(tm),
1113 ..empty_header()
1114 };
1115 let bytes = build_pes(h, &[]);
1116 let pkt = PesPacket::parse(&bytes).unwrap();
1117 assert_eq!(pkt.header.unwrap().dsm_trick_mode, Some(tm));
1118 }
1119
1120 #[test]
1122 fn pes_header_additional_copy_info_round_trip() {
1123 let h = PesHeader {
1124 additional_copy_info: Some(0x7F),
1125 ..empty_header()
1126 };
1127 let bytes = build_pes(h, &[]);
1128 let pkt = PesPacket::parse(&bytes).unwrap();
1129 assert_eq!(pkt.header.unwrap().additional_copy_info, Some(0x7F));
1130 }
1131
1132 #[test]
1134 fn pes_header_pes_crc_round_trip() {
1135 let h = PesHeader {
1136 pes_crc: Some(0xDEAD),
1137 ..empty_header()
1138 };
1139 let bytes = build_pes(h, &[]);
1140 let pkt = PesPacket::parse(&bytes).unwrap();
1141 assert_eq!(pkt.header.unwrap().pes_crc, Some(0xDEAD));
1142 }
1143
1144 #[test]
1146 fn pes_extension_ppsc_round_trip() {
1147 let ppsc = ProgramPacketSequenceCounter {
1148 counter: 42,
1149 mpeg1_mpeg2_identifier: true,
1150 original_stuff_length: 7,
1151 };
1152 let ext = PesExtension {
1153 pes_private_data: None,
1154 pack_header: None,
1155 program_packet_sequence_counter: Some(ppsc),
1156 p_std_buffer: None,
1157 pes_extension_field: None,
1158 };
1159 let h = PesHeader {
1160 pes_extension: Some(ext),
1161 ..empty_header()
1162 };
1163 let bytes = build_pes(h, &[]);
1164 let pkt = PesPacket::parse(&bytes).unwrap();
1165 let decoded_ext = pkt.header.unwrap().pes_extension.unwrap();
1166 assert_eq!(decoded_ext.program_packet_sequence_counter, Some(ppsc));
1167 }
1168
1169 #[test]
1171 fn pes_extension_p_std_buffer_round_trip() {
1172 let pstd = PStdBuffer {
1173 scale: true,
1174 size: 0x1FFF,
1175 };
1176 let ext = PesExtension {
1177 pes_private_data: None,
1178 pack_header: None,
1179 program_packet_sequence_counter: None,
1180 p_std_buffer: Some(pstd),
1181 pes_extension_field: None,
1182 };
1183 let h = PesHeader {
1184 pes_extension: Some(ext),
1185 ..empty_header()
1186 };
1187 let bytes = build_pes(h, &[]);
1188 let pkt = PesPacket::parse(&bytes).unwrap();
1189 let decoded_ext = pkt.header.unwrap().pes_extension.unwrap();
1190 assert_eq!(decoded_ext.p_std_buffer, Some(pstd));
1191 }
1192
1193 #[test]
1195 fn pes_extension_private_data_round_trip() {
1196 let pd: [u8; 16] = [
1197 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,
1198 0x0F, 0x10,
1199 ];
1200 let ext = PesExtension {
1201 pes_private_data: Some(pd),
1202 pack_header: None,
1203 program_packet_sequence_counter: None,
1204 p_std_buffer: None,
1205 pes_extension_field: None,
1206 };
1207 let h = PesHeader {
1208 pes_extension: Some(ext),
1209 ..empty_header()
1210 };
1211 let bytes = build_pes(h, &[]);
1212 let pkt = PesPacket::parse(&bytes).unwrap();
1213 let decoded_ext = pkt.header.unwrap().pes_extension.unwrap();
1214 assert_eq!(decoded_ext.pes_private_data, Some(pd));
1215 }
1216
1217 #[test]
1219 fn pes_header_all_fields_round_trip() {
1220 let ppsc = ProgramPacketSequenceCounter {
1221 counter: 1,
1222 mpeg1_mpeg2_identifier: false,
1223 original_stuff_length: 0,
1224 };
1225 let ext = PesExtension {
1226 pes_private_data: None,
1227 pack_header: None,
1228 program_packet_sequence_counter: Some(ppsc),
1229 p_std_buffer: Some(PStdBuffer {
1230 scale: false,
1231 size: 100,
1232 }),
1233 pes_extension_field: None,
1234 };
1235 let h = PesHeader {
1236 scrambling_control: 0,
1237 pes_priority: true,
1238 data_alignment_indicator: false,
1239 copyright: false,
1240 original_or_copy: true,
1241 pts: Some(Pts(90_000)),
1242 dts: Some(Dts(85_000)),
1243 escr: Some(Escr {
1244 base: 1000,
1245 extension: 0,
1246 }),
1247 es_rate: Some(50_000),
1248 dsm_trick_mode: Some(TrickMode::SlowMotion { rep_cntrl: 3 }),
1249 additional_copy_info: Some(5),
1250 pes_crc: Some(0xCAFE),
1251 pes_extension: Some(ext),
1252 header_stuffing_len: 0,
1253 };
1254 let bytes = build_pes(h, &[0xFF]);
1255 let pkt = PesPacket::parse(&bytes).unwrap();
1256 let dh = pkt.header.unwrap();
1257 assert_eq!(dh.pts, Some(Pts(90_000)));
1258 assert_eq!(dh.dts, Some(Dts(85_000)));
1259 assert!(dh.escr.is_some());
1260 assert_eq!(dh.es_rate, Some(50_000));
1261 assert_eq!(
1262 dh.dsm_trick_mode,
1263 Some(TrickMode::SlowMotion { rep_cntrl: 3 })
1264 );
1265 assert_eq!(dh.additional_copy_info, Some(5));
1266 assert_eq!(dh.pes_crc, Some(0xCAFE));
1267 assert!(dh.pes_extension.is_some());
1268 }
1269}