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::error::{Error, Result};
4use crate::stream_id::StreamId;
5use crate::timestamp::{self, Dts, Pts};
6use crate::PACKET_START_CODE_PREFIX;
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
11/// The optional PES header present for non-special `stream_id`s
12/// (§2.4.3.6). The variable optional fields (PTS/DTS, ESCR, ES_rate, trick mode,
13/// …) are retained verbatim in [`optional_fields`](Self::optional_fields);
14/// `pts`/`dts` are decoded from their front for convenience.
15#[derive(Debug, Clone, PartialEq, Eq)]
16#[cfg_attr(feature = "serde", derive(serde::Serialize))]
17pub struct PesHeader<'a> {
18    /// PES_scrambling_control (2 bits).
19    pub scrambling_control: u8,
20    /// PES_priority.
21    pub pes_priority: bool,
22    /// data_alignment_indicator.
23    pub data_alignment_indicator: bool,
24    /// copyright.
25    pub copyright: bool,
26    /// original_or_copy.
27    pub original_or_copy: bool,
28    /// ESCR_flag.
29    pub escr_flag: bool,
30    /// ES_rate_flag.
31    pub es_rate_flag: bool,
32    /// DSM_trick_mode_flag.
33    pub dsm_trick_mode_flag: bool,
34    /// additional_copy_info_flag.
35    pub additional_copy_info_flag: bool,
36    /// PES_CRC_flag.
37    pub pes_crc_flag: bool,
38    /// PES_extension_flag.
39    pub pes_extension_flag: bool,
40    /// Presentation time stamp, if `PTS_DTS_flags` indicated one.
41    pub pts: Option<Pts>,
42    /// Decoding time stamp, if `PTS_DTS_flags` was `11`.
43    pub dts: Option<Dts>,
44    /// The raw optional-header data block (`PES_header_data_length` bytes): the
45    /// PTS/DTS plus any ESCR/ES_rate/trick-mode/CRC/extension sub-fields.
46    #[cfg_attr(feature = "serde", serde(skip))]
47    pub optional_fields: &'a [u8],
48}
49
50/// A parsed PES packet.
51#[derive(Debug, Clone, PartialEq, Eq)]
52#[cfg_attr(feature = "serde", derive(serde::Serialize))]
53pub struct PesPacket<'a> {
54    /// stream_id (Table 2-22).
55    pub stream_id: StreamId,
56    /// PES_packet_length as carried; `0` means unbounded (video).
57    pub pes_packet_length: u16,
58    /// Optional PES header (absent for the special `stream_id`s).
59    pub header: Option<PesHeader<'a>>,
60    /// The elementary-stream bytes (`PES_packet_data_byte`s).
61    #[cfg_attr(feature = "serde", serde(skip))]
62    pub payload: &'a [u8],
63}
64
65impl<'a> PesPacket<'a> {
66    /// Parse a PES packet from the bytes starting at its `packet_start_code_prefix`.
67    pub fn parse(b: &'a [u8]) -> Result<Self> {
68        if b.len() < MIN_LEN {
69            return Err(Error::BufferTooShort {
70                need: MIN_LEN,
71                have: b.len(),
72                what: "PES packet header",
73            });
74        }
75        if b[0..3] != PACKET_START_CODE_PREFIX {
76            return Err(Error::BadStartCode(
77                (u32::from(b[0]) << 16) | (u32::from(b[1]) << 8) | u32::from(b[2]),
78            ));
79        }
80        let stream_id = StreamId(b[3]);
81        let pes_packet_length = u16::from_be_bytes([b[4], b[5]]);
82        // Where the payload ends: bounded by PES_packet_length unless 0 (unbounded).
83        let payload_end = if pes_packet_length == 0 {
84            b.len()
85        } else {
86            (MIN_LEN + pes_packet_length as usize).min(b.len())
87        };
88
89        if !stream_id.has_optional_header() {
90            return Ok(PesPacket {
91                stream_id,
92                pes_packet_length,
93                header: None,
94                payload: &b[MIN_LEN..payload_end],
95            });
96        }
97
98        if b.len() < MIN_LEN + HEADER_FIXED {
99            return Err(Error::BufferTooShort {
100                need: MIN_LEN + HEADER_FIXED,
101                have: b.len(),
102                what: "PES optional header",
103            });
104        }
105        let f1 = b[6];
106        let f2 = b[7];
107        let hdl = usize::from(b[8]);
108        let hdr_start = MIN_LEN + HEADER_FIXED;
109        let hdr_end = hdr_start + hdl;
110        if b.len() < hdr_end {
111            return Err(Error::BufferTooShort {
112                need: hdr_end,
113                have: b.len(),
114                what: "PES_header_data_length",
115            });
116        }
117        let optional_fields = &b[hdr_start..hdr_end];
118
119        let pts_dts_flags = (f2 >> 6) & 0x03;
120        let (pts, dts) = match pts_dts_flags {
121            0b10 => (
122                Some(Pts(timestamp::read(optional_fields, 0b0010, "PTS")?)),
123                None,
124            ),
125            0b11 => {
126                let pts = Pts(timestamp::read(optional_fields, 0b0011, "PTS")?);
127                let dts_bytes = optional_fields.get(5..).unwrap_or(&[]);
128                let dts = Dts(timestamp::read(dts_bytes, 0b0001, "DTS")?);
129                (Some(pts), Some(dts))
130            }
131            _ => (None, None),
132        };
133
134        let header = PesHeader {
135            scrambling_control: (f1 >> 4) & 0x03,
136            pes_priority: f1 & 0x08 != 0,
137            data_alignment_indicator: f1 & 0x04 != 0,
138            copyright: f1 & 0x02 != 0,
139            original_or_copy: f1 & 0x01 != 0,
140            escr_flag: f2 & 0x20 != 0,
141            es_rate_flag: f2 & 0x10 != 0,
142            dsm_trick_mode_flag: f2 & 0x08 != 0,
143            additional_copy_info_flag: f2 & 0x04 != 0,
144            pes_crc_flag: f2 & 0x02 != 0,
145            pes_extension_flag: f2 & 0x01 != 0,
146            pts,
147            dts,
148            optional_fields,
149        };
150
151        Ok(PesPacket {
152            stream_id,
153            pes_packet_length,
154            header: Some(header),
155            payload: &b[hdr_end.min(payload_end)..payload_end],
156        })
157    }
158
159    /// Serialized length in bytes.
160    #[must_use]
161    pub fn serialized_len(&self) -> usize {
162        let hdr = self
163            .header
164            .as_ref()
165            .map_or(0, |h| HEADER_FIXED + h.optional_fields.len());
166        MIN_LEN + hdr + self.payload.len()
167    }
168
169    /// Serialize back to bytes (byte-identical to a spec-compliant input).
170    pub fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
171        let len = self.serialized_len();
172        if buf.len() < len {
173            return Err(Error::BufferTooShort {
174                need: len,
175                have: buf.len(),
176                what: "PES serialize output",
177            });
178        }
179        buf[0..3].copy_from_slice(&PACKET_START_CODE_PREFIX);
180        buf[3] = self.stream_id.0;
181        buf[4..6].copy_from_slice(&self.pes_packet_length.to_be_bytes());
182        let payload_at = match &self.header {
183            None => MIN_LEN,
184            Some(h) => {
185                if h.optional_fields.len() > 255 {
186                    return Err(Error::OptionalFieldsTooLarge(h.optional_fields.len()));
187                }
188                let f1 = 0x80
189                    | ((h.scrambling_control & 0x03) << 4)
190                    | (u8::from(h.pes_priority) << 3)
191                    | (u8::from(h.data_alignment_indicator) << 2)
192                    | (u8::from(h.copyright) << 1)
193                    | u8::from(h.original_or_copy);
194                let pts_dts_flags = match (h.pts.is_some(), h.dts.is_some()) {
195                    (true, true) => 0b11,
196                    (true, false) => 0b10,
197                    _ => 0b00,
198                };
199                let f2 = (pts_dts_flags << 6)
200                    | (u8::from(h.escr_flag) << 5)
201                    | (u8::from(h.es_rate_flag) << 4)
202                    | (u8::from(h.dsm_trick_mode_flag) << 3)
203                    | (u8::from(h.additional_copy_info_flag) << 2)
204                    | (u8::from(h.pes_crc_flag) << 1)
205                    | u8::from(h.pes_extension_flag);
206                buf[6] = f1;
207                buf[7] = f2;
208                buf[8] = h.optional_fields.len() as u8;
209                let hdr_end = MIN_LEN + HEADER_FIXED + h.optional_fields.len();
210                buf[MIN_LEN + HEADER_FIXED..hdr_end].copy_from_slice(h.optional_fields);
211                hdr_end
212            }
213        };
214        buf[payload_at..len].copy_from_slice(self.payload);
215        Ok(len)
216    }
217}
218
219#[cfg(test)]
220mod tests {
221    use super::*;
222    extern crate alloc;
223    use alloc::vec;
224
225    fn round_trip(b: &[u8]) {
226        let pkt = PesPacket::parse(b).unwrap();
227        let mut out = vec![0u8; pkt.serialized_len()];
228        pkt.serialize_into(&mut out).unwrap();
229        assert_eq!(&out[..], b, "round-trip mismatch");
230        assert_eq!(PesPacket::parse(&out).unwrap(), pkt);
231    }
232
233    #[test]
234    fn video_pts_only() {
235        // stream_id 0xE0, len=0x0A, flags 0x80/0x80, hdl=5, PTS=0, payload AA BB.
236        let b = [
237            0x00, 0x00, 0x01, 0xE0, 0x00, 0x0A, 0x80, 0x80, 0x05, 0x21, 0x00, 0x01, 0x00, 0x01,
238            0xAA, 0xBB,
239        ];
240        let pkt = PesPacket::parse(&b).unwrap();
241        assert_eq!(pkt.stream_id, StreamId(0xE0));
242        let h = pkt.header.as_ref().unwrap();
243        assert_eq!(h.pts, Some(Pts(0)));
244        assert!(h.dts.is_none());
245        assert_eq!(pkt.payload, &[0xAA, 0xBB]);
246        round_trip(&b);
247    }
248
249    #[test]
250    fn pts_and_dts() {
251        // PTS_DTS_flags=11, hdl=10. PTS prefix 0011, DTS prefix 0001.
252        let b = [
253            0x00, 0x00, 0x01, 0xE0, 0x00, 0x0F, 0x80, 0xC0, 0x0A, 0x31, 0x00, 0x03, 0x00, 0x01,
254            0x11, 0x00, 0x05, 0x00, 0x01, 0xCC,
255        ];
256        let pkt = PesPacket::parse(&b).unwrap();
257        let h = pkt.header.as_ref().unwrap();
258        assert!(h.pts.is_some());
259        assert!(h.dts.is_some());
260        round_trip(&b);
261    }
262
263    #[test]
264    fn special_stream_no_header() {
265        // padding_stream 0xBE: bytes after length are payload directly.
266        let b = [0x00, 0x00, 0x01, 0xBE, 0x00, 0x03, 0xFF, 0xFF, 0xFF];
267        let pkt = PesPacket::parse(&b).unwrap();
268        assert!(pkt.header.is_none());
269        assert_eq!(pkt.payload, &[0xFF, 0xFF, 0xFF]);
270        round_trip(&b);
271    }
272
273    #[test]
274    fn unbounded_length_zero() {
275        // PES_packet_length=0 (video): payload runs to end of buffer.
276        let b = [
277            0x00, 0x00, 0x01, 0xE0, 0x00, 0x00, 0x80, 0x80, 0x05, 0x21, 0x00, 0x01, 0x00, 0x01,
278            0x01, 0x02, 0x03,
279        ];
280        let pkt = PesPacket::parse(&b).unwrap();
281        assert_eq!(pkt.pes_packet_length, 0);
282        assert_eq!(pkt.payload, &[0x01, 0x02, 0x03]);
283        round_trip(&b);
284    }
285
286    #[test]
287    fn rejects_bad_start_code() {
288        let err = PesPacket::parse(&[0x00, 0x00, 0x02, 0xE0, 0x00, 0x00]).unwrap_err();
289        assert!(matches!(err, Error::BadStartCode(0x000002)));
290    }
291
292    #[test]
293    fn rejects_short() {
294        let err = PesPacket::parse(&[0x00, 0x00, 0x01]).unwrap_err();
295        assert!(matches!(err, Error::BufferTooShort { .. }));
296    }
297
298    #[test]
299    fn serialize_rejects_oversized_optional_fields() {
300        // A manually-built header with > 255 optional bytes cannot fit
301        // PES_header_data_length (u8) — must error, not silently truncate.
302        let big = vec![0u8; 256];
303        let pkt = PesPacket {
304            stream_id: StreamId(0xE0),
305            pes_packet_length: 0,
306            header: Some(PesHeader {
307                scrambling_control: 0,
308                pes_priority: false,
309                data_alignment_indicator: false,
310                copyright: false,
311                original_or_copy: false,
312                escr_flag: false,
313                es_rate_flag: false,
314                dsm_trick_mode_flag: false,
315                additional_copy_info_flag: false,
316                pes_crc_flag: false,
317                pes_extension_flag: false,
318                pts: None,
319                dts: None,
320                optional_fields: &big,
321            }),
322            payload: &[],
323        };
324        let mut out = vec![0u8; pkt.serialized_len()];
325        assert!(matches!(
326            pkt.serialize_into(&mut out),
327            Err(Error::OptionalFieldsTooLarge(256))
328        ));
329    }
330}