Skip to main content

st377_1/
lib.rs

1//! SMPTE ST 377-1:2019 "Material Exchange Format (MXF) — File Format
2//! Specification".
3//!
4//! This crate implements exactly the wire structure described in the
5//! curated spec transcription at `st377-1/docs/st377-1.md` (fetched
6//! directly from `https://pub.smpte.org/latest/st377-1/st377-1-2019.pdf`) —
7//! cite that file, not this doc comment, as the field-semantics oracle. It
8//! also documents in detail this crate's scope decision: MXF is a huge
9//! ecosystem spec (Operational Patterns, Essence Container mappings, DM/
10//! Application Metadata plug-ins, per-essence-kind Descriptors all live in
11//! sibling documents this crate does not attempt to anticipate), so this
12//! first pass fully types the format's own backbone and the four Root
13//! Metadata Sets every real MXF file has, and falls back to an identified-
14//! but-generic passthrough for everything else — see `docs/st377-1.md`'s
15//! "Scope decision for this crate" section for the full breakdown with
16//! spec citations.
17//!
18//! - [`KlvItem`] — the generic KLV (Key-Length-Value) triplet (§6.3) every
19//!   other structure in an MXF file rides on; [`walk_klv_items`] /
20//!   [`collect_klv_items`] walk a sequence of them.
21//! - [`PartitionPack`] — the Header/Body/Footer Partition Pack (§7.1-§7.4,
22//!   Tables 4-8): [`PartitionKind`] + [`PartitionStatus`] plus every Table 5
23//!   field.
24//! - [`PrimerPack`] — the per-Partition local-tag lookup table (§9.2).
25//! - [`LocalSet`] — the generic "local set" KLV-lite framing (§9.3) used by
26//!   every Header Metadata Set; [`StructuralSetKind`] identifies which Set
27//!   a given instance is (Table 17), even for the many Sets this crate does
28//!   not deeply type.
29//! - [`Preface`], [`Identification`], [`ContentStorage`],
30//!   [`EssenceContainerData`] — the four Root Metadata Sets (Annex A) every
31//!   real MXF file has exactly one/more of, decoded field-by-field.
32//! - [`MaterialPackage`], [`SourcePackage`] — the two concrete Package
33//!   kinds (Annex E / B.1), carrying Package UID, dates, and Track
34//!   references.
35//! - [`TimelineTrack`], [`EventTrack`], [`StaticTrack`] — the three Track
36//!   kinds (B.12/B.13/B.14), wrapping a Sequence reference plus timing
37//!   properties.
38//! - [`Sequence`] — the ordered component collection inside every Track
39//!   (B.9).
40//! - [`SourceClip`] — a component referencing a span of Source Package
41//!   essence (B.10).
42//! - [`TimecodeComponent`] — a component carrying a timecode reference
43//!   (B.17).
44//! - [`FillerComponent`] — a gap placeholder inside a Sequence (B.11).
45//! - [`op1a`] — OP1a Operational Pattern UL helpers (ST 378).
46//! - [`RandomIndexPack`] — the optional file-trailer Partition index (§12).
47//!
48//! **Out of scope entirely**: Essence Container payload bytes (the actual
49//! audio/video/data samples) — carried opaquely via [`KlvItem`], never
50//! decoded, the same boundary as `st337`'s `burst_payload`/`rdd29`'s
51//! `AudioDataDLC`. Index Table *contents*, Descriptors (F.*), DM Segments/
52//! Source Clips (B.32-B.33), and Application Metadata Sets (C.*) are
53//! identified via [`StructuralSetKind`] but not individually typed — see
54//! `docs/st377-1.md`.
55//!
56//! ## OP1a support is structural-metadata-only (issue #937)
57//!
58//! [`op1a`] plus [`MaterialPackage`]/[`SourcePackage`]/[`TimelineTrack`]/
59//! [`EventTrack`]/[`StaticTrack`]/[`Sequence`]/[`SourceClip`]/
60//! [`TimecodeComponent`]/[`FillerComponent`] parse and byte-losslessly
61//! round-trip every OP1a Header Metadata Set this crate types (see
62//! `docs/st378-op1a.md`), and are validated against a real `ffmpeg`-muxed
63//! OP1a file in `tests/fixture_real_op1a.rs`. Two things this does **not**
64//! add up to:
65//!
66//! - **No Essence Descriptor type.** `docs/st378-op1a.md`'s minimum OP1a
67//!   file requires the File Package to carry an `EssenceDescriptor`
68//!   (§6.5/§8), but this crate has no typed representation of any
69//!   Descriptor (F.2-F.6) — [`SourcePackage::descriptor`] is a bare
70//!   [`StrongRef`], a 16-byte Instance UID this crate can neither resolve
71//!   nor build a target for. Doing so properly would mean typing not just
72//!   ST 377-1's own generic Descriptor Sets but the per-essence-kind
73//!   registrations that actually appear on the wire (this crate's real
74//!   fixture carries an MPEG Video Descriptor and a Wave Audio Descriptor,
75//!   both defined by *sibling* essence-container-mapping specs, not
76//!   ST 377-1 itself) — exactly the ecosystem-anticipation problem the
77//!   Scope section above already declines to take on.
78//! - **No file assembler.** Nothing in this crate computes cross-Partition
79//!   byte offsets (`ThisPartition`/`PreviousPartition`/`FooterPartition`),
80//!   `HeaderByteCount`/`IndexByteCount`, or builds a [`RandomIndexPack`]
81//!   that actually points at the Partitions it describes.
82//!   [`PartitionPack`], [`PrimerPack`], the typed Header Metadata Sets, and
83//!   [`RandomIndexPack`] each parse and serialize correctly in isolation,
84//!   but nothing stitches them into one valid, playable OP1a file —
85//!   confirm this yourself in `tests/round_trip.rs`'s
86//!   `full_op1a_structure_builds_and_round_trips`: every offset/byte-count
87//!   field there is a hardcoded placeholder (`0`, or `9999` for the
88//!   `RandomIndexPack` byte offset), not a computed value.
89//!
90//! A full implementation would need, at minimum: a typed `EssenceDescriptor`
91//! family (File/Generic Picture/CDCI/RGBA/Generic Sound/Generic Data/
92//! Multiple, F.2-F.6) plus a way to plug in essence-kind-specific
93//! descriptors from sibling specs; and a writer that lays out Partitions in
94//! order, tracks running byte offsets as it serializes each one, backpatches
95//! `HeaderByteCount`/`IndexByteCount`/`ThisPartition`/`PreviousPartition`/
96//! `FooterPartition`, and emits a `RandomIndexPack` from the real offsets.
97//! That is a second, comparably-sized project; tracked separately rather
98//! than attempted here.
99//!
100//! Depends only on `broadcast-common`. `#![no_std]` + `alloc` when the
101//! `std` feature is disabled.
102//!
103//! # Examples
104//!
105//! Parse a Partition Pack and walk its Header Metadata:
106//!
107//! ```
108//! use broadcast_common::{Parse, Serialize};
109//! use st377_1::{PartitionKind, PartitionPack, PartitionStatus};
110//!
111//! let pack = PartitionPack {
112//!     kind: PartitionKind::Header,
113//!     status: PartitionStatus::ClosedComplete,
114//!     major_version: 1,
115//!     minor_version: 3,
116//!     kag_size: 512,
117//!     this_partition: 0,
118//!     previous_partition: 0,
119//!     footer_partition: 0,
120//!     header_byte_count: 0,
121//!     index_byte_count: 0,
122//!     index_sid: 0,
123//!     body_offset: 0,
124//!     body_sid: 0,
125//!     operational_pattern: [0u8; 16],
126//!     essence_containers: Vec::new(),
127//! };
128//! let bytes = pack.to_bytes();
129//! assert_eq!(PartitionPack::parse(&bytes).unwrap(), pack);
130//! ```
131#![cfg_attr(not(feature = "std"), no_std)]
132#![cfg_attr(docsrs, feature(doc_cfg))]
133#![warn(missing_docs)]
134// Runnable examples, embedded so they render on docs.rs and stay in sync
135// with the actual `examples/*.rs` files (shown, not compiled).
136#![doc = "\n## Runnable examples\n"]
137#![doc = "Run with `cargo run -p st377-1 --example <name>`.\n"]
138#![doc = "\n### `parse_partition`\n\n```rust,ignore"]
139#![doc = include_str!("../examples/parse_partition.rs")]
140#![doc = "```\n\n### `build_preface`\n\n```rust,ignore"]
141#![doc = include_str!("../examples/build_preface.rs")]
142#![doc = "```"]
143
144extern crate alloc;
145
146mod ber;
147mod content_storage;
148mod error;
149mod essence_container_data;
150mod filler_component;
151mod identification;
152mod klv;
153mod local_set;
154pub mod op1a;
155mod package;
156mod partition;
157mod preface;
158mod primer;
159mod random_index_pack;
160mod sequence;
161mod sets;
162mod source_clip;
163mod timecode_component;
164mod track;
165mod types;
166
167pub use content_storage::ContentStorage;
168pub use error::{Error, Result};
169pub use essence_container_data::EssenceContainerData;
170pub use filler_component::FillerComponent;
171pub use identification::Identification;
172pub use klv::{
173    FILL_ITEM_KEY_PREFIX, FILL_ITEM_KEY_SUFFIX, KlvItem, collect_klv_items, is_fill_item_key,
174    walk_klv_items,
175};
176pub use local_set::{ItemLengthMode, LocalSet, LocalSetItem, StructuralSetKind, is_local_set_key};
177pub use package::{MaterialPackage, SourcePackage};
178pub use partition::{PartitionKind, PartitionPack, PartitionStatus};
179pub use preface::Preface;
180pub use preface::VERSION_1_3;
181pub use primer::PrimerPack;
182pub use random_index_pack::{PartitionLocation, RandomIndexPack};
183pub use sequence::Sequence;
184pub use sets::InterchangeObjectFields;
185pub use source_clip::SourceClip;
186pub use timecode_component::TimecodeComponent;
187pub use track::{EventTrack, StaticTrack, TimelineTrack};
188pub use types::{
189    Auid, MxfTimestamp, PRODUCT_VERSION_LEN, PackageId, ProductVersion, RATIONAL_LEN, Rational,
190    ReleaseType, StrongRef, TIMESTAMP_LEN, UlBytes, decode_utf16_be, encode_utf16_be,
191    parse_uid_batch, serialize_uid_batch,
192};
193
194// Re-exported so downstream code can build owned local-set item lists for
195// dark/private extensions without depending on this crate's internal
196// module layout.
197pub use sets::LocalSetOwnedItem;