scuffle_mp4/boxes/types/
trun.rs

1use std::io;
2
3use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
4use bytes::Bytes;
5
6use crate::boxes::header::{BoxHeader, FullBoxHeader};
7use crate::boxes::traits::BoxType;
8
9#[derive(Debug, Clone, PartialEq)]
10/// Track Fragment Run Box
11/// ISO/IEC 14496-12:2022(E) - 8.8.8
12pub struct Trun {
13    pub header: FullBoxHeader,
14    pub data_offset: Option<i32>,
15    pub first_sample_flags: Option<TrunSampleFlag>,
16    pub samples: Vec<TrunSample>,
17}
18
19impl Trun {
20    pub fn new(samples: Vec<TrunSample>, first_sample_flags: Option<TrunSampleFlag>) -> Self {
21        let flags = if samples.is_empty() {
22            Self::FLAG_DATA_OFFSET
23        } else {
24            let mut flags = Self::FLAG_DATA_OFFSET;
25            if samples[0].duration.is_some() {
26                flags |= Self::FLAG_SAMPLE_DURATION;
27            }
28            if samples[0].size.is_some() {
29                flags |= Self::FLAG_SAMPLE_SIZE;
30            }
31            if samples[0].flags.is_some() {
32                flags |= Self::FLAG_SAMPLE_FLAGS;
33            }
34            if samples[0].composition_time_offset.is_some() {
35                flags |= Self::FLAG_SAMPLE_COMPOSITION_TIME_OFFSET;
36            }
37            flags
38        };
39
40        let version = if samples
41            .iter()
42            .any(|s| s.composition_time_offset.is_some() && s.composition_time_offset.unwrap() < 0)
43        {
44            1
45        } else {
46            0
47        };
48
49        Self {
50            header: FullBoxHeader::new(Self::NAME, version, flags),
51            data_offset: Some(0),
52            first_sample_flags,
53            samples,
54        }
55    }
56}
57
58#[derive(Debug, Clone, PartialEq)]
59pub struct TrunSample {
60    pub duration: Option<u32>,
61    pub size: Option<u32>,
62    pub flags: Option<TrunSampleFlag>,
63    pub composition_time_offset: Option<i64>, // we use i64 because it is either a u32 or a i32
64}
65
66#[derive(Debug, Clone, PartialEq, Copy, Default)]
67pub struct TrunSampleFlag {
68    pub reserved: u8,                     // 4 bits
69    pub is_leading: u8,                   // 2 bits
70    pub sample_depends_on: u8,            // 2 bits
71    pub sample_is_depended_on: u8,        // 2 bits
72    pub sample_has_redundancy: u8,        // 2 bits
73    pub sample_padding_value: u8,         // 3 bits
74    pub sample_is_non_sync_sample: bool,  // 1 bit
75    pub sample_degradation_priority: u16, // 16 bits
76}
77
78impl TrunSampleFlag {
79    pub fn validate(&self) -> io::Result<()> {
80        if self.reserved != 0 {
81            return Err(io::Error::new(io::ErrorKind::InvalidData, "reserved bits must be 0"));
82        }
83
84        if self.is_leading > 2 {
85            return Err(io::Error::new(io::ErrorKind::InvalidData, "is_leading must be 0, 1 or 2"));
86        }
87
88        if self.sample_depends_on > 2 {
89            return Err(io::Error::new(
90                io::ErrorKind::InvalidData,
91                "sample_depends_on must be 0, 1 or 2",
92            ));
93        }
94
95        if self.sample_is_depended_on > 2 {
96            return Err(io::Error::new(
97                io::ErrorKind::InvalidData,
98                "sample_is_depended_on must be 0, 1 or 2",
99            ));
100        }
101
102        if self.sample_has_redundancy > 2 {
103            return Err(io::Error::new(
104                io::ErrorKind::InvalidData,
105                "sample_has_redundancy must be 0, 1 or 2",
106            ));
107        }
108
109        if self.sample_padding_value > 7 {
110            return Err(io::Error::new(
111                io::ErrorKind::InvalidData,
112                "sample_padding_value must be 0, 1, 2, 3, 4, 5, 6 or 7",
113            ));
114        }
115
116        Ok(())
117    }
118}
119
120impl From<u32> for TrunSampleFlag {
121    fn from(value: u32) -> Self {
122        let reserved = (value >> 28) as u8;
123        let is_leading = ((value >> 26) & 0b11) as u8;
124        let sample_depends_on = ((value >> 24) & 0b11) as u8;
125        let sample_is_depended_on = ((value >> 22) & 0b11) as u8;
126        let sample_has_redundancy = ((value >> 20) & 0b11) as u8;
127        let sample_padding_value = ((value >> 17) & 0b111) as u8;
128        let sample_is_non_sync_sample = ((value >> 16) & 0b1) != 0;
129        let sample_degradation_priority = (value & 0xFFFF) as u16;
130
131        Self {
132            reserved,
133            is_leading,
134            sample_depends_on,
135            sample_is_depended_on,
136            sample_has_redundancy,
137            sample_padding_value,
138            sample_is_non_sync_sample,
139            sample_degradation_priority,
140        }
141    }
142}
143
144impl From<TrunSampleFlag> for u32 {
145    fn from(value: TrunSampleFlag) -> Self {
146        let mut result = 0;
147
148        result |= (value.reserved as u32) << 28;
149        result |= (value.is_leading as u32) << 26;
150        result |= (value.sample_depends_on as u32) << 24;
151        result |= (value.sample_is_depended_on as u32) << 22;
152        result |= (value.sample_has_redundancy as u32) << 20;
153        result |= (value.sample_padding_value as u32) << 17;
154        result |= (value.sample_is_non_sync_sample as u32) << 16;
155        result |= value.sample_degradation_priority as u32;
156
157        result
158    }
159}
160
161impl Trun {
162    pub const FLAG_DATA_OFFSET: u32 = 0x000001;
163    pub const FLAG_FIRST_SAMPLE_FLAGS: u32 = 0x000004;
164    pub const FLAG_SAMPLE_COMPOSITION_TIME_OFFSET: u32 = 0x000800;
165    pub const FLAG_SAMPLE_DURATION: u32 = 0x000100;
166    pub const FLAG_SAMPLE_FLAGS: u32 = 0x000400;
167    pub const FLAG_SAMPLE_SIZE: u32 = 0x000200;
168}
169
170impl BoxType for Trun {
171    const NAME: [u8; 4] = *b"trun";
172
173    fn demux(header: BoxHeader, data: Bytes) -> io::Result<Self> {
174        let mut reader = io::Cursor::new(data);
175
176        let header = FullBoxHeader::demux(header, &mut reader)?;
177
178        let sample_count = reader.read_u32::<BigEndian>()?;
179
180        let data_offset = if header.flags & Self::FLAG_DATA_OFFSET != 0 {
181            Some(reader.read_i32::<BigEndian>()?)
182        } else {
183            None
184        };
185
186        let first_sample_flags = if header.flags & Self::FLAG_FIRST_SAMPLE_FLAGS != 0 {
187            Some(reader.read_u32::<BigEndian>()?.into())
188        } else {
189            None
190        };
191
192        let mut samples = Vec::with_capacity(sample_count as usize);
193        for _ in 0..sample_count {
194            let duration = if header.flags & Self::FLAG_SAMPLE_DURATION != 0 {
195                Some(reader.read_u32::<BigEndian>()?)
196            } else {
197                None
198            };
199
200            let size = if header.flags & Self::FLAG_SAMPLE_SIZE != 0 {
201                Some(reader.read_u32::<BigEndian>()?)
202            } else {
203                None
204            };
205
206            let flags = if header.flags & Self::FLAG_SAMPLE_FLAGS != 0 {
207                Some(reader.read_u32::<BigEndian>()?.into())
208            } else {
209                None
210            };
211
212            let composition_time_offset = if header.flags & Self::FLAG_SAMPLE_COMPOSITION_TIME_OFFSET != 0 {
213                if header.version == 1 {
214                    Some(reader.read_i32::<BigEndian>()? as i64)
215                } else {
216                    Some(reader.read_u32::<BigEndian>()? as i64)
217                }
218            } else {
219                None
220            };
221
222            samples.push(TrunSample {
223                duration,
224                size,
225                flags,
226                composition_time_offset,
227            });
228        }
229
230        Ok(Self {
231            header,
232            data_offset,
233            first_sample_flags,
234            samples,
235        })
236    }
237
238    fn primitive_size(&self) -> u64 {
239        self.header.size()
240        + 4 // sample_count
241        + if self.header.flags & Self::FLAG_DATA_OFFSET != 0 { 4 } else { 0 }
242        + if self.header.flags & Self::FLAG_FIRST_SAMPLE_FLAGS != 0 { 4 } else { 0 }
243        + self.samples.iter().map(|_| {
244            (if self.header.flags & Self::FLAG_SAMPLE_DURATION != 0 { 4 } else { 0 })
245            + (if self.header.flags & Self::FLAG_SAMPLE_SIZE != 0 { 4 } else { 0 })
246            + (if self.header.flags & Self::FLAG_SAMPLE_FLAGS != 0 { 4 } else { 0 })
247            + (if self.header.flags & Self::FLAG_SAMPLE_COMPOSITION_TIME_OFFSET != 0 {
248                4
249            } else {
250                0
251            })
252        }).sum::<u64>()
253    }
254
255    fn primitive_mux<T: io::Write>(&self, writer: &mut T) -> io::Result<()> {
256        self.header.mux(writer)?;
257
258        writer.write_u32::<BigEndian>(self.samples.len() as u32)?;
259
260        if let Some(data_offset) = self.data_offset {
261            writer.write_i32::<BigEndian>(data_offset)?;
262        }
263
264        if let Some(first_sample_flags) = self.first_sample_flags {
265            writer.write_u32::<BigEndian>(first_sample_flags.into())?;
266        }
267
268        for sample in &self.samples {
269            if let Some(duration) = sample.duration {
270                writer.write_u32::<BigEndian>(duration)?;
271            }
272
273            if let Some(size) = sample.size {
274                writer.write_u32::<BigEndian>(size)?;
275            }
276
277            if let Some(flags) = sample.flags {
278                writer.write_u32::<BigEndian>(flags.into())?;
279            }
280
281            if let Some(composition_time_offset) = sample.composition_time_offset {
282                if self.header.version == 1 {
283                    writer.write_i32::<BigEndian>(composition_time_offset as i32)?;
284                } else {
285                    writer.write_u32::<BigEndian>(composition_time_offset as u32)?;
286                }
287            }
288        }
289
290        Ok(())
291    }
292
293    fn validate(&self) -> io::Result<()> {
294        if self.header.version > 1 {
295            return Err(io::Error::new(io::ErrorKind::InvalidData, "trun version must be 0 or 1"));
296        }
297
298        if self.header.flags & Self::FLAG_SAMPLE_COMPOSITION_TIME_OFFSET == 0 && self.header.version == 1 {
299            return Err(io::Error::new(
300                io::ErrorKind::InvalidData,
301                "trun version must be 0 if sample composition time offset is not present",
302            ));
303        }
304
305        if self.header.flags & Self::FLAG_DATA_OFFSET != 0 && self.data_offset.is_none() {
306            return Err(io::Error::new(
307                io::ErrorKind::InvalidData,
308                "trun data offset is present but not set",
309            ));
310        } else if self.header.flags & Self::FLAG_DATA_OFFSET == 0 && self.data_offset.is_some() {
311            return Err(io::Error::new(
312                io::ErrorKind::InvalidData,
313                "trun data offset is not present but set",
314            ));
315        }
316
317        if self.header.flags & Self::FLAG_FIRST_SAMPLE_FLAGS != 0 && self.first_sample_flags.is_none() {
318            return Err(io::Error::new(
319                io::ErrorKind::InvalidData,
320                "trun first sample flags is present but not set",
321            ));
322        } else if self.header.flags & Self::FLAG_FIRST_SAMPLE_FLAGS == 0 && self.first_sample_flags.is_some() {
323            return Err(io::Error::new(
324                io::ErrorKind::InvalidData,
325                "trun first sample flags is not present but set",
326            ));
327        }
328
329        if self.header.flags & Self::FLAG_SAMPLE_DURATION != 0 && self.samples.iter().any(|s| s.duration.is_none()) {
330            return Err(io::Error::new(
331                io::ErrorKind::InvalidData,
332                "trun sample duration is present but not set",
333            ));
334        } else if self.header.flags & Self::FLAG_SAMPLE_DURATION == 0 && self.samples.iter().any(|s| s.duration.is_some()) {
335            return Err(io::Error::new(
336                io::ErrorKind::InvalidData,
337                "trun sample duration is not present but set",
338            ));
339        }
340
341        if self.header.flags & Self::FLAG_SAMPLE_SIZE != 0 && self.samples.iter().any(|s| s.size.is_none()) {
342            return Err(io::Error::new(
343                io::ErrorKind::InvalidData,
344                "trun sample size is present but not set",
345            ));
346        } else if self.header.flags & Self::FLAG_SAMPLE_SIZE == 0 && self.samples.iter().any(|s| s.size.is_some()) {
347            return Err(io::Error::new(
348                io::ErrorKind::InvalidData,
349                "trun sample size is not present but set",
350            ));
351        }
352
353        if self.header.flags & Self::FLAG_SAMPLE_FLAGS != 0 && self.samples.iter().any(|s| s.flags.is_none()) {
354            return Err(io::Error::new(
355                io::ErrorKind::InvalidData,
356                "trun sample flags is present but not set",
357            ));
358        } else if self.header.flags & Self::FLAG_SAMPLE_FLAGS == 0 && self.samples.iter().any(|s| s.flags.is_some()) {
359            return Err(io::Error::new(
360                io::ErrorKind::InvalidData,
361                "trun sample flags is not present but set",
362            ));
363        }
364
365        if self.header.flags & Self::FLAG_SAMPLE_COMPOSITION_TIME_OFFSET != 0
366            && self.samples.iter().any(|s| s.composition_time_offset.is_none())
367        {
368            return Err(io::Error::new(
369                io::ErrorKind::InvalidData,
370                "trun sample composition time offset is present but not set",
371            ));
372        } else if self.header.flags & Self::FLAG_SAMPLE_COMPOSITION_TIME_OFFSET == 0
373            && self.samples.iter().any(|s| s.composition_time_offset.is_some())
374        {
375            return Err(io::Error::new(
376                io::ErrorKind::InvalidData,
377                "trun sample composition time offset is not present but set",
378            ));
379        }
380
381        if self.header.flags & Self::FLAG_SAMPLE_COMPOSITION_TIME_OFFSET != 0 {
382            if self.header.version == 1
383                && self
384                    .samples
385                    .iter()
386                    .any(|s| s.composition_time_offset.unwrap() > i32::MAX as i64)
387            {
388                return Err(io::Error::new(
389                    io::ErrorKind::InvalidData,
390                    "trun sample composition time offset cannot be larger than i32::MAX",
391                ));
392            } else if self.header.version == 0
393                && self
394                    .samples
395                    .iter()
396                    .any(|s| s.composition_time_offset.unwrap() > u32::MAX as i64)
397            {
398                return Err(io::Error::new(
399                    io::ErrorKind::InvalidData,
400                    "trun sample composition time offset cannot be larger than u32::MAX",
401                ));
402            }
403        }
404
405        if let Some(first_sample_flags) = self.first_sample_flags {
406            first_sample_flags.validate()?;
407        }
408
409        for sample in &self.samples {
410            if let Some(flags) = sample.flags {
411                flags.validate()?;
412            }
413        }
414
415        Ok(())
416    }
417}