Skip to main content

mpeg_pes/
packet.rs

1//! PES packet header parsing (ISO/IEC 13818-1 §2.4.3.6, Table 2-21).
2
3use 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; // start_code(3) + stream_id(1) + PES_packet_length(2)
9const HEADER_FIXED: usize = 3; // 2 flag bytes + PES_header_data_length
10/// PES header stuffing byte (ISO/IEC 13818-1:2007 §2.4.3.7 — `0xFF`).
11const PES_HEADER_STUFFING_BYTE: u8 = 0xFF;
12
13// ── ESCR (ISO/IEC 13818-1 §2.4.3.7 Table 2-21) ──────────────────────────────
14
15/// Elementary Stream Clock Reference: 33-bit base (90 kHz) + 9-bit extension
16/// (27 MHz) — ISO/IEC 13818-1 §2.4.3.7, Table 2-21.
17///
18/// Wire layout (6 bytes, 48 bits):
19/// `2×reserved(1) | ESCR_base[32:30](3) | marker(1) | ESCR_base[29:15](15) |
20///  marker(1) | ESCR_base[14:0](15) | marker(1) | ESCR_ext(9) | marker(1)`.
21#[derive(Debug, Clone, Copy, PartialEq, Eq)]
22#[cfg_attr(feature = "serde", derive(serde::Serialize))]
23pub struct Escr {
24    /// 33-bit base (90 kHz units).
25    pub base: u64,
26    /// 9-bit extension (27 MHz units, 0..=299).
27    pub extension: u16,
28}
29
30impl Escr {
31    /// Full ESCR value on the 27 MHz clock: `base * 300 + extension`.
32    #[must_use]
33    pub fn as_27mhz(self) -> u64 {
34        self.base * 300 + self.extension as u64
35    }
36
37    /// Construct from an absolute 27 MHz clock value.
38    #[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    /// Decode from the 6-byte ESCR field.
49    ///
50    /// Bit layout (ISO/IEC 13818-1 §2.4.3.7, Table 2-21):
51    /// `B0[7:6]`=reserved, `B0[5:3]`=`base[32:30]`, `B0[2]`=marker,
52    /// `B0[1:0]`=`base[29:28]`, `B1[7:0]`=`base[27:20]`,
53    /// `B2[7:3]`=`base[19:15]`, `B2[2]`=marker, `B2[1:0]`=`base[14:13]`,
54    /// `B3[7:0]`=`base[12:5]`,
55    /// `B4[7:3]`=`base[4:0]`, `B4[2]`=marker, `B4[1:0]`=`ext[8:7]`,
56    /// `B5[7:1]`=`ext[6:0]`, `B5[0]`=marker.
57    pub fn from_field_bytes(b: &[u8; 6]) -> Result<Self> {
58        let base = ((((b[0] >> 3) & 0x07) as u64) << 30)   // base[32:30]
59            | (((b[0] & 0x03) as u64) << 28)                 // base[29:28]
60            | ((b[1] as u64) << 20)                           // base[27:20]
61            | ((((b[2] >> 3) & 0x1F) as u64) << 15)          // base[19:15]
62            | (((b[2] & 0x03) as u64) << 13)                  // base[14:13]
63            | ((b[3] as u64) << 5)                             // base[12:5]
64            | (((b[4] >> 3) & 0x1F) as u64); // base[4:0]
65        let extension = ((((b[4] & 0x03) as u16) << 7) | ((b[5] >> 1) as u16)) & 0x1FF;
66        Ok(Self { base, extension })
67    }
68
69    /// Encode as the 6-byte ESCR field.
70    ///
71    /// Reserved bits are set to `1` per the spec convention.
72    /// Exact inverse of [`from_field_bytes`](Self::from_field_bytes).
73    #[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            // B0: reserved(2)='11' | base[32:30](3) | marker(1)='1' | base[29:28](2)
79            0xC0 | (((b >> 30) & 0x07) as u8) << 3 | 0x04 | ((b >> 28) & 0x03) as u8,
80            // B1: base[27:20](8)
81            ((b >> 20) & 0xFF) as u8,
82            // B2: base[19:15](5) | marker(1)='1' | base[14:13](2)
83            (((b >> 15) & 0x1F) as u8) << 3 | 0x04 | ((b >> 13) & 0x03) as u8,
84            // B3: base[12:5](8)
85            ((b >> 5) & 0xFF) as u8,
86            // B4: base[4:0](5) | marker(1)='1' | ext[8:7](2)
87            (((b & 0x1F) as u8) << 3) | 0x04 | ((e >> 7) & 0x03) as u8,
88            // B5: ext[6:0](7) | marker(1)='1'
89            (((e & 0x7F) as u8) << 1) | 0x01,
90        ]
91    }
92}
93
94// ── DSM trick mode (ISO/IEC 13818-1 §2.4.3.7, Table 2-24) ──────────────────
95
96/// Trick-mode control values for the `DSM_trick_mode_flag` field
97/// (ISO/IEC 13818-1 §2.4.3.7, Table 2-24).
98#[derive(Debug, Clone, Copy, PartialEq, Eq)]
99#[cfg_attr(feature = "serde", derive(serde::Serialize))]
100#[non_exhaustive]
101pub enum TrickMode {
102    /// `000` — fast forward.
103    FastForward {
104        /// 2-bit `field_id`.
105        field_id: u8,
106        /// `intra_slice_refresh` flag.
107        intra_slice_refresh: bool,
108        /// 2-bit `frequency_truncation`.
109        frequency_truncation: u8,
110    },
111    /// `001` — slow motion.
112    SlowMotion {
113        /// 5-bit `rep_cntrl`.
114        rep_cntrl: u8,
115    },
116    /// `010` — freeze frame.
117    FreezeFrame {
118        /// 2-bit `field_id`.
119        field_id: u8,
120    },
121    /// `011` — fast reverse.
122    FastReverse {
123        /// 2-bit `field_id`.
124        field_id: u8,
125        /// `intra_slice_refresh` flag.
126        intra_slice_refresh: bool,
127        /// 2-bit `frequency_truncation`.
128        frequency_truncation: u8,
129    },
130    /// `100` — slow reverse.
131    SlowReverse {
132        /// 5-bit `rep_cntrl`.
133        rep_cntrl: u8,
134    },
135    /// `101`–`111` — reserved.
136    Reserved {
137        /// Raw 3-bit `trick_mode_control` value.
138        trick_mode_control: u8,
139        /// Raw 5-bit remainder.
140        data: u8,
141    },
142}
143
144impl TrickMode {
145    /// Decode from the 1-byte trick-mode field (ISO/IEC 13818-1 §2.4.3.7).
146    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    /// Encode as the 1-byte trick-mode field.
173    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// ── PES extension (ISO/IEC 13818-1 §2.4.3.7) ──────────────────────────────
206
207/// `program_packet_sequence_counter` sub-field of [`PesExtension`].
208#[derive(Debug, Clone, Copy, PartialEq, Eq)]
209#[cfg_attr(feature = "serde", derive(serde::Serialize))]
210pub struct ProgramPacketSequenceCounter {
211    /// 7-bit counter.
212    pub counter: u8,
213    /// `MPEG1_MPEG2_identifier` flag.
214    pub mpeg1_mpeg2_identifier: bool,
215    /// 6-bit `original_stuff_length`.
216    pub original_stuff_length: u8,
217}
218
219/// `P-STD_buffer` sub-field of [`PesExtension`].
220#[derive(Debug, Clone, Copy, PartialEq, Eq)]
221#[cfg_attr(feature = "serde", derive(serde::Serialize))]
222pub struct PStdBuffer {
223    /// P-STD buffer size scale: `false` = 128 bytes/unit, `true` = 1024 bytes/unit.
224    pub scale: bool,
225    /// 13-bit buffer size in units of the scale.
226    pub size: u16,
227}
228
229/// Typed PES header extension sub-structure
230/// (ISO/IEC 13818-1 §2.4.3.7, Table 2-21, `PES_extension_flag = 1`).
231#[derive(Debug, Clone, PartialEq, Eq)]
232#[cfg_attr(feature = "serde", derive(serde::Serialize))]
233pub struct PesExtension<'a> {
234    /// 128-bit PES private data, if `PES_private_data_flag` is set.
235    pub pes_private_data: Option<[u8; 16]>,
236    /// Pack header field bytes (opaque per spec — `&[u8]` is correct here).
237    pub pack_header: Option<&'a [u8]>,
238    /// Program packet sequence counter sub-fields.
239    pub program_packet_sequence_counter: Option<ProgramPacketSequenceCounter>,
240    /// P-STD buffer sub-field.
241    pub p_std_buffer: Option<PStdBuffer>,
242    /// PES extension field bytes (opaque per spec).
243    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    /// Number of bytes this PES extension occupies on the wire.
362    pub fn serialized_len(&self) -> usize {
363        let mut n = 1usize; // flags byte
364        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            // byte 0: marker(1) | counter(7)
423            buf[cursor] = 0x80 | (ppsc.counter & 0x7F);
424            // byte 1: marker(1) | mpeg1_mpeg2_id(1) | original_stuff_length(6)
425            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            // '01' | scale(1) | size(13)
432            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// ── PesHeader ──────────────────────────────────────────────────────────────
447
448/// The optional PES header present for non-special `stream_id`s
449/// (ISO/IEC 13818-1 §2.4.3.6, §2.4.3.7). All optional sub-fields are fully
450/// typed — the raw `optional_fields` blob has been replaced with the
451/// individual decoded fields.
452#[non_exhaustive]
453#[derive(Debug, Clone, PartialEq, Eq)]
454#[cfg_attr(feature = "serde", derive(serde::Serialize))]
455pub struct PesHeader<'a> {
456    /// PES_scrambling_control (2 bits).
457    pub scrambling_control: u8,
458    /// PES_priority.
459    pub pes_priority: bool,
460    /// data_alignment_indicator.
461    pub data_alignment_indicator: bool,
462    /// copyright.
463    pub copyright: bool,
464    /// original_or_copy.
465    pub original_or_copy: bool,
466    /// Presentation time stamp, if `PTS_DTS_flags` indicated one.
467    pub pts: Option<Pts>,
468    /// Decoding time stamp, if `PTS_DTS_flags` was `11`.
469    pub dts: Option<Dts>,
470    /// Elementary stream clock reference (6 bytes), if `ESCR_flag` is set.
471    pub escr: Option<Escr>,
472    /// 22-bit ES rate (bytes/second × 50), if `ES_rate_flag` is set.
473    pub es_rate: Option<u32>,
474    /// DSM trick-mode control byte (typed), if `DSM_trick_mode_flag` is set.
475    pub dsm_trick_mode: Option<TrickMode>,
476    /// 7-bit `additional_copy_info`, if `additional_copy_info_flag` is set.
477    pub additional_copy_info: Option<u8>,
478    /// Previous PES packet CRC (16 bits), if `PES_CRC_flag` is set.
479    pub pes_crc: Option<u16>,
480    /// PES extension sub-structure, if `PES_extension_flag` is set.
481    pub pes_extension: Option<PesExtension<'a>>,
482    /// Number of trailing `0xFF` stuffing bytes inside the `PES_header_data_length`
483    /// region, after the typed optional fields (ISO/IEC 13818-1:2007 §2.4.3.7 —
484    /// "stuffing_byte: fixed 8-bit value `0xFF`").
485    ///
486    /// Encoders pad the optional-header block (often so the elementary stream
487    /// starts at a fixed offset). These bytes are part of the wire image;
488    /// capturing the count lets [`PesPacket::serialize_into`] reproduce the
489    /// header byte-for-byte. Set to `0` when constructing a header with no
490    /// stuffing.
491    pub header_stuffing_len: usize,
492}
493
494impl PesHeader<'_> {
495    /// Number of bytes this header occupies in the serialized `PES_header_data_length`
496    /// region (not counting the 3 fixed header bytes — the 2 flag bytes + length byte).
497    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/// A parsed PES packet.
529#[derive(Debug, Clone, PartialEq, Eq)]
530#[cfg_attr(feature = "serde", derive(serde::Serialize))]
531pub struct PesPacket<'a> {
532    /// stream_id (Table 2-22).
533    pub stream_id: StreamId,
534    /// PES_packet_length as carried; `0` means unbounded (video).
535    pub pes_packet_length: u16,
536    /// Optional PES header (absent for the special `stream_id`s).
537    pub header: Option<PesHeader<'a>>,
538    /// The elementary-stream bytes (`PES_packet_data_byte`s).
539    #[cfg_attr(feature = "serde", serde(skip))]
540    pub payload: &'a [u8],
541}
542
543impl<'a> PesPacket<'a> {
544    /// Parse a PES packet from the bytes starting at its `packet_start_code_prefix`.
545    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        // Where the payload ends: bounded by PES_packet_length unless 0 (unbounded).
561        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; // = 9
587        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        // PTS/DTS (ISO/IEC 13818-1 §2.4.3.7 Table 2-21).
599        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        // ESCR (6 bytes, ISO/IEC 13818-1 §2.4.3.7).
631        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        // ES_rate (3 bytes: 1 marker + 22-bit rate + 1 marker).
648        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        // DSM trick mode (1 byte).
666        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        // additional_copy_info (1 byte: marker + 7-bit info).
682        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        // PES_CRC (2 bytes).
698        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        // PES_extension.
714        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        // Bytes remaining in the `PES_header_data_length` region after the typed
723        // optional fields are `0xFF` stuffing (ISO/IEC 13818-1:2007 §2.4.3.7).
724        // Record the count so serialization reproduces the header byte-for-byte.
725        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    /// Serialized length in bytes.
753    #[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    /// Serialize back to bytes (byte-identical to a spec-compliant input).
763    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                // f1: marker '10' | scrambling(2) | priority(1) | align(1) | copyright(1) | orig(1)
784                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                // f2: pts_dts_flags(2) | escr_flag(1) | es_rate_flag(1) |
792                //     trick_mode(1) | add_copy(1) | crc(1) | ext(1)
793                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; // = 9
811
812                // PTS (and/or DTS).
813                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(&timestamp::write(pts.0, prefix));
816                    cursor += 5;
817                }
818                if let Some(dts) = h.dts {
819                    buf[cursor..cursor + 5].copy_from_slice(&timestamp::write(dts.0, 0b0001));
820                    cursor += 5;
821                }
822                // ESCR.
823                if let Some(escr) = h.escr {
824                    buf[cursor..cursor + 6].copy_from_slice(&escr.to_field_bytes());
825                    cursor += 6;
826                }
827                // ES_rate: marker(1) | rate(22) | marker(1) = 3 bytes.
828                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                // DSM trick mode.
835                if let Some(tm) = h.dsm_trick_mode {
836                    buf[cursor] = tm.to_byte();
837                    cursor += 1;
838                }
839                // additional_copy_info: marker(1) | info(7).
840                if let Some(aci) = h.additional_copy_info {
841                    buf[cursor] = 0x80 | (aci & 0x7F);
842                    cursor += 1;
843                }
844                // PES_CRC (2 bytes, big-endian).
845                if let Some(crc) = h.pes_crc {
846                    buf[cursor..cursor + 2].copy_from_slice(&crc.to_be_bytes());
847                    cursor += 2;
848                }
849                // PES_extension.
850                if let Some(ref ext) = h.pes_extension {
851                    let written = ext.serialize_into(&mut buf[cursor..])?;
852                    cursor += written;
853                }
854
855                // Trailing `0xFF` stuffing inside the header_data_length region
856                // (ISO/IEC 13818-1:2007 §2.4.3.7), reproducing the encoder's pad.
857                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        // Compare without the borrowed lifetime complexity — compare serialized form.
883        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        // stream_id 0xE0, len=0x0A, flags 0x80/0x80, hdl=5, PTS=0, payload AA BB.
891        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        // PTS_DTS_flags=11, hdl=10. PTS prefix 0011, DTS prefix 0001.
907        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        // PTS-only (flags 0x80), PES_header_data_length = 8: 5 PTS bytes + 3
921        // 0xFF stuffing bytes (ISO/IEC 13818-1:2007 §2.4.3.7).
922        let b = [
923            0x00, 0x00, 0x01, 0xE0, 0x00, 0x0C, 0x80, 0x80, 0x08, // hdl = 8
924            0x21, 0x00, 0x01, 0x00, 0x01, // PTS = 0
925            0xFF, 0xFF, 0xFF, // 3 stuffing bytes
926            0xAA, // payload
927        ];
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        // serialized_len must account for the stuffing.
936        assert_eq!(pkt.serialized_len(), b.len());
937        round_trip(&b); // byte-identical, incl. the 0xFF stuffing
938    }
939
940    #[test]
941    fn special_stream_no_header() {
942        // padding_stream 0xBE: bytes after length are payload directly.
943        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        // PES_packet_length=0 (video): payload runs to end of buffer.
953        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        // Construct a PesHeader whose optional_len() > 255 by adding enough flags.
978        // max realistic: 5+5+6+3+1+1+2 = 23, plus PesExtension with big private data.
979        // This is structurally impossible with typed fields to reach 256 naturally,
980        // but we test the guard exists by checking that OptionalFieldsTooLarge
981        // is the right error variant name still exists.
982        let _ = Error::OptionalFieldsTooLarge(256);
983    }
984
985    // ── Typed optional fields round-trips ──────────────────────────────────
986
987    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, // unbounded
991            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    /// Construct PES with ESCR set, serialize, parse, assert field preserved.
1019    #[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    /// Escr::from_27mhz / as_27mhz round-trip.
1035    #[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    /// Escr::to_field_bytes / from_field_bytes round-trip.
1044    #[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    /// ES_rate round-trip.
1063    #[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    /// TrickMode round-trip (all variants).
1075    #[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    /// TrickMode in PES header round-trip.
1104    #[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    /// additional_copy_info round-trip.
1121    #[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    /// PES_CRC round-trip.
1133    #[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    /// PesExtension with program_packet_sequence_counter.
1145    #[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    /// PesExtension with P-STD buffer.
1170    #[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    /// PesExtension with private data (16 bytes).
1194    #[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    /// All optional PES header fields set at once — serialize and parse round-trip.
1218    #[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}