mp4_atom/moof/traf/
trun.rs

1use crate::*;
2
3ext! {
4    name: Trun,
5    versions: [0, 1],
6    flags: {
7        data_offset = 0,
8        first_sample_flags = 2,
9        sample_duration = 8,
10        sample_size = 9,
11        sample_flags = 10,
12        sample_cts = 11,
13    }
14}
15
16#[derive(Debug, Clone, PartialEq, Eq, Default)]
17#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
18pub struct Trun {
19    pub data_offset: Option<i32>,
20    pub entries: Vec<TrunEntry>,
21}
22
23#[derive(Debug, Clone, PartialEq, Eq, Default)]
24#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
25pub struct TrunEntry {
26    pub duration: Option<u32>,
27    pub size: Option<u32>,
28    pub flags: Option<u32>,
29    pub cts: Option<i32>,
30}
31
32impl AtomExt for Trun {
33    const KIND_EXT: FourCC = FourCC::new(b"trun");
34
35    type Ext = TrunExt;
36
37    fn decode_body_ext<B: Buf>(buf: &mut B, ext: TrunExt) -> Result<Self> {
38        let sample_count = u32::decode(buf)?;
39        let data_offset = match ext.data_offset {
40            true => i32::decode(buf)?.into(),
41            false => None,
42        };
43
44        let mut first_sample_flags = match ext.first_sample_flags {
45            true => u32::decode(buf)?.into(),
46            false => None,
47        };
48
49        // Avoid a memory exhaustion attack.
50        // If none of the flags are set, then the trun entry has zero size, then we'll allocate `sample_count` entries.
51        // Rather than make the API worse, we just limit the number of (useless?) identical entries to 4096.
52        if !(ext.sample_duration
53            || ext.sample_size
54            || ext.sample_flags
55            || ext.sample_cts
56            || sample_count <= 4096)
57        {
58            return Err(Error::OutOfMemory);
59        }
60
61        let mut entries = Vec::with_capacity(sample_count.min(4096) as _);
62
63        for _ in 0..sample_count {
64            let duration = match ext.sample_duration {
65                true => u32::decode(buf)?.into(),
66                false => None,
67            };
68            let size = match ext.sample_size {
69                true => u32::decode(buf)?.into(),
70                false => None,
71            };
72            let sample_flags = match first_sample_flags.take() {
73                Some(flags) => Some(flags),
74                None => match ext.sample_flags {
75                    true => u32::decode(buf)?.into(),
76                    false => None,
77                },
78            };
79            let cts = match ext.sample_cts {
80                true => i32::decode(buf)?.into(),
81                false => None,
82            };
83
84            entries.push(TrunEntry {
85                duration,
86                size,
87                flags: sample_flags,
88                cts,
89            });
90        }
91
92        Ok(Trun {
93            data_offset,
94            entries,
95        })
96    }
97
98    fn encode_body_ext<B: BufMut>(&self, buf: &mut B) -> Result<TrunExt> {
99        let ext = TrunExt {
100            version: TrunVersion::V1,
101            data_offset: self.data_offset.is_some(),
102            first_sample_flags: false,
103
104            // TODO error if these are not all the same
105            sample_duration: self.entries.iter().all(|s| s.duration.is_some()),
106            sample_size: self.entries.iter().all(|s| s.size.is_some()),
107            sample_flags: self.entries.iter().all(|s| s.flags.is_some()),
108            sample_cts: self.entries.iter().all(|s| s.cts.is_some()),
109        };
110
111        (self.entries.len() as u32).encode(buf)?;
112
113        self.data_offset.encode(buf)?;
114        if ext.first_sample_flags {
115            0u32.encode(buf)?; // TODO first sample flags
116        }
117
118        for entry in &self.entries {
119            ext.sample_duration.then_some(entry.duration).encode(buf)?;
120            ext.sample_size.then_some(entry.size).encode(buf)?;
121            ext.sample_flags.then_some(entry.flags).encode(buf)?;
122            ext.sample_cts.then_some(entry.cts).encode(buf)?;
123        }
124
125        Ok(ext)
126    }
127}