Skip to main content

Crate mpeg_ps

Crate mpeg_ps 

Source
Expand description

§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

StandardEditionScopeStatusNotes
ITU-T H.222.0 / ISO/IEC 13818-1, SystemsH.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-pesFull for the listed syntaxThe 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 onlyNot implementedReported as Error::Mpeg1NotSupported.

§Feature flags

FeatureDefaultWhat it enables
stdyesLinks std; off gives #![no_std] + alloc
serdenoserde::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

AreaPublic API
Pack headerPackHeader<'a> (Parse/Serialize; header_len), Scr (42-bit @ 27 MHz; ticks, seconds), PACK_START_CODE
System headerSystemHeader (rate_bound, audio_bound, video_bound, lock/CSPS/fixed flags, reserved_bits; Parse/Serialize), StdBufferBound, SYSTEM_HEADER_START_CODE
Program stream mapProgramStreamMap<'a> (version, current_next_indicator, program_stream_info, elementary_stream_map: Vec<EsMapEntry>, crc), EsMapEntry<'a>, MAP_STREAM_ID
Stream walkingprogram_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 / errorsPROGRAM_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)].

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§

PackHeader
A parsed pack header.
Scr
System Clock Reference (42-bit total: 33-bit base + 9-bit extension, in 27 MHz units).
StdBufferBound
A per-stream P-STD buffer bound entry.
SystemHeader
A parsed system header.

Enums§

Error
A Program Stream parse error.

Constants§

PACKET_START_CODE_PREFIX
The 3-byte packet_start_code_prefix that opens PES and PSM packets (0x000001) — the one definition lives in mpeg-pes (#1141). The 3-byte packet_start_code_prefix that 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.