1#![forbid(unsafe_code)]
8
9use crate::types::TrackInfo;
10use crate::{INLINE_TRACKS, MuxError, ids, vint};
11use smallvec::SmallVec;
12use std::marker::PhantomData;
13
14#[derive(Debug, Clone, Copy, Default)]
16pub struct Open;
17
18#[derive(Debug, Clone, Copy, Default)]
20pub struct Live;
21
22pub const DEFAULT_CLUSTER_BATCH: usize = 32;
24
25const DEFAULT_TIMECODE_SCALE: u64 = 1_000_000;
28
29const fn fits_relative_timecode(delta: i64) -> bool {
33 delta >= i16::MIN as i64 && delta <= i16::MAX as i64
34}
35
36#[derive(Debug)]
38pub struct Muxer<S = Open> {
39 tracks: SmallVec<[TrackInfo; INLINE_TRACKS]>,
40 timecode_scale: u64,
41 output: Vec<u8>,
42 output_consumed: usize,
43 cluster: Vec<u8>,
45 cluster_timecode: Option<i64>,
47 cluster_frames: usize,
48 batch: usize,
49 _state: PhantomData<S>,
50}
51
52impl Muxer<Open> {
53 #[must_use]
56 pub fn new() -> Self {
57 Self::with_options(DEFAULT_TIMECODE_SCALE, DEFAULT_CLUSTER_BATCH)
58 }
59
60 #[must_use]
62 pub fn with_options(timecode_scale: u64, batch: usize) -> Self {
63 Self {
64 tracks: SmallVec::new(),
65 timecode_scale: timecode_scale.max(1),
66 output: Vec::with_capacity(4 * 1024),
67 output_consumed: 0,
68 cluster: Vec::new(),
69 cluster_timecode: None,
70 cluster_frames: 0,
71 batch: batch.max(1),
72 _state: PhantomData,
73 }
74 }
75
76 #[must_use]
78 pub fn tracks(&self) -> &[TrackInfo] {
79 &self.tracks
80 }
81
82 pub fn add_track(&mut self, track: TrackInfo) -> Result<(), MuxError> {
89 if track.track_number == 0 {
90 return Err(MuxError::InvalidTrackNumber);
91 }
92 if self
93 .tracks
94 .iter()
95 .any(|t| t.track_number == track.track_number)
96 {
97 return Err(MuxError::DuplicateTrack(track.track_number));
98 }
99 self.tracks.push(track);
100 Ok(())
101 }
102
103 #[must_use]
106 pub fn begin(mut self) -> Muxer<Live> {
107 write_ebml_header(&mut self.output);
108 write_id(&mut self.output, ids::SEGMENT);
109 vint::encode_unknown_size(4, &mut self.output); write_segment_info(&mut self.output, self.timecode_scale);
111 write_tracks(&mut self.output, &self.tracks);
112 Muxer {
113 tracks: self.tracks,
114 timecode_scale: self.timecode_scale,
115 output: self.output,
116 output_consumed: self.output_consumed,
117 cluster: self.cluster,
118 cluster_timecode: self.cluster_timecode,
119 cluster_frames: self.cluster_frames,
120 batch: self.batch,
121 _state: PhantomData,
122 }
123 }
124}
125
126impl Default for Muxer<Open> {
127 fn default() -> Self {
128 Self::new()
129 }
130}
131
132impl Muxer<Live> {
133 #[must_use]
135 pub fn tracks(&self) -> &[TrackInfo] {
136 &self.tracks
137 }
138
139 #[must_use]
142 pub const fn timecode_scale(&self) -> u64 {
143 self.timecode_scale
144 }
145
146 pub fn push_frame(
160 &mut self,
161 track_number: u64,
162 timecode: i64,
163 is_keyframe: bool,
164 payload: &[u8],
165 ) -> Result<(), MuxError> {
166 if !self.tracks.iter().any(|t| t.track_number == track_number) {
167 return Err(MuxError::UnknownTrack(track_number));
168 }
169 let needs_new_cluster = match self.cluster_timecode {
170 None => true,
171 Some(base) => {
172 self.cluster_frames >= self.batch || !fits_relative_timecode(timecode - base)
173 }
174 };
175 if needs_new_cluster {
176 self.close_cluster();
177 self.cluster_timecode = Some(timecode);
178 }
179 let base = self.cluster_timecode.unwrap_or(timecode);
182 write_simple_block(
183 &mut self.cluster,
184 track_number,
185 (timecode - base) as i16,
186 is_keyframe,
187 payload,
188 );
189 self.cluster_frames += 1;
190 Ok(())
191 }
192
193 pub fn flush(&mut self) {
197 self.close_cluster();
198 }
199
200 pub fn poll_bytes(&mut self, out: &mut Vec<u8>) -> usize {
203 let available = self.output.len().saturating_sub(self.output_consumed);
204 if available == 0 {
205 return 0;
206 }
207 out.extend_from_slice(&self.output[self.output_consumed..]);
208 self.output_consumed = self.output.len();
209 if self.output_consumed >= 64 * 1024 {
210 self.output.drain(..self.output_consumed);
211 self.output_consumed = 0;
212 }
213 available
214 }
215
216 fn close_cluster(&mut self) {
217 if self.cluster_timecode.is_none() || self.cluster.is_empty() {
218 self.cluster.clear();
219 self.cluster_timecode = None;
220 self.cluster_frames = 0;
221 return;
222 }
223 let base = self.cluster_timecode.unwrap_or(0);
224 let mut body = Vec::with_capacity(self.cluster.len() + 16);
225 write_uint_elem(&mut body, ids::TIMECODE, base.max(0) as u64);
226 body.extend_from_slice(&self.cluster);
227 write_id(&mut self.output, ids::CLUSTER);
228 vint::encode_size(body.len() as u64, &mut self.output);
229 self.output.extend_from_slice(&body);
230 self.cluster.clear();
231 self.cluster_timecode = None;
232 self.cluster_frames = 0;
233 }
234}
235
236fn write_id(out: &mut Vec<u8>, id: u32) {
237 vint::encode_id(id, out);
238}
239
240fn write_ebml_header(out: &mut Vec<u8>) {
241 let mut body = Vec::new();
242 write_uint_elem(&mut body, ids::EBML_VERSION, 1);
243 write_uint_elem(&mut body, ids::EBML_READ_VERSION, 1);
244 write_uint_elem(&mut body, ids::EBML_MAX_ID_LENGTH, 4);
245 write_uint_elem(&mut body, ids::EBML_MAX_SIZE_LENGTH, 8);
246 write_string_elem(&mut body, ids::DOC_TYPE, "webm");
247 write_uint_elem(&mut body, ids::DOC_TYPE_VERSION, 2);
248 write_uint_elem(&mut body, ids::DOC_TYPE_READ_VERSION, 2);
249 write_id(out, ids::EBML_HEADER);
250 vint::encode_size(body.len() as u64, out);
251 out.extend_from_slice(&body);
252}
253
254fn write_segment_info(out: &mut Vec<u8>, timecode_scale: u64) {
255 let mut body = Vec::new();
256 write_uint_elem(&mut body, ids::TIMECODE_SCALE, timecode_scale);
257 write_id(out, ids::SEGMENT_INFO);
258 vint::encode_size(body.len() as u64, out);
259 out.extend_from_slice(&body);
260}
261
262fn write_tracks(out: &mut Vec<u8>, tracks: &[TrackInfo]) {
263 if tracks.is_empty() {
264 return;
265 }
266 let mut body = Vec::new();
267 for t in tracks {
268 write_track_entry(&mut body, t);
269 }
270 write_id(out, ids::TRACKS);
271 vint::encode_size(body.len() as u64, out);
272 out.extend_from_slice(&body);
273}
274
275fn write_track_entry(out: &mut Vec<u8>, t: &TrackInfo) {
276 let mut body = Vec::new();
277 write_uint_elem(&mut body, ids::TRACK_NUMBER, t.track_number);
278 write_uint_elem(&mut body, ids::TRACK_TYPE, u64::from(t.track_type));
279 write_string_elem(&mut body, ids::CODEC_ID, &t.codec_id);
280 if t.is_video() {
281 let mut video = Vec::new();
282 write_uint_elem(&mut video, ids::PIXEL_WIDTH, u64::from(t.width));
283 write_uint_elem(&mut video, ids::PIXEL_HEIGHT, u64::from(t.height));
284 write_id(&mut body, ids::VIDEO);
285 vint::encode_size(video.len() as u64, &mut body);
286 body.extend_from_slice(&video);
287 } else {
288 let mut audio = Vec::new();
289 write_float_elem(&mut audio, ids::SAMPLING_FREQUENCY, t.sample_rate);
290 write_uint_elem(&mut audio, ids::CHANNELS, u64::from(t.channels));
291 write_id(&mut body, ids::AUDIO);
292 vint::encode_size(audio.len() as u64, &mut body);
293 body.extend_from_slice(&audio);
294 }
295 write_id(out, ids::TRACK_ENTRY);
296 vint::encode_size(body.len() as u64, out);
297 out.extend_from_slice(&body);
298}
299
300fn write_simple_block(
305 out: &mut Vec<u8>,
306 track_number: u64,
307 relative_timecode: i16,
308 is_keyframe: bool,
309 payload: &[u8],
310) {
311 let mut body = Vec::with_capacity(payload.len() + 4);
312 vint::encode_size(track_number, &mut body);
313 body.extend_from_slice(&relative_timecode.to_be_bytes());
314 body.push(if is_keyframe { 0x80 } else { 0x00 });
315 body.extend_from_slice(payload);
316 write_id(out, ids::SIMPLE_BLOCK);
317 vint::encode_size(body.len() as u64, out);
318 out.extend_from_slice(&body);
319}
320
321fn write_uint_elem(out: &mut Vec<u8>, id: u32, value: u64) {
326 let be = value.to_be_bytes();
327 let first_nonzero = be.iter().position(|&b| b != 0).unwrap_or(be.len() - 1);
328 write_id(out, id);
329 vint::encode_size((be.len() - first_nonzero) as u64, out);
330 out.extend_from_slice(&be[first_nonzero..]);
331}
332
333fn write_float_elem(out: &mut Vec<u8>, id: u32, value: f64) {
337 write_id(out, id);
338 vint::encode_size(8, out);
339 out.extend_from_slice(&value.to_be_bytes());
340}
341
342fn write_string_elem(out: &mut Vec<u8>, id: u32, value: &str) {
346 write_id(out, id);
347 vint::encode_size(value.len() as u64, out);
348 out.extend_from_slice(value.as_bytes());
349}
350
351#[cfg(test)]
352#[path = "mux_tests.rs"]
353mod tests;