mpeg-ps 0.7.0

MPEG-2 Program Stream parser — pack header, system header, program stream map (ISO/IEC 13818-1 §2.5).
Documentation

mpeg-ps

Crates.io docs.rs

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());
}
# Ok::<(), mpeg_ps::Error>(())

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 are Error::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 checked fit_bits and returns FieldOverflow rather than masking.
  • Resynchronising scan. scan_packs is the tolerant counterpart of the strict parse_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.