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.8, Table 2-24) ──────────────────
95
96/// Trick-mode control values for the `DSM_trick_mode_flag` field
97/// (ISO/IEC 13818-1 §2.4.3.8, Table 2-24).
98#[derive(Debug, Clone, Copy, PartialEq, Eq)]
99#[cfg_attr(feature = "serde", derive(serde::Serialize))]
100pub enum TrickMode {
101    /// `000` — fast forward.
102    FastForward {
103        /// 2-bit `field_id`.
104        field_id: u8,
105        /// `intra_slice_refresh` flag.
106        intra_slice_refresh: bool,
107        /// 2-bit `frequency_truncation`.
108        frequency_truncation: u8,
109    },
110    /// `001` — slow motion.
111    SlowMotion {
112        /// 5-bit `rep_cntrl`.
113        rep_cntrl: u8,
114    },
115    /// `010` — freeze frame.
116    FreezeFrame {
117        /// 2-bit `field_id`.
118        field_id: u8,
119    },
120    /// `011` — fast reverse.
121    FastReverse {
122        /// 2-bit `field_id`.
123        field_id: u8,
124        /// `intra_slice_refresh` flag.
125        intra_slice_refresh: bool,
126        /// 2-bit `frequency_truncation`.
127        frequency_truncation: u8,
128    },
129    /// `100` — slow reverse.
130    SlowReverse {
131        /// 5-bit `rep_cntrl`.
132        rep_cntrl: u8,
133    },
134    /// `101`–`111` — reserved.
135    Reserved {
136        /// Raw 3-bit `trick_mode_control` value.
137        trick_mode_control: u8,
138        /// Raw 5-bit remainder.
139        data: u8,
140    },
141}
142
143impl TrickMode {
144    /// Decode from the 1-byte trick-mode field (ISO/IEC 13818-1 §2.4.3.8).
145    pub fn from_byte(b: u8) -> Self {
146        let control = (b >> 5) & 0x07;
147        let data = b & 0x1F;
148        match control {
149            0b000 => TrickMode::FastForward {
150                field_id: (data >> 3) & 0x03,
151                intra_slice_refresh: (data >> 2) & 0x01 != 0,
152                frequency_truncation: data & 0x03,
153            },
154            0b001 => TrickMode::SlowMotion { rep_cntrl: data },
155            0b010 => TrickMode::FreezeFrame {
156                field_id: (data >> 3) & 0x03,
157            },
158            0b011 => TrickMode::FastReverse {
159                field_id: (data >> 3) & 0x03,
160                intra_slice_refresh: (data >> 2) & 0x01 != 0,
161                frequency_truncation: data & 0x03,
162            },
163            0b100 => TrickMode::SlowReverse { rep_cntrl: data },
164            _ => TrickMode::Reserved {
165                trick_mode_control: control,
166                data,
167            },
168        }
169    }
170
171    /// Encode as the 1-byte trick-mode field.
172    pub fn to_byte(self) -> u8 {
173        match self {
174            TrickMode::FastForward {
175                field_id,
176                intra_slice_refresh,
177                frequency_truncation,
178            } => {
179                ((field_id & 0x03) << 3)
180                    | ((intra_slice_refresh as u8) << 2)
181                    | (frequency_truncation & 0x03)
182            }
183            TrickMode::SlowMotion { rep_cntrl } => (0b001 << 5) | (rep_cntrl & 0x1F),
184            TrickMode::FreezeFrame { field_id } => (0b010 << 5) | ((field_id & 0x03) << 3),
185            TrickMode::FastReverse {
186                field_id,
187                intra_slice_refresh,
188                frequency_truncation,
189            } => {
190                (0b011 << 5)
191                    | ((field_id & 0x03) << 3)
192                    | ((intra_slice_refresh as u8) << 2)
193                    | (frequency_truncation & 0x03)
194            }
195            TrickMode::SlowReverse { rep_cntrl } => (0b100 << 5) | (rep_cntrl & 0x1F),
196            TrickMode::Reserved {
197                trick_mode_control,
198                data,
199            } => ((trick_mode_control & 0x07) << 5) | (data & 0x1F),
200        }
201    }
202}
203
204// ── PES extension (ISO/IEC 13818-1 §2.4.3.7) ──────────────────────────────
205
206/// `program_packet_sequence_counter` sub-field of [`PesExtension`].
207#[derive(Debug, Clone, Copy, PartialEq, Eq)]
208#[cfg_attr(feature = "serde", derive(serde::Serialize))]
209pub struct ProgramPacketSequenceCounter {
210    /// 7-bit counter.
211    pub counter: u8,
212    /// `MPEG1_MPEG2_identifier` flag.
213    pub mpeg1_mpeg2_identifier: bool,
214    /// 6-bit `original_stuff_length`.
215    pub original_stuff_length: u8,
216}
217
218/// `P-STD_buffer` sub-field of [`PesExtension`].
219#[derive(Debug, Clone, Copy, PartialEq, Eq)]
220#[cfg_attr(feature = "serde", derive(serde::Serialize))]
221pub struct PStdBuffer {
222    /// P-STD buffer size scale: `false` = 128 bytes/unit, `true` = 1024 bytes/unit.
223    pub scale: bool,
224    /// 13-bit buffer size in units of the scale.
225    pub size: u16,
226}
227
228/// Typed PES header extension sub-structure
229/// (ISO/IEC 13818-1 §2.4.3.7, Table 2-21, `PES_extension_flag = 1`).
230#[derive(Debug, Clone, PartialEq, Eq)]
231#[cfg_attr(feature = "serde", derive(serde::Serialize))]
232pub struct PesExtension<'a> {
233    /// 128-bit PES private data, if `PES_private_data_flag` is set.
234    pub pes_private_data: Option<[u8; 16]>,
235    /// Pack header field bytes (opaque per spec — `&[u8]` is correct here).
236    pub pack_header: Option<&'a [u8]>,
237    /// Program packet sequence counter sub-fields.
238    pub program_packet_sequence_counter: Option<ProgramPacketSequenceCounter>,
239    /// P-STD buffer sub-field.
240    pub p_std_buffer: Option<PStdBuffer>,
241    /// PES extension field bytes (opaque per spec).
242    pub pes_extension_field: Option<&'a [u8]>,
243}
244
245impl<'a> PesExtension<'a> {
246    fn parse(data: &'a [u8]) -> Result<Self> {
247        if data.is_empty() {
248            return Err(Error::BufferTooShort {
249                need: 1,
250                have: 0,
251                what: "PES_extension flags byte",
252            });
253        }
254        let flags = data[0];
255        let mut cursor = 1usize;
256
257        let pes_private_data = if flags & 0x80 != 0 {
258            let end = cursor + 16;
259            let arr: [u8; 16] = data
260                .get(cursor..end)
261                .and_then(|s| s.try_into().ok())
262                .ok_or(Error::BufferTooShort {
263                    need: end,
264                    have: data.len(),
265                    what: "PES_private_data",
266                })?;
267            cursor = end;
268            Some(arr)
269        } else {
270            None
271        };
272
273        let pack_header = if flags & 0x40 != 0 {
274            let pack_len = *data.get(cursor).ok_or(Error::BufferTooShort {
275                need: cursor + 1,
276                have: data.len(),
277                what: "pack_field_length",
278            })? as usize;
279            cursor += 1;
280            let end = cursor + pack_len;
281            let slice = data.get(cursor..end).ok_or(Error::BufferTooShort {
282                need: end,
283                have: data.len(),
284                what: "pack_header",
285            })?;
286            cursor = end;
287            Some(slice)
288        } else {
289            None
290        };
291
292        let program_packet_sequence_counter = if flags & 0x20 != 0 {
293            if data.len() < cursor + 2 {
294                return Err(Error::BufferTooShort {
295                    need: cursor + 2,
296                    have: data.len(),
297                    what: "program_packet_sequence_counter",
298                });
299            }
300            let b0 = data[cursor];
301            let b1 = data[cursor + 1];
302            cursor += 2;
303            Some(ProgramPacketSequenceCounter {
304                counter: b0 & 0x7F,
305                mpeg1_mpeg2_identifier: (b1 & 0x40) != 0,
306                original_stuff_length: b1 & 0x3F,
307            })
308        } else {
309            None
310        };
311
312        let p_std_buffer = if flags & 0x10 != 0 {
313            if data.len() < cursor + 2 {
314                return Err(Error::BufferTooShort {
315                    need: cursor + 2,
316                    have: data.len(),
317                    what: "P-STD_buffer",
318                });
319            }
320            let b0 = data[cursor];
321            let b1 = data[cursor + 1];
322            cursor += 2;
323            Some(PStdBuffer {
324                scale: (b0 & 0x20) != 0,
325                size: (((b0 & 0x1F) as u16) << 8) | (b1 as u16),
326            })
327        } else {
328            None
329        };
330
331        let pes_extension_field = if flags & 0x01 != 0 {
332            let ext_len = *data.get(cursor).ok_or(Error::BufferTooShort {
333                need: cursor + 1,
334                have: data.len(),
335                what: "PES_extension_field_length",
336            })? as usize;
337            cursor += 1;
338            let end = cursor + ext_len;
339            let slice = data.get(cursor..end).ok_or(Error::BufferTooShort {
340                need: end,
341                have: data.len(),
342                what: "PES_extension_field",
343            })?;
344            cursor = end;
345            Some(slice)
346        } else {
347            None
348        };
349        let _ = cursor;
350
351        Ok(PesExtension {
352            pes_private_data,
353            pack_header,
354            program_packet_sequence_counter,
355            p_std_buffer,
356            pes_extension_field,
357        })
358    }
359
360    /// Number of bytes this PES extension occupies on the wire.
361    pub fn serialized_len(&self) -> usize {
362        let mut n = 1usize; // flags byte
363        if self.pes_private_data.is_some() {
364            n += 16;
365        }
366        if let Some(ph) = self.pack_header {
367            n += 1 + ph.len();
368        }
369        if self.program_packet_sequence_counter.is_some() {
370            n += 2;
371        }
372        if self.p_std_buffer.is_some() {
373            n += 2;
374        }
375        if let Some(ef) = self.pes_extension_field {
376            n += 1 + ef.len();
377        }
378        n
379    }
380
381    fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
382        let need = self.serialized_len();
383        if buf.len() < need {
384            return Err(Error::BufferTooShort {
385                need,
386                have: buf.len(),
387                what: "PES_extension serialize output",
388            });
389        }
390
391        let mut flags = 0u8;
392        if self.pes_private_data.is_some() {
393            flags |= 0x80;
394        }
395        if self.pack_header.is_some() {
396            flags |= 0x40;
397        }
398        if self.program_packet_sequence_counter.is_some() {
399            flags |= 0x20;
400        }
401        if self.p_std_buffer.is_some() {
402            flags |= 0x10;
403        }
404        if self.pes_extension_field.is_some() {
405            flags |= 0x01;
406        }
407        buf[0] = flags;
408        let mut cursor = 1usize;
409
410        if let Some(pd) = &self.pes_private_data {
411            buf[cursor..cursor + 16].copy_from_slice(pd);
412            cursor += 16;
413        }
414        if let Some(ph) = self.pack_header {
415            buf[cursor] = ph.len() as u8;
416            cursor += 1;
417            buf[cursor..cursor + ph.len()].copy_from_slice(ph);
418            cursor += ph.len();
419        }
420        if let Some(ppsc) = self.program_packet_sequence_counter {
421            // byte 0: marker(1) | counter(7)
422            buf[cursor] = 0x80 | (ppsc.counter & 0x7F);
423            // byte 1: marker(1) | mpeg1_mpeg2_id(1) | original_stuff_length(6)
424            buf[cursor + 1] = 0x80
425                | ((ppsc.mpeg1_mpeg2_identifier as u8) << 6)
426                | (ppsc.original_stuff_length & 0x3F);
427            cursor += 2;
428        }
429        if let Some(ps) = self.p_std_buffer {
430            // '01' | scale(1) | size(13)
431            buf[cursor] = 0x40 | ((ps.scale as u8) << 5) | ((ps.size >> 8) as u8 & 0x1F);
432            buf[cursor + 1] = (ps.size & 0xFF) as u8;
433            cursor += 2;
434        }
435        if let Some(ef) = self.pes_extension_field {
436            buf[cursor] = ef.len() as u8;
437            cursor += 1;
438            buf[cursor..cursor + ef.len()].copy_from_slice(ef);
439            cursor += ef.len();
440        }
441        Ok(cursor)
442    }
443}
444
445// ── PesHeader ──────────────────────────────────────────────────────────────
446
447/// The optional PES header present for non-special `stream_id`s
448/// (ISO/IEC 13818-1 §2.4.3.6, §2.4.3.7). All optional sub-fields are fully
449/// typed — the raw `optional_fields` blob has been replaced with the
450/// individual decoded fields.
451#[non_exhaustive]
452#[derive(Debug, Clone, PartialEq, Eq)]
453#[cfg_attr(feature = "serde", derive(serde::Serialize))]
454pub struct PesHeader<'a> {
455    /// PES_scrambling_control (2 bits).
456    pub scrambling_control: u8,
457    /// PES_priority.
458    pub pes_priority: bool,
459    /// data_alignment_indicator.
460    pub data_alignment_indicator: bool,
461    /// copyright.
462    pub copyright: bool,
463    /// original_or_copy.
464    pub original_or_copy: bool,
465    /// Presentation time stamp, if `PTS_DTS_flags` indicated one.
466    pub pts: Option<Pts>,
467    /// Decoding time stamp, if `PTS_DTS_flags` was `11`.
468    pub dts: Option<Dts>,
469    /// Elementary stream clock reference (6 bytes), if `ESCR_flag` is set.
470    pub escr: Option<Escr>,
471    /// 22-bit ES rate (bytes/second × 50), if `ES_rate_flag` is set.
472    pub es_rate: Option<u32>,
473    /// DSM trick-mode control byte (typed), if `DSM_trick_mode_flag` is set.
474    pub dsm_trick_mode: Option<TrickMode>,
475    /// 7-bit `additional_copy_info`, if `additional_copy_info_flag` is set.
476    pub additional_copy_info: Option<u8>,
477    /// Previous PES packet CRC (16 bits), if `PES_CRC_flag` is set.
478    pub pes_crc: Option<u16>,
479    /// PES extension sub-structure, if `PES_extension_flag` is set.
480    pub pes_extension: Option<PesExtension<'a>>,
481    /// Number of trailing `0xFF` stuffing bytes inside the `PES_header_data_length`
482    /// region, after the typed optional fields (ISO/IEC 13818-1:2007 §2.4.3.7 —
483    /// "stuffing_byte: fixed 8-bit value `0xFF`").
484    ///
485    /// Encoders pad the optional-header block (often so the elementary stream
486    /// starts at a fixed offset). These bytes are part of the wire image;
487    /// capturing the count lets [`PesPacket::serialize_into`] reproduce the
488    /// header byte-for-byte. Set to `0` when constructing a header with no
489    /// stuffing.
490    pub header_stuffing_len: usize,
491}
492
493impl PesHeader<'_> {
494    /// Number of bytes this header occupies in the serialized `PES_header_data_length`
495    /// region (not counting the 3 fixed header bytes — the 2 flag bytes + length byte).
496    fn optional_len(&self) -> usize {
497        let mut n = 0usize;
498        if self.pts.is_some() {
499            n += 5;
500        }
501        if self.dts.is_some() {
502            n += 5;
503        }
504        if self.escr.is_some() {
505            n += 6;
506        }
507        if self.es_rate.is_some() {
508            n += 3;
509        }
510        if self.dsm_trick_mode.is_some() {
511            n += 1;
512        }
513        if self.additional_copy_info.is_some() {
514            n += 1;
515        }
516        if self.pes_crc.is_some() {
517            n += 2;
518        }
519        if let Some(ref ext) = self.pes_extension {
520            n += ext.serialized_len();
521        }
522        n += self.header_stuffing_len;
523        n
524    }
525}
526
527/// A parsed PES packet.
528#[derive(Debug, Clone, PartialEq, Eq)]
529#[cfg_attr(feature = "serde", derive(serde::Serialize))]
530pub struct PesPacket<'a> {
531    /// stream_id (Table 2-22).
532    pub stream_id: StreamId,
533    /// PES_packet_length as carried; `0` means unbounded (video).
534    pub pes_packet_length: u16,
535    /// Optional PES header (absent for the special `stream_id`s).
536    pub header: Option<PesHeader<'a>>,
537    /// The elementary-stream bytes (`PES_packet_data_byte`s).
538    #[cfg_attr(feature = "serde", serde(skip))]
539    pub payload: &'a [u8],
540}
541
542impl<'a> PesPacket<'a> {
543    /// Parse a PES packet from the bytes starting at its `packet_start_code_prefix`.
544    pub fn parse(b: &'a [u8]) -> Result<Self> {
545        if b.len() < MIN_LEN {
546            return Err(Error::BufferTooShort {
547                need: MIN_LEN,
548                have: b.len(),
549                what: "PES packet header",
550            });
551        }
552        if b[0..3] != PACKET_START_CODE_PREFIX {
553            return Err(Error::BadStartCode(
554                (u32::from(b[0]) << 16) | (u32::from(b[1]) << 8) | u32::from(b[2]),
555            ));
556        }
557        let stream_id = StreamId(b[3]);
558        let pes_packet_length = u16::from_be_bytes([b[4], b[5]]);
559        // Where the payload ends: bounded by PES_packet_length unless 0 (unbounded).
560        let payload_end = if pes_packet_length == 0 {
561            b.len()
562        } else {
563            (MIN_LEN + pes_packet_length as usize).min(b.len())
564        };
565
566        if !stream_id.has_optional_header() {
567            return Ok(PesPacket {
568                stream_id,
569                pes_packet_length,
570                header: None,
571                payload: &b[MIN_LEN..payload_end],
572            });
573        }
574
575        if b.len() < MIN_LEN + HEADER_FIXED {
576            return Err(Error::BufferTooShort {
577                need: MIN_LEN + HEADER_FIXED,
578                have: b.len(),
579                what: "PES optional header",
580            });
581        }
582        let f1 = b[6];
583        let f2 = b[7];
584        let hdl = usize::from(b[8]);
585        let hdr_start = MIN_LEN + HEADER_FIXED; // = 9
586        let hdr_end = hdr_start + hdl;
587        if b.len() < hdr_end {
588            return Err(Error::BufferTooShort {
589                need: hdr_end,
590                have: b.len(),
591                what: "PES_header_data_length",
592            });
593        }
594        let opt = &b[hdr_start..hdr_end];
595        let mut cursor = 0usize;
596
597        // PTS/DTS (ISO/IEC 13818-1 §2.4.3.7 Table 2-21).
598        let pts_dts_flags = (f2 >> 6) & 0x03;
599        let (pts, dts) = match pts_dts_flags {
600            0b10 => {
601                if opt.len() < cursor + 5 {
602                    return Err(Error::BufferTooShort {
603                        need: cursor + 5,
604                        have: opt.len(),
605                        what: "PTS",
606                    });
607                }
608                let pts = Pts(timestamp::read(&opt[cursor..], 0b0010, "PTS")?);
609                cursor += 5;
610                (Some(pts), None)
611            }
612            0b11 => {
613                if opt.len() < cursor + 10 {
614                    return Err(Error::BufferTooShort {
615                        need: cursor + 10,
616                        have: opt.len(),
617                        what: "PTS+DTS",
618                    });
619                }
620                let pts = Pts(timestamp::read(&opt[cursor..], 0b0011, "PTS")?);
621                cursor += 5;
622                let dts = Dts(timestamp::read(&opt[cursor..], 0b0001, "DTS")?);
623                cursor += 5;
624                (Some(pts), Some(dts))
625            }
626            _ => (None, None),
627        };
628
629        // ESCR (6 bytes, ISO/IEC 13818-1 §2.4.3.7).
630        let escr = if f2 & 0x20 != 0 {
631            if opt.len() < cursor + 6 {
632                return Err(Error::BufferTooShort {
633                    need: cursor + 6,
634                    have: opt.len(),
635                    what: "ESCR",
636                });
637            }
638            let arr: &[u8; 6] = opt[cursor..cursor + 6].try_into().unwrap();
639            let e = Escr::from_field_bytes(arr)?;
640            cursor += 6;
641            Some(e)
642        } else {
643            None
644        };
645
646        // ES_rate (3 bytes: 1 marker + 22-bit rate + 1 marker).
647        let es_rate = if f2 & 0x10 != 0 {
648            if opt.len() < cursor + 3 {
649                return Err(Error::BufferTooShort {
650                    need: cursor + 3,
651                    have: opt.len(),
652                    what: "ES_rate",
653                });
654            }
655            let rate = (((opt[cursor] & 0x7F) as u32) << 15)
656                | ((opt[cursor + 1] as u32) << 7)
657                | ((opt[cursor + 2] >> 1) as u32);
658            cursor += 3;
659            Some(rate)
660        } else {
661            None
662        };
663
664        // DSM trick mode (1 byte).
665        let dsm_trick_mode = if f2 & 0x08 != 0 {
666            if opt.len() < cursor + 1 {
667                return Err(Error::BufferTooShort {
668                    need: cursor + 1,
669                    have: opt.len(),
670                    what: "trick_mode",
671                });
672            }
673            let tm = TrickMode::from_byte(opt[cursor]);
674            cursor += 1;
675            Some(tm)
676        } else {
677            None
678        };
679
680        // additional_copy_info (1 byte: marker + 7-bit info).
681        let additional_copy_info = if f2 & 0x04 != 0 {
682            if opt.len() < cursor + 1 {
683                return Err(Error::BufferTooShort {
684                    need: cursor + 1,
685                    have: opt.len(),
686                    what: "additional_copy_info",
687                });
688            }
689            let v = opt[cursor] & 0x7F;
690            cursor += 1;
691            Some(v)
692        } else {
693            None
694        };
695
696        // PES_CRC (2 bytes).
697        let pes_crc = if f2 & 0x02 != 0 {
698            if opt.len() < cursor + 2 {
699                return Err(Error::BufferTooShort {
700                    need: cursor + 2,
701                    have: opt.len(),
702                    what: "PES_CRC",
703                });
704            }
705            let crc = u16::from_be_bytes([opt[cursor], opt[cursor + 1]]);
706            cursor += 2;
707            Some(crc)
708        } else {
709            None
710        };
711
712        // PES_extension.
713        let pes_extension = if f2 & 0x01 != 0 {
714            let ext = PesExtension::parse(&opt[cursor..])?;
715            cursor += ext.serialized_len();
716            Some(ext)
717        } else {
718            None
719        };
720
721        // Bytes remaining in the `PES_header_data_length` region after the typed
722        // optional fields are `0xFF` stuffing (ISO/IEC 13818-1:2007 §2.4.3.7).
723        // Record the count so serialization reproduces the header byte-for-byte.
724        let header_stuffing_len = hdl.saturating_sub(cursor);
725
726        let header = PesHeader {
727            scrambling_control: (f1 >> 4) & 0x03,
728            pes_priority: f1 & 0x08 != 0,
729            data_alignment_indicator: f1 & 0x04 != 0,
730            copyright: f1 & 0x02 != 0,
731            original_or_copy: f1 & 0x01 != 0,
732            pts,
733            dts,
734            escr,
735            es_rate,
736            dsm_trick_mode,
737            additional_copy_info,
738            pes_crc,
739            pes_extension,
740            header_stuffing_len,
741        };
742
743        Ok(PesPacket {
744            stream_id,
745            pes_packet_length,
746            header: Some(header),
747            payload: &b[hdr_end.min(payload_end)..payload_end],
748        })
749    }
750
751    /// Serialized length in bytes.
752    #[must_use]
753    pub fn serialized_len(&self) -> usize {
754        let hdr = self
755            .header
756            .as_ref()
757            .map_or(0, |h| HEADER_FIXED + h.optional_len());
758        MIN_LEN + hdr + self.payload.len()
759    }
760
761    /// Serialize back to bytes (byte-identical to a spec-compliant input).
762    pub fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
763        let len = self.serialized_len();
764        if buf.len() < len {
765            return Err(Error::BufferTooShort {
766                need: len,
767                have: buf.len(),
768                what: "PES serialize output",
769            });
770        }
771        buf[0..3].copy_from_slice(&PACKET_START_CODE_PREFIX);
772        buf[3] = self.stream_id.0;
773        buf[4..6].copy_from_slice(&self.pes_packet_length.to_be_bytes());
774        let payload_at = match &self.header {
775            None => MIN_LEN,
776            Some(h) => {
777                let opt_len = h.optional_len();
778                if opt_len > 255 {
779                    return Err(Error::OptionalFieldsTooLarge(opt_len));
780                }
781
782                // f1: marker '10' | scrambling(2) | priority(1) | align(1) | copyright(1) | orig(1)
783                let f1 = 0x80
784                    | ((h.scrambling_control & 0x03) << 4)
785                    | (u8::from(h.pes_priority) << 3)
786                    | (u8::from(h.data_alignment_indicator) << 2)
787                    | (u8::from(h.copyright) << 1)
788                    | u8::from(h.original_or_copy);
789
790                // f2: pts_dts_flags(2) | escr_flag(1) | es_rate_flag(1) |
791                //     trick_mode(1) | add_copy(1) | crc(1) | ext(1)
792                let pts_dts_flags = match (h.pts.is_some(), h.dts.is_some()) {
793                    (true, true) => 0b11u8,
794                    (true, false) => 0b10,
795                    _ => 0b00,
796                };
797                let f2 = (pts_dts_flags << 6)
798                    | (u8::from(h.escr.is_some()) << 5)
799                    | (u8::from(h.es_rate.is_some()) << 4)
800                    | (u8::from(h.dsm_trick_mode.is_some()) << 3)
801                    | (u8::from(h.additional_copy_info.is_some()) << 2)
802                    | (u8::from(h.pes_crc.is_some()) << 1)
803                    | u8::from(h.pes_extension.is_some());
804
805                buf[6] = f1;
806                buf[7] = f2;
807                buf[8] = opt_len as u8;
808
809                let mut cursor = MIN_LEN + HEADER_FIXED; // = 9
810
811                // PTS (and/or DTS).
812                if let Some(pts) = h.pts {
813                    let prefix = if h.dts.is_some() { 0b0011u8 } else { 0b0010u8 };
814                    buf[cursor..cursor + 5].copy_from_slice(&timestamp::write(pts.0, prefix));
815                    cursor += 5;
816                }
817                if let Some(dts) = h.dts {
818                    buf[cursor..cursor + 5].copy_from_slice(&timestamp::write(dts.0, 0b0001));
819                    cursor += 5;
820                }
821                // ESCR.
822                if let Some(escr) = h.escr {
823                    buf[cursor..cursor + 6].copy_from_slice(&escr.to_field_bytes());
824                    cursor += 6;
825                }
826                // ES_rate: marker(1) | rate(22) | marker(1) = 3 bytes.
827                if let Some(rate) = h.es_rate {
828                    buf[cursor] = 0x80 | ((rate >> 15) as u8 & 0x7F);
829                    buf[cursor + 1] = ((rate >> 7) & 0xFF) as u8;
830                    buf[cursor + 2] = (((rate & 0x7F) as u8) << 1) | 0x01;
831                    cursor += 3;
832                }
833                // DSM trick mode.
834                if let Some(tm) = h.dsm_trick_mode {
835                    buf[cursor] = tm.to_byte();
836                    cursor += 1;
837                }
838                // additional_copy_info: marker(1) | info(7).
839                if let Some(aci) = h.additional_copy_info {
840                    buf[cursor] = 0x80 | (aci & 0x7F);
841                    cursor += 1;
842                }
843                // PES_CRC (2 bytes, big-endian).
844                if let Some(crc) = h.pes_crc {
845                    buf[cursor..cursor + 2].copy_from_slice(&crc.to_be_bytes());
846                    cursor += 2;
847                }
848                // PES_extension.
849                if let Some(ref ext) = h.pes_extension {
850                    let written = ext.serialize_into(&mut buf[cursor..])?;
851                    cursor += written;
852                }
853
854                // Trailing `0xFF` stuffing inside the header_data_length region
855                // (ISO/IEC 13818-1:2007 §2.4.3.7), reproducing the encoder's pad.
856                for b in buf[cursor..cursor + h.header_stuffing_len].iter_mut() {
857                    *b = PES_HEADER_STUFFING_BYTE;
858                }
859                cursor += h.header_stuffing_len;
860
861                cursor
862            }
863        };
864        buf[payload_at..len].copy_from_slice(self.payload);
865        Ok(len)
866    }
867}
868
869#[cfg(test)]
870mod tests {
871    use super::*;
872    extern crate alloc;
873    use alloc::vec;
874
875    fn round_trip(b: &[u8]) {
876        let pkt = PesPacket::parse(b).unwrap();
877        let mut out = vec![0u8; pkt.serialized_len()];
878        pkt.serialize_into(&mut out).unwrap();
879        assert_eq!(&out[..], b, "round-trip mismatch");
880        let re = PesPacket::parse(&out).unwrap();
881        // Compare without the borrowed lifetime complexity — compare serialized form.
882        let mut re_out = vec![0u8; re.serialized_len()];
883        re.serialize_into(&mut re_out).unwrap();
884        assert_eq!(out, re_out, "re-parse mismatch");
885    }
886
887    #[test]
888    fn video_pts_only() {
889        // stream_id 0xE0, len=0x0A, flags 0x80/0x80, hdl=5, PTS=0, payload AA BB.
890        let b = [
891            0x00, 0x00, 0x01, 0xE0, 0x00, 0x0A, 0x80, 0x80, 0x05, 0x21, 0x00, 0x01, 0x00, 0x01,
892            0xAA, 0xBB,
893        ];
894        let pkt = PesPacket::parse(&b).unwrap();
895        assert_eq!(pkt.stream_id, StreamId(0xE0));
896        let h = pkt.header.as_ref().unwrap();
897        assert_eq!(h.pts, Some(Pts(0)));
898        assert!(h.dts.is_none());
899        assert_eq!(pkt.payload, &[0xAA, 0xBB]);
900        round_trip(&b);
901    }
902
903    #[test]
904    fn pts_and_dts() {
905        // PTS_DTS_flags=11, hdl=10. PTS prefix 0011, DTS prefix 0001.
906        let b = [
907            0x00, 0x00, 0x01, 0xE0, 0x00, 0x0F, 0x80, 0xC0, 0x0A, 0x31, 0x00, 0x03, 0x00, 0x01,
908            0x11, 0x00, 0x05, 0x00, 0x01, 0xCC,
909        ];
910        let pkt = PesPacket::parse(&b).unwrap();
911        let h = pkt.header.as_ref().unwrap();
912        assert!(h.pts.is_some());
913        assert!(h.dts.is_some());
914        round_trip(&b);
915    }
916
917    #[test]
918    fn pes_header_stuffing_round_trip() {
919        // PTS-only (flags 0x80), PES_header_data_length = 8: 5 PTS bytes + 3
920        // 0xFF stuffing bytes (ISO/IEC 13818-1:2007 §2.4.3.7).
921        let b = [
922            0x00, 0x00, 0x01, 0xE0, 0x00, 0x0C, 0x80, 0x80, 0x08, // hdl = 8
923            0x21, 0x00, 0x01, 0x00, 0x01, // PTS = 0
924            0xFF, 0xFF, 0xFF, // 3 stuffing bytes
925            0xAA, // payload
926        ];
927        let pkt = PesPacket::parse(&b).unwrap();
928        let h = pkt.header.as_ref().unwrap();
929        assert!(h.pts.is_some());
930        assert_eq!(
931            h.header_stuffing_len, 3,
932            "3 stuffing bytes after the 5-byte PTS"
933        );
934        // serialized_len must account for the stuffing.
935        assert_eq!(pkt.serialized_len(), b.len());
936        round_trip(&b); // byte-identical, incl. the 0xFF stuffing
937    }
938
939    #[test]
940    fn special_stream_no_header() {
941        // padding_stream 0xBE: bytes after length are payload directly.
942        let b = [0x00, 0x00, 0x01, 0xBE, 0x00, 0x03, 0xFF, 0xFF, 0xFF];
943        let pkt = PesPacket::parse(&b).unwrap();
944        assert!(pkt.header.is_none());
945        assert_eq!(pkt.payload, &[0xFF, 0xFF, 0xFF]);
946        round_trip(&b);
947    }
948
949    #[test]
950    fn unbounded_length_zero() {
951        // PES_packet_length=0 (video): payload runs to end of buffer.
952        let b = [
953            0x00, 0x00, 0x01, 0xE0, 0x00, 0x00, 0x80, 0x80, 0x05, 0x21, 0x00, 0x01, 0x00, 0x01,
954            0x01, 0x02, 0x03,
955        ];
956        let pkt = PesPacket::parse(&b).unwrap();
957        assert_eq!(pkt.pes_packet_length, 0);
958        assert_eq!(pkt.payload, &[0x01, 0x02, 0x03]);
959        round_trip(&b);
960    }
961
962    #[test]
963    fn rejects_bad_start_code() {
964        let err = PesPacket::parse(&[0x00, 0x00, 0x02, 0xE0, 0x00, 0x00]).unwrap_err();
965        assert!(matches!(err, Error::BadStartCode(0x000002)));
966    }
967
968    #[test]
969    fn rejects_short() {
970        let err = PesPacket::parse(&[0x00, 0x00, 0x01]).unwrap_err();
971        assert!(matches!(err, Error::BufferTooShort { .. }));
972    }
973
974    #[test]
975    fn serialize_rejects_oversized_optional_fields() {
976        // Construct a PesHeader whose optional_len() > 255 by adding enough flags.
977        // max realistic: 5+5+6+3+1+1+2 = 23, plus PesExtension with big private data.
978        // This is structurally impossible with typed fields to reach 256 naturally,
979        // but we test the guard exists by checking that OptionalFieldsTooLarge
980        // is the right error variant name still exists.
981        let _ = Error::OptionalFieldsTooLarge(256);
982    }
983
984    // ── Typed optional fields round-trips ──────────────────────────────────
985
986    fn build_pes(h: PesHeader<'_>, payload: &[u8]) -> alloc::vec::Vec<u8> {
987        let pkt = PesPacket {
988            stream_id: StreamId(0xE0),
989            pes_packet_length: 0, // unbounded
990            header: Some(h),
991            payload,
992        };
993        let mut out = vec![0u8; pkt.serialized_len()];
994        pkt.serialize_into(&mut out).unwrap();
995        out
996    }
997
998    fn empty_header<'a>() -> PesHeader<'a> {
999        PesHeader {
1000            scrambling_control: 0,
1001            pes_priority: false,
1002            data_alignment_indicator: false,
1003            copyright: false,
1004            original_or_copy: false,
1005            pts: None,
1006            dts: None,
1007            escr: None,
1008            es_rate: None,
1009            dsm_trick_mode: None,
1010            additional_copy_info: None,
1011            pes_crc: None,
1012            pes_extension: None,
1013            header_stuffing_len: 0,
1014        }
1015    }
1016
1017    /// Construct PES with ESCR set, serialize, parse, assert field preserved.
1018    #[test]
1019    fn pes_header_escr_round_trip() {
1020        let escr = Escr {
1021            base: 90_000,
1022            extension: 150,
1023        };
1024        let h = PesHeader {
1025            escr: Some(escr),
1026            ..empty_header()
1027        };
1028        let bytes = build_pes(h, &[0xAA]);
1029        let pkt = PesPacket::parse(&bytes).unwrap();
1030        assert_eq!(pkt.header.unwrap().escr, Some(escr));
1031    }
1032
1033    /// Escr::from_27mhz / as_27mhz round-trip.
1034    #[test]
1035    fn escr_27mhz_round_trip() {
1036        for ticks in [0u64, 1, 300, 27_000_000, 8_589_934_591] {
1037            let e = Escr::from_27mhz(ticks);
1038            assert_eq!(e.as_27mhz(), ticks, "ticks={ticks}");
1039        }
1040    }
1041
1042    /// Escr::to_field_bytes / from_field_bytes round-trip.
1043    #[test]
1044    fn escr_field_bytes_round_trip() {
1045        for (base, ext) in [
1046            (0u64, 0u16),
1047            (10_000, 0),
1048            (0x1_FFFF_FFFF, 0x1FF),
1049            (1234, 56),
1050        ] {
1051            let e = Escr {
1052                base,
1053                extension: ext,
1054            };
1055            let bytes = e.to_field_bytes();
1056            let decoded = Escr::from_field_bytes(&bytes).unwrap();
1057            assert_eq!(decoded, e, "base={base} ext={ext}");
1058        }
1059    }
1060
1061    /// ES_rate round-trip.
1062    #[test]
1063    fn pes_header_es_rate_round_trip() {
1064        let h = PesHeader {
1065            es_rate: Some(0x3FFFFF),
1066            ..empty_header()
1067        };
1068        let bytes = build_pes(h, &[]);
1069        let pkt = PesPacket::parse(&bytes).unwrap();
1070        assert_eq!(pkt.header.unwrap().es_rate, Some(0x3FFFFF));
1071    }
1072
1073    /// TrickMode round-trip (all variants).
1074    #[test]
1075    fn trick_mode_all_variants_round_trip() {
1076        let cases = [
1077            TrickMode::FastForward {
1078                field_id: 0x2,
1079                intra_slice_refresh: true,
1080                frequency_truncation: 0x3,
1081            },
1082            TrickMode::SlowMotion { rep_cntrl: 0x1F },
1083            TrickMode::FreezeFrame { field_id: 0x1 },
1084            TrickMode::FastReverse {
1085                field_id: 0x0,
1086                intra_slice_refresh: false,
1087                frequency_truncation: 0x1,
1088            },
1089            TrickMode::SlowReverse { rep_cntrl: 0 },
1090            TrickMode::Reserved {
1091                trick_mode_control: 0b101,
1092                data: 0x1A,
1093            },
1094        ];
1095        for tm in cases {
1096            let b = tm.to_byte();
1097            let decoded = TrickMode::from_byte(b);
1098            assert_eq!(decoded, tm, "tm={tm:?}");
1099        }
1100    }
1101
1102    /// TrickMode in PES header round-trip.
1103    #[test]
1104    fn pes_header_trick_mode_round_trip() {
1105        let tm = TrickMode::FastForward {
1106            field_id: 1,
1107            intra_slice_refresh: false,
1108            frequency_truncation: 2,
1109        };
1110        let h = PesHeader {
1111            dsm_trick_mode: Some(tm),
1112            ..empty_header()
1113        };
1114        let bytes = build_pes(h, &[]);
1115        let pkt = PesPacket::parse(&bytes).unwrap();
1116        assert_eq!(pkt.header.unwrap().dsm_trick_mode, Some(tm));
1117    }
1118
1119    /// additional_copy_info round-trip.
1120    #[test]
1121    fn pes_header_additional_copy_info_round_trip() {
1122        let h = PesHeader {
1123            additional_copy_info: Some(0x7F),
1124            ..empty_header()
1125        };
1126        let bytes = build_pes(h, &[]);
1127        let pkt = PesPacket::parse(&bytes).unwrap();
1128        assert_eq!(pkt.header.unwrap().additional_copy_info, Some(0x7F));
1129    }
1130
1131    /// PES_CRC round-trip.
1132    #[test]
1133    fn pes_header_pes_crc_round_trip() {
1134        let h = PesHeader {
1135            pes_crc: Some(0xDEAD),
1136            ..empty_header()
1137        };
1138        let bytes = build_pes(h, &[]);
1139        let pkt = PesPacket::parse(&bytes).unwrap();
1140        assert_eq!(pkt.header.unwrap().pes_crc, Some(0xDEAD));
1141    }
1142
1143    /// PesExtension with program_packet_sequence_counter.
1144    #[test]
1145    fn pes_extension_ppsc_round_trip() {
1146        let ppsc = ProgramPacketSequenceCounter {
1147            counter: 42,
1148            mpeg1_mpeg2_identifier: true,
1149            original_stuff_length: 7,
1150        };
1151        let ext = PesExtension {
1152            pes_private_data: None,
1153            pack_header: None,
1154            program_packet_sequence_counter: Some(ppsc),
1155            p_std_buffer: None,
1156            pes_extension_field: None,
1157        };
1158        let h = PesHeader {
1159            pes_extension: Some(ext),
1160            ..empty_header()
1161        };
1162        let bytes = build_pes(h, &[]);
1163        let pkt = PesPacket::parse(&bytes).unwrap();
1164        let decoded_ext = pkt.header.unwrap().pes_extension.unwrap();
1165        assert_eq!(decoded_ext.program_packet_sequence_counter, Some(ppsc));
1166    }
1167
1168    /// PesExtension with P-STD buffer.
1169    #[test]
1170    fn pes_extension_p_std_buffer_round_trip() {
1171        let pstd = PStdBuffer {
1172            scale: true,
1173            size: 0x1FFF,
1174        };
1175        let ext = PesExtension {
1176            pes_private_data: None,
1177            pack_header: None,
1178            program_packet_sequence_counter: None,
1179            p_std_buffer: Some(pstd),
1180            pes_extension_field: None,
1181        };
1182        let h = PesHeader {
1183            pes_extension: Some(ext),
1184            ..empty_header()
1185        };
1186        let bytes = build_pes(h, &[]);
1187        let pkt = PesPacket::parse(&bytes).unwrap();
1188        let decoded_ext = pkt.header.unwrap().pes_extension.unwrap();
1189        assert_eq!(decoded_ext.p_std_buffer, Some(pstd));
1190    }
1191
1192    /// PesExtension with private data (16 bytes).
1193    #[test]
1194    fn pes_extension_private_data_round_trip() {
1195        let pd: [u8; 16] = [
1196            0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,
1197            0x0F, 0x10,
1198        ];
1199        let ext = PesExtension {
1200            pes_private_data: Some(pd),
1201            pack_header: None,
1202            program_packet_sequence_counter: None,
1203            p_std_buffer: None,
1204            pes_extension_field: None,
1205        };
1206        let h = PesHeader {
1207            pes_extension: Some(ext),
1208            ..empty_header()
1209        };
1210        let bytes = build_pes(h, &[]);
1211        let pkt = PesPacket::parse(&bytes).unwrap();
1212        let decoded_ext = pkt.header.unwrap().pes_extension.unwrap();
1213        assert_eq!(decoded_ext.pes_private_data, Some(pd));
1214    }
1215
1216    /// All optional PES header fields set at once — serialize and parse round-trip.
1217    #[test]
1218    fn pes_header_all_fields_round_trip() {
1219        let ppsc = ProgramPacketSequenceCounter {
1220            counter: 1,
1221            mpeg1_mpeg2_identifier: false,
1222            original_stuff_length: 0,
1223        };
1224        let ext = PesExtension {
1225            pes_private_data: None,
1226            pack_header: None,
1227            program_packet_sequence_counter: Some(ppsc),
1228            p_std_buffer: Some(PStdBuffer {
1229                scale: false,
1230                size: 100,
1231            }),
1232            pes_extension_field: None,
1233        };
1234        let h = PesHeader {
1235            scrambling_control: 0,
1236            pes_priority: true,
1237            data_alignment_indicator: false,
1238            copyright: false,
1239            original_or_copy: true,
1240            pts: Some(Pts(90_000)),
1241            dts: Some(Dts(85_000)),
1242            escr: Some(Escr {
1243                base: 1000,
1244                extension: 0,
1245            }),
1246            es_rate: Some(50_000),
1247            dsm_trick_mode: Some(TrickMode::SlowMotion { rep_cntrl: 3 }),
1248            additional_copy_info: Some(5),
1249            pes_crc: Some(0xCAFE),
1250            pes_extension: Some(ext),
1251            header_stuffing_len: 0,
1252        };
1253        let bytes = build_pes(h, &[0xFF]);
1254        let pkt = PesPacket::parse(&bytes).unwrap();
1255        let dh = pkt.header.unwrap();
1256        assert_eq!(dh.pts, Some(Pts(90_000)));
1257        assert_eq!(dh.dts, Some(Dts(85_000)));
1258        assert!(dh.escr.is_some());
1259        assert_eq!(dh.es_rate, Some(50_000));
1260        assert_eq!(
1261            dh.dsm_trick_mode,
1262            Some(TrickMode::SlowMotion { rep_cntrl: 3 })
1263        );
1264        assert_eq!(dh.additional_copy_info, Some(5));
1265        assert_eq!(dh.pes_crc, Some(0xCAFE));
1266        assert!(dh.pes_extension.is_some());
1267    }
1268}