Skip to main content

mp4/mp4box/
trun.rs

1use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
2use serde::Serialize;
3use std::io::{Read, Seek, Write};
4use std::mem::size_of;
5
6use crate::mp4box::*;
7
8#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize)]
9pub struct TrunBox {
10    pub version: u8,
11    pub flags: u32,
12    pub sample_count: u32,
13    pub data_offset: Option<i32>,
14    pub first_sample_flags: Option<u32>,
15
16    #[serde(skip_serializing)]
17    pub sample_durations: Vec<u32>,
18    #[serde(skip_serializing)]
19    pub sample_sizes: Vec<u32>,
20    #[serde(skip_serializing)]
21    pub sample_flags: Vec<u32>,
22    #[serde(skip_serializing)]
23    pub sample_cts: Vec<u32>,
24}
25
26impl TrunBox {
27    pub const FLAG_DATA_OFFSET: u32 = 0x01;
28    pub const FLAG_FIRST_SAMPLE_FLAGS: u32 = 0x04;
29    pub const FLAG_SAMPLE_DURATION: u32 = 0x100;
30    pub const FLAG_SAMPLE_SIZE: u32 = 0x200;
31    pub const FLAG_SAMPLE_FLAGS: u32 = 0x400;
32    pub const FLAG_SAMPLE_CTS: u32 = 0x800;
33
34    pub fn get_type(&self) -> BoxType {
35        BoxType::TrunBox
36    }
37
38    pub fn get_size(&self) -> u64 {
39        let mut sum = HEADER_SIZE + HEADER_EXT_SIZE + 4;
40        if TrunBox::FLAG_DATA_OFFSET & self.flags > 0 {
41            sum += 4;
42        }
43        if TrunBox::FLAG_FIRST_SAMPLE_FLAGS & self.flags > 0 {
44            sum += 4;
45        }
46        if TrunBox::FLAG_SAMPLE_DURATION & self.flags > 0 {
47            sum += 4 * self.sample_count as u64;
48        }
49        if TrunBox::FLAG_SAMPLE_SIZE & self.flags > 0 {
50            sum += 4 * self.sample_count as u64;
51        }
52        if TrunBox::FLAG_SAMPLE_FLAGS & self.flags > 0 {
53            sum += 4 * self.sample_count as u64;
54        }
55        if TrunBox::FLAG_SAMPLE_CTS & self.flags > 0 {
56            sum += 4 * self.sample_count as u64;
57        }
58        sum
59    }
60}
61
62impl Mp4Box for TrunBox {
63    fn box_type(&self) -> BoxType {
64        self.get_type()
65    }
66
67    fn box_size(&self) -> u64 {
68        self.get_size()
69    }
70
71    fn to_json(&self) -> Result<String> {
72        Ok(serde_json::to_string(&self).unwrap())
73    }
74
75    fn summary(&self) -> Result<String> {
76        let s = format!("sample_size={}", self.sample_count);
77        Ok(s)
78    }
79}
80
81impl<R: Read + Seek> ReadBox<&mut R> for TrunBox {
82    fn read_box(reader: &mut R, size: u64) -> Result<Self> {
83        let start = box_start(reader)?;
84
85        let (version, flags) = read_box_header_ext(reader)?;
86
87        let header_size = HEADER_SIZE + HEADER_EXT_SIZE;
88        let other_size = size_of::<u32>() // sample_count
89            + if TrunBox::FLAG_DATA_OFFSET & flags > 0 { size_of::<i32>() } else { 0 } // data_offset
90            + if TrunBox::FLAG_FIRST_SAMPLE_FLAGS & flags > 0 { size_of::<u32>() } else { 0 }; // first_sample_flags
91        let sample_size = if TrunBox::FLAG_SAMPLE_DURATION & flags > 0 { size_of::<u32>() } else { 0 } // sample_duration
92            + if TrunBox::FLAG_SAMPLE_SIZE & flags > 0 { size_of::<u32>() } else { 0 } // sample_size
93            + if TrunBox::FLAG_SAMPLE_FLAGS & flags > 0 { size_of::<u32>() } else { 0 } // sample_flags
94            + if TrunBox::FLAG_SAMPLE_CTS & flags > 0 { size_of::<u32>() } else { 0 }; // sample_composition_time_offset
95
96        let sample_count = reader.read_u32::<BigEndian>()?;
97
98        let data_offset = if TrunBox::FLAG_DATA_OFFSET & flags > 0 {
99            Some(reader.read_i32::<BigEndian>()?)
100        } else {
101            None
102        };
103
104        let first_sample_flags = if TrunBox::FLAG_FIRST_SAMPLE_FLAGS & flags > 0 {
105            Some(reader.read_u32::<BigEndian>()?)
106        } else {
107            None
108        };
109
110        let mut sample_durations = Vec::new();
111        let mut sample_sizes = Vec::new();
112        let mut sample_flags = Vec::new();
113        let mut sample_cts = Vec::new();
114        if u64::from(sample_count) * sample_size as u64
115            > size
116                .saturating_sub(header_size)
117                .saturating_sub(other_size as u64)
118        {
119            return Err(Error::InvalidData(
120                "trun sample_count indicates more values than could fit in the box",
121            ));
122        }
123        if TrunBox::FLAG_SAMPLE_DURATION & flags > 0 {
124            sample_durations.reserve(sample_count as usize);
125        }
126        if TrunBox::FLAG_SAMPLE_SIZE & flags > 0 {
127            sample_sizes.reserve(sample_count as usize);
128        }
129        if TrunBox::FLAG_SAMPLE_FLAGS & flags > 0 {
130            sample_flags.reserve(sample_count as usize);
131        }
132        if TrunBox::FLAG_SAMPLE_CTS & flags > 0 {
133            sample_cts.reserve(sample_count as usize);
134        }
135
136        for _ in 0..sample_count {
137            if TrunBox::FLAG_SAMPLE_DURATION & flags > 0 {
138                let duration = reader.read_u32::<BigEndian>()?;
139                sample_durations.push(duration);
140            }
141
142            if TrunBox::FLAG_SAMPLE_SIZE & flags > 0 {
143                let sample_size = reader.read_u32::<BigEndian>()?;
144                sample_sizes.push(sample_size);
145            }
146
147            if TrunBox::FLAG_SAMPLE_FLAGS & flags > 0 {
148                let sample_flag = reader.read_u32::<BigEndian>()?;
149                sample_flags.push(sample_flag);
150            }
151
152            if TrunBox::FLAG_SAMPLE_CTS & flags > 0 {
153                let cts = reader.read_u32::<BigEndian>()?;
154                sample_cts.push(cts);
155            }
156        }
157
158        skip_bytes_to(reader, start + size)?;
159
160        Ok(TrunBox {
161            version,
162            flags,
163            sample_count,
164            data_offset,
165            first_sample_flags,
166            sample_durations,
167            sample_sizes,
168            sample_flags,
169            sample_cts,
170        })
171    }
172}
173
174impl<W: Write> WriteBox<&mut W> for TrunBox {
175    fn write_box(&self, writer: &mut W) -> Result<u64> {
176        let size = self.box_size();
177        BoxHeader::new(self.box_type(), size).write(writer)?;
178
179        write_box_header_ext(writer, self.version, self.flags)?;
180
181        writer.write_u32::<BigEndian>(self.sample_count)?;
182        if let Some(v) = self.data_offset {
183            writer.write_i32::<BigEndian>(v)?;
184        }
185        if let Some(v) = self.first_sample_flags {
186            writer.write_u32::<BigEndian>(v)?;
187        }
188        if self.sample_count != self.sample_sizes.len() as u32 {
189            return Err(Error::InvalidData("sample count out of sync"));
190        }
191        for i in 0..self.sample_count as usize {
192            if TrunBox::FLAG_SAMPLE_DURATION & self.flags > 0 {
193                writer.write_u32::<BigEndian>(self.sample_durations[i])?;
194            }
195            if TrunBox::FLAG_SAMPLE_SIZE & self.flags > 0 {
196                writer.write_u32::<BigEndian>(self.sample_sizes[i])?;
197            }
198            if TrunBox::FLAG_SAMPLE_FLAGS & self.flags > 0 {
199                writer.write_u32::<BigEndian>(self.sample_flags[i])?;
200            }
201            if TrunBox::FLAG_SAMPLE_CTS & self.flags > 0 {
202                writer.write_u32::<BigEndian>(self.sample_cts[i])?;
203            }
204        }
205
206        Ok(size)
207    }
208}
209
210#[cfg(test)]
211mod tests {
212    use super::*;
213    use crate::mp4box::BoxHeader;
214    use std::io::Cursor;
215
216    #[test]
217    fn test_trun_same_size() {
218        let src_box = TrunBox {
219            version: 0,
220            flags: 0,
221            data_offset: None,
222            sample_count: 0,
223            sample_sizes: vec![],
224            sample_flags: vec![],
225            first_sample_flags: None,
226            sample_durations: vec![],
227            sample_cts: vec![],
228        };
229        let mut buf = Vec::new();
230        src_box.write_box(&mut buf).unwrap();
231        assert_eq!(buf.len(), src_box.box_size() as usize);
232
233        let mut reader = Cursor::new(&buf);
234        let header = BoxHeader::read(&mut reader).unwrap();
235        assert_eq!(header.name, BoxType::TrunBox);
236        assert_eq!(src_box.box_size(), header.size);
237
238        let dst_box = TrunBox::read_box(&mut reader, header.size).unwrap();
239        assert_eq!(src_box, dst_box);
240    }
241
242    #[test]
243    fn test_trun_many_sizes() {
244        let src_box = TrunBox {
245            version: 0,
246            flags: TrunBox::FLAG_SAMPLE_DURATION
247                | TrunBox::FLAG_SAMPLE_SIZE
248                | TrunBox::FLAG_SAMPLE_FLAGS
249                | TrunBox::FLAG_SAMPLE_CTS,
250            data_offset: None,
251            sample_count: 9,
252            sample_sizes: vec![1165, 11, 11, 8545, 10126, 10866, 9643, 9351, 7730],
253            sample_flags: vec![1165, 11, 11, 8545, 10126, 10866, 9643, 9351, 7730],
254            first_sample_flags: None,
255            sample_durations: vec![1165, 11, 11, 8545, 10126, 10866, 9643, 9351, 7730],
256            sample_cts: vec![1165, 11, 11, 8545, 10126, 10866, 9643, 9351, 7730],
257        };
258        let mut buf = Vec::new();
259        src_box.write_box(&mut buf).unwrap();
260        assert_eq!(buf.len(), src_box.box_size() as usize);
261
262        let mut reader = Cursor::new(&buf);
263        let header = BoxHeader::read(&mut reader).unwrap();
264        assert_eq!(header.name, BoxType::TrunBox);
265        assert_eq!(src_box.box_size(), header.size);
266
267        let dst_box = TrunBox::read_box(&mut reader, header.size).unwrap();
268        assert_eq!(src_box, dst_box);
269    }
270}