Skip to main content

mpeg_ps/
program_stream.rs

1//! Program Stream walker — ISO/IEC 13818-1 §2.5.3.1–2.5.3.3 (Tables 2-37, 2-38).
2//!
3//! Iterates through a Program Stream, yielding each [`Pack`], which itself
4//! carries a [`PackHeader`], an optional
5//! [`SystemHeader`], and parsed PES packets (via `dvb-pes`).
6//!
7//! The stream terminates with the `MPEG_program_end_code` `0x000001B9`.
8
9use alloc::vec::Vec;
10
11use dvb_common::{Parse, Serialize};
12
13use crate::pack_header::{PackHeader, PACK_START_CODE};
14use crate::system_header::{SystemHeader, SYSTEM_HEADER_START_CODE};
15use crate::Result;
16
17/// `MPEG_program_end_code` — `0x000001B9`.
18const PROGRAM_END_CODE: u32 = 0x0000_01B9;
19
20/// A single pack within a Program Stream: a `pack_header()`, optionally a
21/// `system_header()`, followed by zero or more PES packets.
22#[derive(Debug, Clone)]
23pub struct Pack<'a> {
24    /// The pack header (SCR, program_mux_rate, stuffing).
25    pub pack_header: PackHeader<'a>,
26    /// The optional system header (only in the first pack of a compliant stream).
27    pub system_header: Option<SystemHeader>,
28    /// Parsed PES packets within this pack.
29    pub pes_packets: Vec<dvb_pes::PesPacket<'a>>,
30}
31
32/// Scans forward for the next pack_start_code or program_end_code boundary.
33fn find_next_boundary(b: &[u8], from: usize) -> Option<usize> {
34    let mut i = from;
35    while i + 4 <= b.len() {
36        let word = u32::from_be_bytes([b[i], b[i + 1], b[i + 2], b[i + 3]]);
37        if word == PACK_START_CODE || word == PROGRAM_END_CODE {
38            return Some(i);
39        }
40        i += 1;
41    }
42    None
43}
44
45/// Parses a single pack from the start of `b`.
46///
47/// Returns `Ok((Some(pack), consumed_bytes))` on success,
48/// or `Ok((None, 4))` when `MPEG_program_end_code` `0x000001B9` is reached.
49pub fn parse_pack(b: &[u8]) -> Result<(Option<Pack<'_>>, usize)> {
50    use crate::error::Error;
51
52    if b.len() < 4 {
53        return Err(Error::BufferTooShort {
54            need: 4,
55            have: b.len(),
56            what: "pack start_code or end_code",
57        });
58    }
59
60    let start = u32::from_be_bytes([b[0], b[1], b[2], b[3]]);
61    if start == PROGRAM_END_CODE {
62        return Ok((None, 4));
63    }
64
65    // Parse pack header
66    let pack_header = PackHeader::parse(b)?;
67    let hdr_len = pack_header.header_len();
68    let rest = &b[hdr_len..];
69
70    // Find the next pack boundary or end_code to limit PES parsing
71    let boundary = find_next_boundary(rest, 0);
72
73    // Check for optional system header (before any PES)
74    let (system_header, pes_start, _sh_len) = if rest.len() >= 4 {
75        let maybe_sh = u32::from_be_bytes([rest[0], rest[1], rest[2], rest[3]]);
76        if maybe_sh == SYSTEM_HEADER_START_CODE {
77            let sh = SystemHeader::parse(rest)?;
78            let slen = sh.serialized_len();
79            (Some(sh), slen, slen)
80        } else {
81            (None, 0, 0)
82        }
83    } else {
84        (None, 0, 0)
85    };
86
87    let pes_data = &rest[pes_start..];
88    let pes_end = boundary.map_or(pes_data.len(), |b| b - pes_start);
89
90    // Parse PES packets up to the boundary
91    let (pes_packets, _pes_consumed) = parse_pes_loop(&pes_data[..pes_end])?;
92
93    let consumed = hdr_len + pes_start + _pes_consumed;
94    Ok((
95        Some(Pack {
96            pack_header,
97            system_header,
98            pes_packets,
99        }),
100        consumed,
101    ))
102}
103
104fn parse_pes_loop<'a>(data: &'a [u8]) -> Result<(Vec<dvb_pes::PesPacket<'a>>, usize)> {
105    use crate::error::Error;
106
107    let mut packets = Vec::new();
108    let mut pos = 0;
109
110    while pos + 6 <= data.len()
111        && data[pos] == 0x00
112        && data[pos + 1] == 0x00
113        && data[pos + 2] == 0x01
114    {
115        match dvb_pes::PesPacket::parse(&data[pos..]) {
116            Ok(pkt) => {
117                let pkt_len = pkt.serialized_len();
118                packets.push(pkt);
119                pos += pkt_len;
120            }
121            Err(e) => return Err(Error::Pes(e)),
122        }
123    }
124
125    Ok((packets, pos))
126}
127
128/// Iterate over all packs in a Program Stream buffer.
129///
130/// Returns all packs and the remaining trailing bytes (if any).
131pub fn parse_all_packs(b: &[u8]) -> Result<(Vec<Pack<'_>>, &[u8])> {
132    let mut packs = Vec::new();
133    let mut remaining = b;
134    while remaining.len() >= 4 {
135        let (pack_opt, consumed) = parse_pack(remaining)?;
136        match pack_opt {
137            Some(pack) => {
138                remaining = &remaining[consumed..];
139                packs.push(pack);
140            }
141            None => {
142                // End code consumed 4 bytes; finish
143                remaining = &remaining[4..];
144                break;
145            }
146        }
147    }
148    Ok((packs, remaining))
149}