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.
KlvItem— the generic KLV (Key-Length-Value) triplet (§6.3) every other structure in an MXF file rides on;walk_klv_items/collect_klv_itemswalk a sequence of them.PartitionPack— the Header/Body/Footer Partition Pack (§7.1-§7.4, Tables 4-8):PartitionKind+PartitionStatusplus every Table 5 field.PrimerPack— the per-Partition local-tag lookup table (§9.2).LocalSet— the generic “local set” KLV-lite framing (§9.3) used by every Header Metadata Set;StructuralSetKindidentifies which Set a given instance is (Table 17), even for the many Sets this crate does not deeply type.Preface,Identification,ContentStorage,EssenceContainerData— the four Root Metadata Sets (Annex A) every real MXF file has exactly one/more of, decoded field-by-field.MaterialPackage,SourcePackage— the two concrete Package kinds (Annex E / B.1), carrying Package UID, dates, and Track references.TimelineTrack,EventTrack,StaticTrack— the three Track kinds (B.12/B.13/B.14), wrapping a Sequence reference plus timing properties.Sequence— the ordered component collection inside every Track (B.9).SourceClip— a component referencing a span of Source Package essence (B.10).TimecodeComponent— a component carrying a timecode reference (B.17).FillerComponent— a gap placeholder inside a Sequence (B.11).op1a— OP1a Operational Pattern UL helpers (ST 378).RandomIndexPack— the optional file-trailer Partition index (§12).
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 anEssenceDescriptor(§6.5/§8), but this crate has no typed representation of any Descriptor (F.2-F.6) —SourcePackage::descriptoris a bareStrongRef, 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 aRandomIndexPackthat actually points at the Partitions it describes.PartitionPack,PrimerPack, the typed Header Metadata Sets, andRandomIndexPackeach parse and serialize correctly in isolation, but nothing stitches them into one valid, playable OP1a file — confirm this yourself intests/round_trip.rs’sfull_op1a_structure_builds_and_round_trips: every offset/byte-count field there is a hardcoded placeholder (0, or9999for theRandomIndexPackbyte 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). - Content
Storage - 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’sContentStorageproperty. - Essence
Container Data - The Essence Container Data Set — SMPTE ST 377-1:2019 Annex A.5: links a
LinkedPackageUIDto theBodySID/IndexSIDpair identifying its Essence Container / Index Table Segments among the file’s Partitions. - Event
Track - 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. - Filler
Component - 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
Identificationsarray. - Interchange
Object Fields - 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). - Local
Set - 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 ofLocalSetItems. - Local
SetItem - One
{local_tag, value}item inside aLocalSet(Figure 8). - Local
SetOwned Item - An owned
{tag, value}pair, used to build a freshLocalSetfor serialization (the borrowedLocalSetItemcan’t hold bytes owned by the very struct being serialized). - Material
Package - 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”. - Package
Id - 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. - Partition
Location - One
{BodySID, ByteOffset}pair (Table 30) locating a single Partition. - Partition
Pack - 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
Identificationhistory and theContentStorageSet. - Primer
Pack - 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). - Product
Version - A tool/product version number (§4.3): 5 big-endian
UInt16fields. - Random
Index Pack - The Random Index Pack — SMPTE ST 377-1:2019 §12: one
PartitionLocationper Partition in the file (including Header and Footer), ascendingbyte_offsetorder, 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
Int32values, 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. - Source
Clip - 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. - Source
Package - 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 aDescriptorstrong reference, plus the Generic Package properties (B.1). - Static
Track - 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. - Timecode
Component - 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). - Timeline
Track - 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.
- Item
Length Mode - Byte 6 of a Local Set Key (§9.3 Note 1): which length encoding its items use.
- Partition
Kind - Which kind of Partition a Partition Pack Key identifies (Table 4 byte 14; Tables 6-8).
- Partition
Status - The Open/Closed × Complete/Incomplete status of a Partition (§6.2.3, Table 4 byte 15).
- Release
Type - ProductVersion’s
releasefield enumeration (§4.3). - Structural
SetKind - 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
0x01there instead of the RP 210 value0x02). - 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
Versionproperty value (0x0103= v1.3, A.2).
Functions§
- collect_
klv_ items - Collect every KLV item in
bytesinto aVec(small helper for tests and examples; large real files should preferwalk_klv_itemsto 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
keyis 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
keymatches the common Local Set Key structure (Table 16): fixed prefix bytes, byte 6 a validItemLengthMode, and the fixed0x0D/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 bycount16-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 writesitem_len = 16: it names the size of each element type in the batch, not the instance count, so this crate keeps it constant even whencount == 0. (Some real encoders writeitem_len = 0for an empty batch instead — e.g.ffmpeg’s OP1a PrefaceDMSchemesintests/fixtures/op1a_mpeg2_pcm.mxf— whichparse_uid_batchtolerates 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), callingfwith each item and its byte offset from the start ofbytes. Stops at the first parse error (returned to the caller) or when the buffer is exhausted.