1use super::mp4::{extract_asc_from_stsd, extract_avcc_from_stsd};
13use alloc::vec::Vec;
14
15#[derive(Clone, Copy, Debug, PartialEq, Eq)]
16pub enum TrackKind {
17 Video,
18 Audio,
19 Other,
20}
21
22#[derive(Clone, Debug)]
24pub struct TrackInfo {
25 pub track_id: u32,
26 pub kind: TrackKind,
27 pub timescale: u32,
29 pub width: u32,
31 pub height: u32,
32 pub avcc: Option<Vec<u8>>,
34 pub asc: Option<Vec<u8>>,
36 default_duration: u32,
37 default_size: u32,
38 default_flags: u32,
39}
40
41#[derive(Clone, Debug)]
43pub struct FragSample {
44 pub track_id: u32,
45 pub dts: u64,
47 pub pts: i64,
49 pub duration: u32,
50 pub keyframe: bool,
51 pub data: Vec<u8>,
52}
53
54struct PendingSample {
56 track_id: u32,
57 dts: u64,
58 pts: i64,
59 duration: u32,
60 keyframe: bool,
61 offset: u64,
62 size: u32,
63}
64
65#[derive(Default)]
67pub struct Fmp4Demuxer {
68 pub tracks: Vec<TrackInfo>,
69 buf: Vec<u8>,
71 buf_pos: u64,
72 pending: Vec<PendingSample>,
74 next_dts: Vec<(u32, u64)>,
76}
77
78fn be32(d: &[u8], p: usize) -> Option<u32> {
79 d.get(p..p + 4).map(|b| u32::from_be_bytes([b[0], b[1], b[2], b[3]]))
80}
81
82fn be64(d: &[u8], p: usize) -> Option<u64> {
83 d.get(p..p + 8).map(|b| u64::from_be_bytes([b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]]))
84}
85
86fn box_header(d: &[u8], p: usize) -> Option<(usize, u64, [u8; 4])> {
88 let size = be32(d, p)? as u64;
89 let t = d.get(p + 4..p + 8)?;
90 let typ = [t[0], t[1], t[2], t[3]];
91 if size == 1 {
92 Some((16, be64(d, p + 8)?, typ))
93 } else {
94 Some((8, size, typ))
95 }
96}
97
98fn children(d: &[u8]) -> Vec<([u8; 4], &[u8])> {
100 let mut out = Vec::new();
101 let mut p = 0;
102 while let Some((hdr, size, typ)) = box_header(d, p) {
103 let size = if size == 0 { (d.len() - p) as u64 } else { size };
104 if size < hdr as u64 || p as u64 + size > d.len() as u64 {
105 break;
106 }
107 out.push((typ, &d[p + hdr..p + size as usize]));
108 p += size as usize;
109 }
110 out
111}
112
113fn child<'a>(d: &'a [u8], typ: &[u8; 4]) -> Option<&'a [u8]> {
114 children(d).into_iter().find(|(t, _)| t == typ).map(|(_, c)| c)
115}
116
117impl Fmp4Demuxer {
118 pub fn new() -> Self {
119 Self::default()
120 }
121
122 pub fn track(&self, id: u32) -> Option<&TrackInfo> {
123 self.tracks.iter().find(|t| t.track_id == id)
124 }
125
126 pub fn push(&mut self, bytes: &[u8]) -> Result<Vec<FragSample>, &'static str> {
128 self.buf.extend_from_slice(bytes);
129 let mut out = Vec::new();
130 while let Some((hdr, size, typ)) = box_header(&self.buf, 0) {
131 if size == 0 {
132 break;
134 }
135 if size < hdr as u64 {
136 return Err("fMP4: bad box size");
137 }
138 if size > 512 * 1024 * 1024 {
139 return Err("fMP4: box too large");
140 }
141 if (self.buf.len() as u64) < size {
142 break;
143 }
144 let size = size as usize;
145 let start = self.buf_pos;
146 let body: Vec<u8> = self.buf[hdr..size].to_vec();
147 match &typ {
148 b"moov" => self.parse_moov(&body)?,
149 b"moof" => self.parse_moof(&body, start)?,
150 b"mdat" => self.take_mdat(&body, start + hdr as u64, &mut out),
151 _ => {} }
153 self.buf.drain(..size);
154 self.buf_pos += size as u64;
155 }
156 Ok(out)
157 }
158
159 fn parse_moov(&mut self, moov: &[u8]) -> Result<(), &'static str> {
160 self.tracks.clear();
161 for (t, trak) in children(moov) {
162 if &t != b"trak" {
163 continue;
164 }
165 let Some(tkhd) = child(trak, b"tkhd") else { continue };
166 let is_tkhd_v1 = tkhd.first() == Some(&1);
167 let track_id = if is_tkhd_v1 { be32(tkhd, 20) } else { be32(tkhd, 12) }.unwrap_or(0);
168 let tkhd_w = be32(tkhd, if is_tkhd_v1 { 88 } else { 76 }).map_or(0, |v| v >> 16);
170 let tkhd_h = be32(tkhd, if is_tkhd_v1 { 92 } else { 80 }).map_or(0, |v| v >> 16);
171 let Some(mdia) = child(trak, b"mdia") else { continue };
172 let timescale = child(mdia, b"mdhd")
173 .and_then(|m| if m.first() == Some(&1) { be32(m, 20) } else { be32(m, 12) })
174 .unwrap_or(1000)
175 .max(1);
176 let kind = match child(mdia, b"hdlr").and_then(|h| h.get(8..12)) {
177 Some(b"vide") => TrackKind::Video,
178 Some(b"soun") => TrackKind::Audio,
179 _ => TrackKind::Other,
180 };
181 let stsd = child(mdia, b"minf").and_then(|m| child(m, b"stbl")).and_then(|s| child(s, b"stsd"));
182 let (stsd_w, stsd_h) = stsd
184 .and_then(|s| s.get(40..44))
185 .map(|b| (u16::from_be_bytes([b[0], b[1]]) as u32, u16::from_be_bytes([b[2], b[3]]) as u32))
186 .unwrap_or((0, 0));
187 let (width, height) = if kind == TrackKind::Video {
188 (if stsd_w > 0 { stsd_w } else { tkhd_w }, if stsd_h > 0 { stsd_h } else { tkhd_h })
189 } else {
190 (0, 0)
191 };
192 let (avcc, asc) = match (kind, stsd) {
193 (TrackKind::Video, Some(s)) => (extract_avcc_from_stsd(s), None),
194 (TrackKind::Audio, Some(s)) => (None, extract_asc_from_stsd(s)),
195 _ => (None, None),
196 };
197 self.tracks.push(TrackInfo {
198 track_id,
199 kind,
200 timescale,
201 width,
202 height,
203 avcc,
204 asc,
205 default_duration: 0,
206 default_size: 0,
207 default_flags: 0,
208 });
209 }
210 if let Some(mvex) = child(moov, b"mvex") {
212 for (t, trex) in children(mvex) {
213 if &t != b"trex" {
214 continue;
215 }
216 let id = be32(trex, 4).unwrap_or(0);
217 if let Some(tr) = self.tracks.iter_mut().find(|x| x.track_id == id) {
218 tr.default_duration = be32(trex, 12).unwrap_or(0);
219 tr.default_size = be32(trex, 16).unwrap_or(0);
220 tr.default_flags = be32(trex, 20).unwrap_or(0);
221 }
222 }
223 }
224 if self.tracks.is_empty() {
225 return Err("fMP4: no tracks in moov");
226 }
227 Ok(())
228 }
229
230 fn parse_moof(&mut self, moof: &[u8], moof_pos: u64) -> Result<(), &'static str> {
232 self.pending.clear();
233 let mut implicit_base = moof_pos;
236 for (t, traf) in children(moof) {
237 if &t != b"traf" {
238 continue;
239 }
240 let tfhd = child(traf, b"tfhd").ok_or("fMP4: traf without tfhd")?;
241 let flags = be32(tfhd, 0).unwrap_or(0) & 0xff_ffff;
242 let track_id = be32(tfhd, 4).ok_or("fMP4: short tfhd")?;
243 let tr = self.track(track_id).cloned();
244 let (mut dd, mut ds, mut df) = tr.as_ref().map_or((0, 0, 0), |t| (t.default_duration, t.default_size, t.default_flags));
245 let mut p = 8;
246 let mut base = if flags & 0x02_0000 != 0 { moof_pos } else { implicit_base };
247 if flags & 0x01 != 0 {
248 base = be64(tfhd, p).ok_or("fMP4: short tfhd")?;
249 p += 8;
250 }
251 if flags & 0x02 != 0 {
252 p += 4; }
254 if flags & 0x08 != 0 {
255 dd = be32(tfhd, p).ok_or("fMP4: short tfhd")?;
256 p += 4;
257 }
258 if flags & 0x10 != 0 {
259 ds = be32(tfhd, p).ok_or("fMP4: short tfhd")?;
260 p += 4;
261 }
262 if flags & 0x20 != 0 {
263 df = be32(tfhd, p).ok_or("fMP4: short tfhd")?;
264 }
265 let mut dts = match child(traf, b"tfdt") {
266 Some(tfdt) if tfdt.first() == Some(&1) => be64(tfdt, 4).unwrap_or(0),
267 Some(tfdt) => be32(tfdt, 4).unwrap_or(0) as u64,
268 None => self.next_dts.iter().find(|(id, _)| *id == track_id).map_or(0, |x| x.1),
269 };
270 let mut data_end = base;
271 for (t2, trun) in children(traf) {
272 if &t2 != b"trun" {
273 continue;
274 }
275 let version = trun.first().copied().unwrap_or(0);
276 let tf = be32(trun, 0).unwrap_or(0) & 0xff_ffff;
277 let count = be32(trun, 4).ok_or("fMP4: short trun")? as usize;
278 if count > 1_000_000 {
279 return Err("fMP4: too many samples in trun");
280 }
281 let mut q = 8;
282 let mut off = data_end;
283 if tf & 0x01 != 0 {
284 let d = be32(trun, q).ok_or("fMP4: short trun")? as i32;
285 off = (base as i64 + d as i64).max(0) as u64;
286 q += 4;
287 }
288 let mut first_flags = None;
289 if tf & 0x04 != 0 {
290 first_flags = Some(be32(trun, q).ok_or("fMP4: short trun")?);
291 q += 4;
292 }
293 for i in 0..count {
294 let mut dur = dd;
295 let mut size = ds;
296 let mut sflags = if i == 0 { first_flags.unwrap_or(df) } else { df };
297 let mut cto: i64 = 0;
298 if tf & 0x100 != 0 {
299 dur = be32(trun, q).ok_or("fMP4: short trun")?;
300 q += 4;
301 }
302 if tf & 0x200 != 0 {
303 size = be32(trun, q).ok_or("fMP4: short trun")?;
304 q += 4;
305 }
306 if tf & 0x400 != 0 {
307 let f = be32(trun, q).ok_or("fMP4: short trun")?;
308 if !(i == 0 && first_flags.is_some()) {
309 sflags = f;
310 }
311 q += 4;
312 }
313 if tf & 0x800 != 0 {
314 let v = be32(trun, q).ok_or("fMP4: short trun")?;
315 cto = if version == 0 { v as i64 } else { v as i32 as i64 };
316 q += 4;
317 }
318 let keyframe = sflags & 0x1_0000 == 0;
320 self.pending.push(PendingSample {
321 track_id,
322 dts,
323 pts: dts as i64 + cto,
324 duration: dur,
325 keyframe,
326 offset: off,
327 size,
328 });
329 dts += dur as u64;
330 off += size as u64;
331 }
332 data_end = off;
333 }
334 implicit_base = data_end;
335 if let Some(e) = self.next_dts.iter_mut().find(|(id, _)| *id == track_id) {
336 e.1 = dts;
337 } else {
338 self.next_dts.push((track_id, dts));
339 }
340 }
341 Ok(())
342 }
343
344 fn take_mdat(&mut self, mdat: &[u8], data_pos: u64, out: &mut Vec<FragSample>) {
346 let end = data_pos + mdat.len() as u64;
347 self.pending.retain(|s| {
348 if s.offset >= data_pos && s.offset + s.size as u64 <= end {
349 let a = (s.offset - data_pos) as usize;
350 out.push(FragSample {
351 track_id: s.track_id,
352 dts: s.dts,
353 pts: s.pts,
354 duration: s.duration,
355 keyframe: s.keyframe,
356 data: mdat[a..a + s.size as usize].to_vec(),
357 });
358 false
359 } else {
360 true
361 }
362 });
363 }
364}