oximedia_container/lib.rs
1//! `OxiMedia` Container Layer
2//!
3//! Container format handling with resilient parsing for:
4//! - Matroska (.mkv) / `WebM` (.webm)
5//! - Ogg (.ogg, .opus, .oga)
6//! - FLAC (.flac)
7//! - WAV (.wav)
8//! - MP4 (.mp4) - AV1/VP9 only
9//! - MPEG-TS (.ts, .m2ts) - AV1/VP9/VP8/Opus/FLAC only
10//! - YUV4MPEG2 (.y4m) - Raw uncompressed video
11//!
12//! # Overview
13//!
14//! This crate provides demuxers and muxers for media container formats.
15//! A demuxer reads a container file and extracts compressed packets,
16//! while a muxer combines compressed packets into a container file.
17//!
18//! # Key Types
19//!
20//! - [`ContainerFormat`] - Enumeration of supported container formats
21//! - [`probe_format`] - Detect container format from magic bytes
22//! - [`Packet`] - Compressed media packet with timestamps
23//! - [`StreamInfo`] - Information about a stream (codec, dimensions, etc.)
24//! - [`Demuxer`] - Trait for container demuxers
25//! - [`Muxer`] - Trait for container muxers
26//!
27//! # Demuxing Example
28//!
29//! ```ignore
30//! use oximedia_container::{probe_format, demux::MatroskaDemuxer, Demuxer};
31//! use oximedia_io::FileSource;
32//!
33//! // Detect format from file header
34//! let mut source = FileSource::open("video.mkv").await?;
35//! let mut buf = [0u8; 12];
36//! source.read(&mut buf).await?;
37//! let format = probe_format(&buf)?;
38//! println!("Format: {:?}", format.format);
39//!
40//! // Demux the file
41//! source.seek(std::io::SeekFrom::Start(0)).await?;
42//! let mut demuxer = MatroskaDemuxer::new(source);
43//! demuxer.probe().await?;
44//!
45//! for stream in demuxer.streams() {
46//! println!("Stream {}: {:?}", stream.index, stream.codec);
47//! }
48//!
49//! while let Ok(packet) = demuxer.read_packet().await {
50//! println!("Packet: stream={}, size={}, keyframe={}",
51//! packet.stream_index, packet.size(), packet.is_keyframe());
52//! }
53//! ```
54//!
55//! # Muxing Example
56//!
57//! ```ignore
58//! use oximedia_container::mux::{MatroskaMuxer, Muxer, MuxerConfig};
59//!
60//! let config = MuxerConfig::new()
61//! .with_title("My Video");
62//!
63//! let mut muxer = MatroskaMuxer::new(sink, config);
64//! muxer.add_stream(video_info)?;
65//! muxer.add_stream(audio_info)?;
66//!
67//! muxer.write_header().await?;
68//!
69//! for packet in packets {
70//! muxer.write_packet(&packet).await?;
71//! }
72//!
73//! muxer.write_trailer().await?;
74//! ```
75//!
76//! # Metadata Editing Example
77//!
78//! ```ignore
79//! use oximedia_container::metadata::MetadataEditor;
80//!
81//! let mut editor = MetadataEditor::open("audio.flac").await?;
82//!
83//! // Read tags
84//! if let Some(title) = editor.get_text("TITLE") {
85//! println!("Title: {}", title);
86//! }
87//!
88//! // Modify tags
89//! editor.set("TITLE", "New Title");
90//! editor.set("ARTIST", "New Artist");
91//!
92//! // Save changes
93//! editor.save().await?;
94//! ```
95//!
96//! # Wave 4 API Additions
97//!
98//! ## MP4 Fragment Mode — `Mp4FragmentMode`
99//!
100//! [`mux::mp4::Mp4FragmentMode`] controls how the MP4 muxer arranges sample data:
101//!
102//! | Variant | Description |
103//! |---------|-------------|
104//! | `Progressive` | Classic MP4: single `moov` + `mdat`, optimal for download |
105//! | `Fragmented { fragment_duration_ms }` | ISOBMFF fragments; each fragment is a self-contained `moof`+`mdat` pair |
106//!
107//! `Mp4Mode` is a backward-compatible type alias for `Mp4FragmentMode`.
108//!
109//! ```ignore
110//! use oximedia_container::mux::mp4::{Mp4Muxer, Mp4Config, Mp4FragmentMode};
111//!
112//! // Progressive (default)
113//! let config = Mp4Config::new().with_mode(Mp4FragmentMode::Progressive);
114//!
115//! // Fragmented — 4-second fragments for DASH/HLS delivery
116//! let config = Mp4Config::new()
117//! .with_mode(Mp4FragmentMode::Fragmented { fragment_duration_ms: 4000 });
118//! ```
119//!
120//! ## Sample-Accurate Seek Cursor — [`DecodeSkipCursor`]
121//!
122//! [`DecodeSkipCursor`] is returned by the `seek_sample_accurate()` methods on the
123//! Matroska, MP4, and AVI demuxers. It locates the nearest keyframe at or before a
124//! target PTS and records how many decoded samples must be discarded to reach the
125//! precise presentation position.
126//!
127//! | Field | Type | Description |
128//! |-------|------|-------------|
129//! | `byte_offset` | `u64` | File offset where decoding should start |
130//! | `sample_index` | `usize` | 0-based index of the keyframe sample |
131//! | `skip_samples` | `u32` | Samples to decode-and-discard after seeking |
132//! | `target_pts` | `i64` | Requested PTS in track timescale units |
133//!
134//! ```ignore
135//! use oximedia_container::demux::MatroskaDemuxer;
136//! use oximedia_io::FileSource;
137//!
138//! let mut demuxer = MatroskaDemuxer::new(FileSource::open("video.mkv").await?);
139//! demuxer.probe().await?;
140//!
141//! // Seek to exactly 30 seconds (in track timescale units)
142//! let cursor = demuxer.seek_sample_accurate(2_700_000).await?;
143//! println!("Start decode at byte {}, skip {} samples",
144//! cursor.byte_offset, cursor.skip_samples);
145//! ```
146//!
147//! ## CMAF Chunked Transfer — `CmafChunkMode` / `CmafChunkedConfig`
148//!
149//! [`streaming::mux::CmafChunkMode`] and [`streaming::mux::CmafChunkedConfig`] implement
150//! chunked CMAF delivery as defined in ISO/IEC 23000-19, enabling sub-segment delivery
151//! for LL-HLS and LL-DASH workflows.
152//!
153//! | Mode | Description |
154//! |------|-------------|
155//! | `Standard` | Whole-segment delivery (default, no chunking) |
156//! | `Chunked` | Each chunk is one or more complete `moof`+`mdat` pairs |
157//! | `LowLatencyChunked` | Each chunk is exactly one sample (minimum latency) |
158//!
159//! `CmafChunkedConfig` carries additional settings: `chunk_duration_ms`,
160//! `max_samples_per_chunk`, `include_mfra`, `signal_low_latency` (writes `cmfl`
161//! compatible brand in the `styp` box), and `part_target_duration_ms` for LL-HLS.
162//!
163//! ```ignore
164//! use oximedia_container::streaming::mux::{CmafChunkedConfig, CmafChunkMode};
165//!
166//! let config = CmafChunkedConfig::new()
167//! .with_mode(CmafChunkMode::LowLatencyChunked)
168//! .with_low_latency(true);
169//! ```
170//!
171//! ## Matroska v4 Block Addition Mapping — `BlockAdditionMapping`
172//!
173//! `demux::matroska::matroska_v4::BlockAdditionMapping` represents a Matroska v4
174//! `BlockAdditionMapping` element (EBML ID 0x41CB), which carries auxiliary per-block
175//! data channels such as HDR10+ metadata, Dolby Vision RPU data, or depth maps.
176//!
177//! | Field | Description |
178//! |-------|-------------|
179//! | `id_name` | Human-readable channel name (e.g., `"hdr10plus"`, `"dovi_rpu"`) |
180//! | `id_type` | Numeric type per the Matroska Block Addition Mapping Registry |
181//! | `id_extra_data` | Codec-specific configuration payload |
182//!
183//! Access via `StreamInfo::block_addition_mappings` after probing a Matroska track.
184
185pub mod attach;
186pub mod bitrate_stats;
187pub mod box_header;
188pub mod caf;
189pub mod chapters;
190pub mod chunk_map;
191pub mod container_probe;
192pub(crate) mod container_probe_parsers;
193pub mod cue;
194pub mod dash;
195pub mod data;
196pub mod demux;
197pub mod edit;
198pub mod edit_list;
199mod format;
200pub mod fragment;
201pub mod media_header;
202pub mod metadata;
203pub mod mkv_cluster;
204pub mod multi_angle;
205pub mod mux;
206pub mod ogg_page;
207mod packet;
208pub mod preroll;
209mod probe;
210pub mod pts_dts;
211pub mod pts_dts_batch;
212pub mod riff;
213pub mod sample_entry;
214pub mod sample_table;
215mod seek;
216pub mod segment_index;
217mod stream;
218pub mod stream_index;
219pub mod streaming;
220pub mod subtitle_mux;
221pub mod timecode;
222pub mod track_header;
223pub mod track_info;
224pub mod tracks;
225
226// Re-export main types at crate root
227pub use container_probe::{DetailedContainerInfo, DetailedStreamInfo, MultiFormatProber};
228pub use dash::{
229 emit_mpd, DashAdaptationSet, DashManifestConfig, DashRepresentation, DashSegmentTemplate,
230 DashSegmentTimeline, DashSegmentTimelineEntry,
231};
232pub use demux::mpegts::scte35::{
233 parse_splice_info_section, BreakDuration, Scte35Config, Scte35Parser, SpliceCommand,
234 SpliceDescriptor, SpliceInfoSection, SpliceInsert, SpliceTime, SCTE35_DEFAULT_PID,
235};
236pub use demux::Demuxer;
237pub use format::ContainerFormat;
238pub use metadata::batch::{BatchMetadataUpdate, BatchResult};
239pub use mux::mpegts::scte35::{
240 emit_splice_insert, emit_splice_null, emit_time_signal, SpliceInsertConfig,
241};
242pub use mux::{Muxer, MuxerConfig};
243pub use packet::{Packet, PacketFlags};
244pub use probe::{probe_format, ProbeResult};
245pub use seek::{
246 DecodeSkipCursor, MultiTrackSeeker, MultiTrackSeekerError, PtsSeekResult, SampleAccurateSeeker,
247 SampleIndexEntry, SeekAccuracy, SeekFlags, SeekIndex, SeekIndexEntry, SeekPlan, SeekResult,
248 SeekTarget, TrackIndex,
249};
250pub use stream::{CodecParams, Metadata, StreamInfo};