Skip to main content

Crate st377_1

Crate st377_1 

Source
Expand description

SMPTE ST 377-1:2019 “Material Exchange Format (MXF) — File Format Specification”.

This crate implements exactly the wire structure described in the curated spec transcription at st377-1/docs/st377-1.md (fetched directly from https://pub.smpte.org/latest/st377-1/st377-1-2019.pdf) — cite that file, not this doc comment, as the field-semantics oracle. It also documents in detail this crate’s scope decision: MXF is a huge ecosystem spec (Operational Patterns, Essence Container mappings, DM/ Application Metadata plug-ins, per-essence-kind Descriptors all live in sibling documents this crate does not attempt to anticipate), so this first pass fully types the format’s own backbone and the four Root Metadata Sets every real MXF file has, and falls back to an identified- but-generic passthrough for everything else — see docs/st377-1.md’s “Scope decision for this crate” section for the full breakdown with spec citations.

Out of scope entirely: Essence Container payload bytes (the actual audio/video/data samples) — carried opaquely via KlvItem, never decoded, the same boundary as st337’s burst_payload/rdd29’s AudioDataDLC. Index Table contents, Descriptors (F.), DM Segments/ Source Clips (B.32-B.33), and Application Metadata Sets (C.) are identified via StructuralSetKind but not individually typed — see docs/st377-1.md.

§OP1a support is structural-metadata-only (issue #937)

op1a plus MaterialPackage/SourcePackage/TimelineTrack/ EventTrack/StaticTrack/Sequence/SourceClip/ TimecodeComponent/FillerComponent parse and byte-losslessly round-trip every OP1a Header Metadata Set this crate types (see docs/st378-op1a.md), and are validated against a real ffmpeg-muxed OP1a file in tests/fixture_real_op1a.rs. Two things this does not add up to:

  • No Essence Descriptor type. docs/st378-op1a.md’s minimum OP1a file requires the File Package to carry an EssenceDescriptor (§6.5/§8), but this crate has no typed representation of any Descriptor (F.2-F.6) — SourcePackage::descriptor is a bare StrongRef, a 16-byte Instance UID this crate can neither resolve nor build a target for. Doing so properly would mean typing not just ST 377-1’s own generic Descriptor Sets but the per-essence-kind registrations that actually appear on the wire (this crate’s real fixture carries an MPEG Video Descriptor and a Wave Audio Descriptor, both defined by sibling essence-container-mapping specs, not ST 377-1 itself) — exactly the ecosystem-anticipation problem the Scope section above already declines to take on.
  • No file assembler. Nothing in this crate computes cross-Partition byte offsets (ThisPartition/PreviousPartition/FooterPartition), HeaderByteCount/IndexByteCount, or builds a RandomIndexPack that actually points at the Partitions it describes. PartitionPack, PrimerPack, the typed Header Metadata Sets, and RandomIndexPack each parse and serialize correctly in isolation, but nothing stitches them into one valid, playable OP1a file — confirm this yourself in tests/round_trip.rs’s full_op1a_structure_builds_and_round_trips: every offset/byte-count field there is a hardcoded placeholder (0, or 9999 for the RandomIndexPack byte offset), not a computed value.

A full implementation would need, at minimum: a typed EssenceDescriptor family (File/Generic Picture/CDCI/RGBA/Generic Sound/Generic Data/ Multiple, F.2-F.6) plus a way to plug in essence-kind-specific descriptors from sibling specs; and a writer that lays out Partitions in order, tracks running byte offsets as it serializes each one, backpatches HeaderByteCount/IndexByteCount/ThisPartition/PreviousPartition/ FooterPartition, and emits a RandomIndexPack from the real offsets. That is a second, comparably-sized project; tracked separately rather than attempted here.

Depends only on broadcast-common. #![no_std] + alloc when the std feature is disabled.

§Examples

Parse a Partition Pack and walk its Header Metadata:

use broadcast_common::{Parse, Serialize};
use st377_1::{PartitionKind, PartitionPack, PartitionStatus};

let pack = PartitionPack {
    kind: PartitionKind::Header,
    status: PartitionStatus::ClosedComplete,
    major_version: 1,
    minor_version: 3,
    kag_size: 512,
    this_partition: 0,
    previous_partition: 0,
    footer_partition: 0,
    header_byte_count: 0,
    index_byte_count: 0,
    index_sid: 0,
    body_offset: 0,
    body_sid: 0,
    operational_pattern: [0u8; 16],
    essence_containers: Vec::new(),
};
let bytes = pack.to_bytes();
assert_eq!(PartitionPack::parse(&bytes).unwrap(), pack);

§Runnable examples

Run with cargo run -p st377-1 --example <name>.

§parse_partition

//! Build a Header Partition Pack, serialize it, then parse it back and walk
//! its Operational Pattern / Essence Container inventory — SMPTE
//! ST 377-1:2019 §7.1-§7.2.
//!
//! Run with `cargo run -p st377-1 --example parse_partition`.

use broadcast_common::{Parse, Serialize};
use st377_1::{PartitionKind, PartitionPack, PartitionStatus};

fn main() {
    let pack = PartitionPack {
        kind: PartitionKind::Header,
        status: PartitionStatus::ClosedComplete,
        major_version: 1,
        minor_version: 3,
        kag_size: 512,
        this_partition: 0,
        previous_partition: 0,
        footer_partition: 65536,
        header_byte_count: 2048,
        index_byte_count: 0,
        index_sid: 0,
        body_offset: 0,
        body_sid: 1,
        // A placeholder Operational Pattern UL (see §8) — a real encoder
        // would use one of the registered OP1a/OP-Atom/etc. values.
        operational_pattern: [
            0x06, 0x0E, 0x2B, 0x34, 0x04, 0x01, 0x01, 0x01, 0x0D, 0x01, 0x02, 0x01, 0x01, 0x01,
            0x01, 0x00,
        ],
        essence_containers: vec![[0x11; 16]],
    };

    let bytes = pack.to_bytes();
    println!("serialized {} bytes:", bytes.len());
    println!(
        "{}",
        bytes
            .iter()
            .map(|b| format!("{b:02x}"))
            .collect::<Vec<_>>()
            .join(" ")
    );

    let parsed = PartitionPack::parse(&bytes).expect("parse Partition Pack");
    assert_eq!(parsed, pack);

    println!("kind:   {}", parsed.kind);
    println!("status: {}", parsed.status);
    println!(
        "KAG size: {} bytes, footer at byte offset {}",
        parsed.kag_size, parsed.footer_partition
    );
    println!(
        "{} Essence Container UL(s) referenced",
        parsed.essence_containers.len()
    );
}

§build_preface

//! Build a `Preface` Header Metadata Set from typed fields, serialize it,
//! then parse it back — SMPTE ST 377-1:2019 Annex A.2.
//!
//! Run with `cargo run -p st377-1 --example build_preface`.

use broadcast_common::{Parse, Serialize};
use st377_1::{InterchangeObjectFields, MxfTimestamp, Preface, VERSION_1_3};

fn main() {
    let preface = Preface {
        interchange: InterchangeObjectFields {
            instance_uid: [0x01; 16],
            generation_uid: None,
            object_class: None,
        },
        last_modified_date: MxfTimestamp {
            year: 2026,
            month: 7,
            day: 12,
            hour: 10,
            minute: 0,
            second: 0,
            msec_div4: 0,
        },
        version: VERSION_1_3,
        object_model_version: Some(1),
        primary_package: None,
        identifications: vec![[0x02; 16]],
        content_storage: [0x03; 16],
        operational_pattern: [0x04; 16],
        essence_containers: vec![[0x05; 16]],
        dm_schemes: Vec::new(),
        dark: Vec::new(),
    };

    let bytes = preface.to_bytes();
    println!("serialized Preface: {} bytes", bytes.len());

    let parsed = Preface::parse(&bytes).expect("parse Preface");
    assert_eq!(parsed, preface);

    println!(
        "last modified: {:04}-{:02}-{:02} {:02}:{:02}:{:02}",
        parsed.last_modified_date.year,
        parsed.last_modified_date.month,
        parsed.last_modified_date.day,
        parsed.last_modified_date.hour,
        parsed.last_modified_date.minute,
        parsed.last_modified_date.second,
    );
    println!("version: 0x{:04X}", parsed.version);
    println!(
        "{} Identification(s), {} EssenceContainer UL(s)",
        parsed.identifications.len(),
        parsed.essence_containers.len()
    );
}

Modules§

op1a
OP1a Operational Pattern — SMPTE ST 378:2004 / ST 377-1:2019 §A.2 (docs/st377-1.md): identification helpers for the “single item, single package” operational pattern that nearly all real MXF files use.

Structs§

Auid
An AUID (§4.2.1): a 16-byte field holding either a UL or a UUID, distinguished by the top bit of byte 0 (0 = UL, stored value-order; 1 = UUID, stored with its top/bottom 8 bytes swapped from natural UUID order).
ContentStorage
The Content Storage Set — SMPTE ST 377-1:2019 Annex A.4: strong references to every Package (Packages) and every Essence Container Data Set (EssenceContainerData) in the file, referenced from the Preface’s ContentStorage property.
EssenceContainerData
The Essence Container Data Set — SMPTE ST 377-1:2019 Annex A.5: links a LinkedPackageUID to the BodySID/IndexSID pair identifying its Essence Container / Index Table Segments among the file’s Partitions.
EventTrack
The Event Track (DM) Set — SMPTE ST 377-1:2019 Annex B §B.13 (byte 14/15 = 0x01/0x39): a DM event-driven track with an edit rate and optional origin.
FillerComponent
The Filler Set — SMPTE ST 377-1:2019 Annex B §B.11 (byte 14/15 = 0x01/0x09): a gap placeholder inside a Sequence, consuming a specified duration of the track’s timeline without referencing any source essence.
Identification
The Identification Set — SMPTE ST 377-1:2019 Annex A.3: one instance per application/device that has created or modified the file (§7.5.2), each referenced from the Preface’s Identifications array.
InterchangeObjectFields
The two Interchange Object (Annex A.1) properties with a static local tag, common to every Root Metadata Set.
KlvItem
A single KLV triplet: a 16-byte Key, a BER-encoded Length, and the Value bytes it describes (docs/st377-1.md §6.3).
LocalSet
A Header Metadata Set encoded with MXF’s “local set” framing (§9.3): a 16-byte Set Key identifying which Set this is (see StructuralSetKind), a BER Length, and a sequence of LocalSetItems.
LocalSetItem
One {local_tag, value} item inside a LocalSet (Figure 8).
LocalSetOwnedItem
An owned {tag, value} pair, used to build a fresh LocalSet for serialization (the borrowed LocalSetItem can’t hold bytes owned by the very struct being serialized).
MaterialPackage
The Material Package Set — SMPTE ST 377-1:2019 Annex E §E.1 (byte 14/15 = 0x01/0x36): the top-level composition that describes the final timeline of the file. Carries only the Generic Package properties (B.1) — no additional fields.
MxfTimestamp
A Gregorian timestamp (§4.3): year: Int16, month/day/hour/minute/second/ msec_div4: UInt8, big-endian, 8 bytes total. All-zero means “unknown”.
PackageId
A “Package ID” (§4.2): a 32-byte Basic UMID (SMPTE ST 330) or 32 zero bytes (“terminate a reference chain”). This crate treats the UMID’s own internal bit layout as out of scope (ST 330 is a separate normative reference) — see docs/st377-1.md’s Scope section — and exposes it only as an opaque, fixed-size value.
PartitionLocation
One {BodySID, ByteOffset} pair (Table 30) locating a single Partition.
PartitionPack
The Partition Pack — SMPTE ST 377-1:2019 §7.1, Table 4 (Key) + Table 5 (Value). Covers the Header/Body/Footer variants (§7.2-7.4); which one a given instance is lives in kind.
Preface
The Preface Set — SMPTE ST 377-1:2019 Annex A.2: the file’s overall metadata (modification time, Operational Pattern, Essence Container / Descriptive Metadata scheme inventory) and strong references to the Identification history and the ContentStorage Set.
PrimerPack
The Primer Pack — SMPTE ST 377-1:2019 §9.2, Tables 13-15: a Batch of {local_tag: u16, uid: AUID} entries, scoped to the single Partition that contains it (§9.2 — never accumulated across Partitions).
ProductVersion
A tool/product version number (§4.3): 5 big-endian UInt16 fields.
RandomIndexPack
The Random Index Pack — SMPTE ST 377-1:2019 §12: one PartitionLocation per Partition in the file (including Header and Footer), ascending byte_offset order, plus a trailing overall-length field (§12.2 Note 2) that lets a decoder seek from EOF directly to this Pack’s own Key without a forward scan.
Rational
A Rational number (§4.3) — two big-endian Int32 values, 8 bytes total. Used for Edit Rate, Sample Rate, and similar time-base properties.
Sequence
The Sequence Set — SMPTE ST 377-1:2019 Annex B §B.9 (byte 14/15 = 0x01/0x0F): the single child of every Track, holding an ordered list of Structural Components (Source Clips, Timecode Components, Fillers, etc.) that compose the track’s content.
SourceClip
The Source Clip Set — SMPTE ST 377-1:2019 Annex B §B.10 (byte 14/15 = 0x01/0x11): references a contiguous span of essence from a Source Package Track, identified by its UMID, Track ID, and a start position within that track.
SourcePackage
The Source Package Set — SMPTE ST 377-1:2019 Annex E §E.2 (byte 14/15 = 0x01/0x37): references the file’s actual essence via a Descriptor strong reference, plus the Generic Package properties (B.1).
StaticTrack
The Static Track (DM) Set — SMPTE ST 377-1:2019 Annex B §B.14 (byte 14/15 = 0x01/0x3A): a DM track with no temporal extent. Carries only the Generic Track properties (B.6) — no additional fields.
TimecodeComponent
The Timecode Component Set — SMPTE ST 377-1:2019 Annex B §B.17 (byte 14/15 = 0x01/0x14): supplies a timecode reference inside a Track Sequence (typically in a Timecode Track).
TimelineTrack
The Timeline Track Set — SMPTE ST 377-1:2019 Annex B §B.12 (byte 14/15 = 0x01/0x3B): a timed track with a fixed edit rate and origin position.

Enums§

Error
An MXF parse / serialize error.
ItemLengthMode
Byte 6 of a Local Set Key (§9.3 Note 1): which length encoding its items use.
PartitionKind
Which kind of Partition a Partition Pack Key identifies (Table 4 byte 14; Tables 6-8).
PartitionStatus
The Open/Closed × Complete/Incomplete status of a Partition (§6.2.3, Table 4 byte 15).
ReleaseType
ProductVersion’s release field enumeration (§4.3).
StructuralSetKind
Set Kind (Table 17 — this crate’s byte 14/15 identification list for every Header Metadata Set the spec defines, whether or not this crate types its properties). See docs/st377-1.md’s Table 17 reproduction.

Constants§

FILL_ITEM_KEY_PREFIX
The KLV Fill item key (§6.3.3), matching byte 8 (the version number) as a wildcard per the spec’s own note (“MXF decoders shall ignore the version number byte … when determining if a KLV key is the Fill item key” — some early encoders wrote 0x01 there instead of the RP 210 value 0x02).
FILL_ITEM_KEY_SUFFIX
Byte 8 (version) is a wildcard; bytes 9-16 of the Fill item key.
PRODUCT_VERSION_LEN
Wire size of ProductVersion — always 10 bytes.
RATIONAL_LEN
Wire size of Rational — always 8 bytes.
TIMESTAMP_LEN
Wire size of MxfTimestamp — always 8 bytes.
VERSION_1_3
This revision’s fixed Version property value (0x0103 = v1.3, A.2).

Functions§

collect_klv_items
Collect every KLV item in bytes into a Vec (small helper for tests and examples; large real files should prefer walk_klv_items to avoid buffering every item at once).
decode_utf16_be
Decode a big-endian UTF-16 string (§4.3 “String”) into an owned String.
encode_utf16_be
Encode a string as big-endian UTF-16 (§4.3 “String”).
is_fill_item_key
True if key is the KLV Fill item key (§6.3.3), ignoring byte 8 (the version number) per the spec’s own decoder rule.
is_local_set_key
True if key matches the common Local Set Key structure (Table 16): fixed prefix bytes, byte 6 a valid ItemLengthMode, and the fixed 0x0D/organization/application/structure-kind bytes. Byte 15 (reserved) is not checked (some dark extensions may not zero it).
parse_uid_batch
Parse a Batch/Array of 16-byte elements (§4.3): 8-byte header (count: u32, item_len: u32, both big-endian) followed by count 16-byte elements. Used for every UL/StrongRef Batch or Array in the Root Metadata Sets (EssenceContainers, DMSchemes, Identifications, Packages, EssenceContainerData).
serialize_uid_batch
Serialize a Batch/Array of 16-byte elements (§4.3) — see parse_uid_batch. Always writes item_len = 16: it names the size of each element type in the batch, not the instance count, so this crate keeps it constant even when count == 0. (Some real encoders write item_len = 0 for an empty batch instead — e.g. ffmpeg’s OP1a Preface DMSchemes in tests/fixtures/op1a_mpeg2_pcm.mxf — which parse_uid_batch tolerates on input; this crate’s own output stays canonical.)
walk_klv_items
Walk every KLV item in bytes (e.g. one Partition’s full body), calling f with each item and its byte offset from the start of bytes. Stops at the first parse error (returned to the caller) or when the buffer is exhausted.

Type Aliases§

Result
Result alias for st377-1 parsing/serialization.
StrongRef
A Strong Reference (§5.4.4): a 16-byte UUID referencing another Set in the same file. Identical on the wire to UlBytes; the alias documents the semantic role in property declarations.
UlBytes
A 16-byte SMPTE Universal Label or UUID.