Expand description
§mpeg-ps
MPEG-2 Program Stream framing: pack header, system header, program stream map, with PES parsed through mpeg-pes.
§What it does / what it does not
Program Streams are the .mpg / .vob / DVD container: a sequence of packs,
each opened by a pack header (42-bit SCR + program_mux_rate), optionally
carrying a system header and a program stream map (PSM), followed by PES
packets. This crate parses and serializes that framing and hands each PES
packet to mpeg-pes. It is for demuxers, analyzers and container-detection
code.
It does not decode the elementary streams, does not mux a Program Stream from
scratch (it round-trips parsed structures), and does not support MPEG-1 System
Streams: an ISO/IEC 11172-1 pack ('0010' prefix) is recognised and rejected
with Error::Mpeg1NotSupported, never mis-parsed as corrupt data.
§Standards supported
| Standard | Edition | Scope | Status | Notes |
|---|---|---|---|---|
| ITU-T H.222.0 / ISO/IEC 13818-1, Systems | H.222.0 v9 (08/2023), verified against the vendored copy | §2.5 Program Stream: pack header (§2.5.3.3), system header (Table 2-40, incl. per-stream P-STD buffer bounds and stream_id_extension), Program Stream Map (§2.5.4, Table 2-41, CRC-32), MPEG_program_end_code; PES via mpeg-pes | Full for the listed syntax | The Program Stream Directory is not implemented; PSM program_stream_info descriptors are carried as opaque bytes. |
| ISO/IEC 11172-1 (MPEG-1 Systems) | not vendored (unverified) | Detection of the MPEG-1 pack layout only | Not implemented | Reported as Error::Mpeg1NotSupported. |
§Feature flags
| Feature | Default | What it enables |
|---|---|---|
std | yes | Links std; off gives #![no_std] + alloc |
serde | no | serde::Serialize derives on the public types |
[dependencies]
mpeg-ps = "0.7"§Quick start
use broadcast_common::Parse;
use mpeg_ps::{PackHeader, program_stream};
// A minimal pack header: start_code 0x000001BA, SCR = 0, mux_rate = 3, no stuffing.
let bytes = [
0x00, 0x00, 0x01, 0xBA,
0x44, 0x00, 0x04, 0x00, 0x04, 0x01,
0x00, 0x00, 0x0F, 0x00,
];
let h = PackHeader::parse(&bytes)?;
assert_eq!(h.program_mux_rate, 3);
assert_eq!(h.scr.ticks(), 0);
// Walk a real capture (committed fixture; path is relative to the crate root).
let data = std::fs::read("../fixtures/ps/ffmpeg-mpeg2-ps.mpg").unwrap();
let (packs, _trailing) = program_stream::parse_all_packs(&data)?;
assert!(!packs.is_empty());
for pack in &packs {
let _ = (pack.pack_header.scr.ticks(), pack.pes_packets.len());
}For damaged streams, program_stream::scan_packs returns a PackScan: every
pack that parsed (packs), every span it had to skip together with the error
(skipped, resynchronising on the next pack_start_code), and the remaining
tail, instead of abandoning the stream at the first bad pack.
Examples: cargo run -p mpeg-ps --example parse_pack_header and
--example walk_ps.
§What is implemented
| Area | Public API |
|---|---|
| Pack header | PackHeader<'a> (Parse/Serialize; header_len), Scr (42-bit @ 27 MHz; ticks, seconds), PACK_START_CODE |
| System header | SystemHeader (rate_bound, audio_bound, video_bound, lock/CSPS/fixed flags, reserved_bits; Parse/Serialize), StdBufferBound, SYSTEM_HEADER_START_CODE |
| Program stream map | ProgramStreamMap<'a> (version, current_next_indicator, program_stream_info, elementary_stream_map: Vec<EsMapEntry>, crc), EsMapEntry<'a>, MAP_STREAM_ID |
| Stream walking | program_stream::{Pack, parse_pack, parse_all_packs, scan_packs, PackScan, SkippedRegion}; Pack holds the pack header, optional system header, optional PSM and pes_packets: Vec<mpeg_pes::PesPacket> |
| Constants / errors | PROGRAM_END_CODE, PACKET_START_CODE_PREFIX (re-exported from mpeg-pes), Error, Result |
§Design notes
- Zero-copy, symmetric. Parsed types borrow the input and implement
Parse+Serialize; serialize -> parse is the identity for the stream loop (invalid combinations areError::InvalidField, not silently re-encoded differently). - Checked widths. Every bit-field (SCR base/extension,
rate_bound,audio_bound,video_bound, P-STD buffer size,stream_id_extension) is written with a checkedfit_bitsand returnsFieldOverflowrather than masking. - Resynchronising scan.
scan_packsis the tolerant counterpart of the strictparse_all_packs. no_std+alloc,#![warn(missing_docs)].
§Related crates
Depends on broadcast-common and mpeg-pes. Used by container-probe (to
recognise Program Streams) and transmux (PS demux). The TS counterpart is
mpeg-ts.
MSRV 1.95.0. Licensed under MIT OR Apache-2.0.
§Runnable examples
Run with cargo run -p mpeg-ps --example <name>.
§parse_pack_header
use broadcast_common::Parse;
/// Parse one pack header from inline bytes and print the SCR + mux_rate.
///
/// ```sh
/// cargo run -p mpeg-ps --example parse_pack_header
/// ```
use mpeg_ps::PackHeader;
fn main() {
// A minimal pack header (from the fixture pattern):
// start_code 0x000001BA, SCR=0, mux_rate=0x10CD8E, reserved=0x1F, stuffing=0
let bytes = [
0x00, 0x00, 0x01, 0xBA, 0x44, 0x00, 0x04, 0x00, 0x04, 0x01, 0x43, 0x36, 0x3B, 0xF8,
];
let header = PackHeader::parse(&bytes).unwrap();
println!("pack_start_code: 0x000001BA");
println!("SCR base: {}", header.scr.base);
println!("SCR extension: {}", header.scr.extension);
println!("SCR ticks: {}", header.scr.ticks());
println!("SCR seconds: {:.6}", header.scr.seconds());
println!(
"program_mux_rate: {} ({} B/s)",
header.program_mux_rate,
header.program_mux_rate * 50
);
println!("pack_stuffing_length: {}", header.stuffing_length);
println!("reserved: {}", header.reserved);
}§walk_ps
/// Walk the real `.mpg` fixture at runtime, report pack count + SCR timeline.
///
/// ```sh
/// cargo run -p mpeg-ps --example walk_ps
/// ```
use std::fs;
use mpeg_ps::program_stream;
fn main() {
// Fixtures live in the workspace-shared `fixtures/` tree, not under the
// crate. A committed fixture that cannot be read is a bug, not a reason
// to skip — so a missing/unreadable fixture is a hard failure.
let path = test_fixtures::path("ps/ffmpeg-mpeg2-ps.mpg");
let data = fs::read(&path).unwrap_or_else(|e| {
panic!(
"committed fixture {} could not be read: {e}",
path.display()
)
});
let (packs, trailing) = program_stream::parse_all_packs(&data).unwrap();
println!("File: {}", path.display());
println!("Size: {} bytes", data.len());
println!("Trailing bytes: {}", trailing.len());
println!("Packs: {}", packs.len());
println!();
println!(
"{:<6} {:<20} {:<15} {:<10}",
"Pack", "SCR (ticks)", "SCR (s)", "mux_rate (B/s)"
);
println!("{:-<6} {:-<20} {:-<15} {:-<10}", "", "", "", "");
let mut pes_total = 0usize;
for (i, pack) in packs.iter().enumerate() {
let scr_ticks = pack.pack_header.scr.ticks();
let scr_s = pack.pack_header.scr.seconds();
let mux_rate = pack.pack_header.program_mux_rate * 50;
println!("{i:<6} {scr_ticks:<20} {scr_s:<15.6} {mux_rate:<10}",);
if let Some(ref sh) = pack.system_header {
println!(
" system_header: rate_bound={}, audio_bound={}, video_bound={}, streams={}",
sh.rate_bound,
sh.audio_bound,
sh.video_bound,
sh.std_buffer_bounds.len(),
);
}
for pes in &pack.pes_packets {
pes_total += 1;
if let Some(ref hdr) = pes.header
&& let Some(pts) = hdr.pts
{
let pts_s = pts.seconds();
println!(
" PES stream_id={:#04x} PTS={:.6}s payload={}B",
pes.stream_id.0,
pts_s,
pes.payload.len(),
);
}
}
}
println!();
println!("Total PES packets: {pes_total}");
}Re-exports§
pub use program_stream_map::EsMapEntry;pub use program_stream_map::ProgramStreamMap;
Modules§
- program_
stream - Program Stream walker — ISO/IEC 13818-1 §2.5.3.1–2.5.3.3 (Tables 2-37, 2-38).
- program_
stream_ map - Program Stream Map — ISO/IEC 13818-1 §2.5.4, Table 2-41.
Structs§
- Pack
Header - A parsed pack header.
- Scr
- System Clock Reference (42-bit total: 33-bit base + 9-bit extension, in 27 MHz units).
- StdBuffer
Bound - A per-stream P-STD buffer bound entry.
- System
Header - A parsed system header.
Enums§
- Error
- A Program Stream parse error.
Constants§
- PACKET_
START_ CODE_ PREFIX - The 3-byte
packet_start_code_prefixthat opens PES and PSM packets (0x000001) — the one definition lives inmpeg-pes(#1141). The 3-bytepacket_start_code_prefixthat opens every PES packet (0x000001). - PROGRAM_
END_ CODE MPEG_program_end_code—0x000001B9, terminates the program stream.
Type Aliases§
- Result
- Result alias for Program Stream parsing.