flowly_mp4/mp4box/
trak.rs

1use serde::Serialize;
2use std::io::Write;
3
4use crate::meta::MetaBox;
5use crate::mp4box::*;
6use crate::mp4box::{edts::EdtsBox, mdia::MdiaBox, tkhd::TkhdBox};
7
8#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize)]
9pub struct TrakBox {
10    pub tkhd: TkhdBox,
11    pub mdia: MdiaBox,
12
13    #[serde(skip_serializing_if = "Option::is_none")]
14    pub edts: Option<EdtsBox>,
15
16    #[serde(skip_serializing_if = "Option::is_none")]
17    pub meta: Option<MetaBox>,
18}
19
20impl TrakBox {
21    pub fn get_type(&self) -> BoxType {
22        BoxType::TrakBox
23    }
24
25    pub fn get_size(&self) -> u64 {
26        let mut size = HEADER_SIZE;
27        size += self.tkhd.box_size();
28        if let Some(ref edts) = self.edts {
29            size += edts.box_size();
30        }
31        size += self.mdia.box_size();
32        size
33    }
34
35    pub(crate) fn stsc_index(&self, sample_id: u32) -> Result<usize, Error> {
36        if self.mdia.minf.stbl.stsc.entries.is_empty() {
37            return Err(Error::InvalidData("no stsc entries"));
38        }
39
40        for (i, entry) in self.mdia.minf.stbl.stsc.entries.iter().enumerate() {
41            if sample_id < entry.first_sample {
42                return if i == 0 {
43                    Err(Error::InvalidData("sample not found"))
44                } else {
45                    Ok(i - 1)
46                };
47            }
48        }
49
50        Ok(self.mdia.minf.stbl.stsc.entries.len() - 1)
51    }
52
53    pub(crate) fn chunk_offset(&self, chunk_id: u32) -> Result<u64, Error> {
54        if self.mdia.minf.stbl.stco.is_none() && self.mdia.minf.stbl.co64.is_none() {
55            return Err(Error::InvalidData("must have either stco or co64 boxes"));
56        }
57
58        if let Some(ref stco) = self.mdia.minf.stbl.stco {
59            if let Some(offset) = stco.entries.get(chunk_id as usize - 1) {
60                return Ok(*offset as u64);
61            } else {
62                return Err(Error::EntryInStblNotFound(
63                    self.tkhd.track_id,
64                    BoxType::StcoBox,
65                    chunk_id,
66                ));
67            }
68        } else if let Some(ref co64) = self.mdia.minf.stbl.co64 {
69            if let Some(offset) = co64.entries.get(chunk_id as usize - 1) {
70                return Ok(*offset);
71            } else {
72                return Err(Error::EntryInStblNotFound(
73                    self.tkhd.track_id,
74                    BoxType::Co64Box,
75                    chunk_id,
76                ));
77            }
78        }
79
80        Err(Error::Box2NotFound(BoxType::StcoBox, BoxType::Co64Box))
81    }
82
83    pub(crate) fn sample_size(&self, sample_id: u32) -> Result<u32, Error> {
84        let stsz = &self.mdia.minf.stbl.stsz;
85
86        if stsz.sample_size > 0 {
87            return Ok(stsz.sample_size);
88        }
89
90        if let Some(size) = stsz.sample_sizes.get(sample_id as usize - 1) {
91            Ok(*size)
92        } else {
93            Err(Error::EntryInStblNotFound(
94                self.tkhd.track_id,
95                BoxType::StszBox,
96                sample_id,
97            ))
98        }
99    }
100
101    pub(crate) fn sample_offset(&self, sample_id: u32) -> Result<u64, Error> {
102        let stsc_index = self.stsc_index(sample_id)?;
103
104        let stsc = &self.mdia.minf.stbl.stsc;
105        let stsc_entry = stsc.entries.get(stsc_index).unwrap();
106
107        let first_chunk = stsc_entry.first_chunk;
108        let first_sample = stsc_entry.first_sample;
109        let samples_per_chunk = stsc_entry.samples_per_chunk;
110
111        let chunk_id = sample_id
112            .checked_sub(first_sample)
113            .map(|n| n / samples_per_chunk)
114            .and_then(|n| n.checked_add(first_chunk))
115            .ok_or(Error::InvalidData(
116                "attempt to calculate stsc chunk_id with overflow",
117            ))?;
118
119        let chunk_offset = self.chunk_offset(chunk_id)?;
120
121        let first_sample_in_chunk = sample_id - (sample_id - first_sample) % samples_per_chunk;
122
123        let mut sample_offset = 0;
124        for i in first_sample_in_chunk..sample_id {
125            sample_offset += self.sample_size(i)?;
126        }
127
128        Ok(chunk_offset + sample_offset as u64)
129    }
130
131    pub(crate) fn sample_time(&self, sample_id: u32) -> Result<(u64, u32), Error> {
132        let stts = &self.mdia.minf.stbl.stts;
133
134        let mut sample_count: u32 = 1;
135        let mut elapsed = 0;
136
137        for entry in stts.entries.iter() {
138            let new_sample_count =
139                sample_count
140                    .checked_add(entry.sample_count)
141                    .ok_or(Error::InvalidData(
142                        "attempt to sum stts entries sample_count with overflow",
143                    ))?;
144
145            if sample_id < new_sample_count {
146                let start_time =
147                    (sample_id - sample_count) as u64 * entry.sample_delta as u64 + elapsed;
148                return Ok((start_time, entry.sample_delta));
149            }
150
151            sample_count = new_sample_count;
152            elapsed += entry.sample_count as u64 * entry.sample_delta as u64;
153        }
154
155        Err(Error::EntryInStblNotFound(
156            self.tkhd.track_id,
157            BoxType::SttsBox,
158            sample_id,
159        ))
160    }
161
162    pub(crate) fn ctts_index(&self, sample_id: u32) -> Result<(usize, u32), Error> {
163        let ctts = self.mdia.minf.stbl.ctts.as_ref().unwrap();
164        let mut sample_count: u32 = 1;
165        for (i, entry) in ctts.entries.iter().enumerate() {
166            let next_sample_count =
167                sample_count
168                    .checked_add(entry.sample_count)
169                    .ok_or(Error::InvalidData(
170                        "attempt to sum ctts entries sample_count with overflow",
171                    ))?;
172            if sample_id < next_sample_count {
173                return Ok((i, sample_count));
174            }
175            sample_count = next_sample_count;
176        }
177
178        Err(Error::EntryInStblNotFound(
179            self.tkhd.track_id,
180            BoxType::CttsBox,
181            sample_id,
182        ))
183    }
184
185    pub fn sample_rendering_offset(&self, sample_id: u32) -> i32 {
186        if let Some(ref ctts) = self.mdia.minf.stbl.ctts {
187            if let Ok((ctts_index, _)) = self.ctts_index(sample_id) {
188                let ctts_entry = ctts.entries.get(ctts_index).unwrap();
189                return ctts_entry.sample_offset;
190            }
191        }
192
193        0
194    }
195
196    #[inline]
197    pub fn sample_is_sync(&self, sample_id: u32) -> bool {
198        if let Some(ref stss) = self.mdia.minf.stbl.stss {
199            stss.entries.binary_search(&sample_id).is_ok()
200        } else {
201            true
202        }
203    }
204}
205
206impl Mp4Box for TrakBox {
207    const TYPE: BoxType = BoxType::TrakBox;
208
209    fn box_size(&self) -> u64 {
210        self.get_size()
211    }
212
213    fn to_json(&self) -> Result<String, Error> {
214        Ok(serde_json::to_string(&self).unwrap())
215    }
216
217    fn summary(&self) -> Result<String, Error> {
218        let s = String::new();
219        Ok(s)
220    }
221}
222
223impl BlockReader for TrakBox {
224    fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result<Self, Error> {
225        let (tkhd, edts, meta, mdia) = reader.try_find_box4()?;
226
227        if tkhd.is_none() {
228            return Err(Error::BoxNotFound(BoxType::TkhdBox));
229        }
230
231        if mdia.is_none() {
232            return Err(Error::BoxNotFound(BoxType::MdiaBox));
233        }
234
235        Ok(TrakBox {
236            tkhd: tkhd.unwrap(),
237            edts,
238            meta,
239            mdia: mdia.unwrap(),
240        })
241    }
242
243    fn size_hint() -> usize {
244        TkhdBox::size_hint() + MdiaBox::size_hint()
245    }
246}
247
248impl<W: Write> WriteBox<&mut W> for TrakBox {
249    fn write_box(&self, writer: &mut W) -> Result<u64, Error> {
250        let size = self.box_size();
251        BoxHeader::new(Self::TYPE, size).write(writer)?;
252
253        self.tkhd.write_box(writer)?;
254        if let Some(ref edts) = self.edts {
255            edts.write_box(writer)?;
256        }
257        self.mdia.write_box(writer)?;
258
259        Ok(size)
260    }
261}