flowly_mp4/mp4box/
trak.rs1use 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}