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