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;