mp4_atom/mfra/
tfra.rs

1use crate::*;
2
3ext! {
4    name: Tfra,
5    versions: [0, 1],
6    flags: {}
7}
8
9#[derive(Debug, Clone, PartialEq, Eq, Default)]
10#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
11pub struct FragmentInfo {
12    pub time: u64,
13    pub moof_offset: u64,
14    pub traf_number: u32,
15    pub trun_number: u32,
16    pub sample_delta: u32,
17}
18
19#[derive(Debug, Clone, PartialEq, Eq, Default)]
20#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
21pub struct Tfra {
22    pub track_id: u32,
23    pub entries: Vec<FragmentInfo>,
24}
25impl Tfra {
26    fn determine_required_lengths(&self) -> (u32, u32, u32, TfraVersion) {
27        let mut length_size_of_traf_num = 0;
28        let mut length_size_of_trun_num = 0;
29        let mut length_size_of_sample_num = 0;
30        let mut version = TfraVersion::V0;
31        for entry in &self.entries {
32            if entry.time > u32::MAX.into() || entry.moof_offset > u32::MAX.into() {
33                version = TfraVersion::V1;
34            }
35            length_size_of_traf_num = std::cmp::max(
36                length_size_of_traf_num,
37                determine_required_length(entry.traf_number),
38            );
39            length_size_of_trun_num = std::cmp::max(
40                length_size_of_trun_num,
41                determine_required_length(entry.trun_number),
42            );
43            length_size_of_sample_num = std::cmp::max(
44                length_size_of_sample_num,
45                determine_required_length(entry.sample_delta),
46            );
47        }
48        (
49            length_size_of_traf_num,
50            length_size_of_trun_num,
51            length_size_of_sample_num,
52            version,
53        )
54    }
55}
56
57fn determine_required_length(value: u32) -> u32 {
58    // number of bytes required minus 1
59    if value > u24::MAX {
60        3
61    } else if value > u16::MAX.into() {
62        2
63    } else if value > u8::MAX.into() {
64        1
65    } else {
66        0
67    }
68}
69
70fn decode_variable_unsigned_int<B: Buf>(buf: &mut B, num_bits_minus_one: u32) -> Result<u32> {
71    match num_bits_minus_one {
72        0 => Ok(u8::decode(buf)? as u32),
73        1 => Ok(u16::decode(buf)? as u32),
74        2 => Ok(u24::decode(buf)?.into()),
75        3 => u32::decode(buf),
76        _ => Err(Error::InvalidSize),
77    }
78}
79
80fn encode_variable_unsigned_int<B: BufMut>(
81    buf: &mut B,
82    value: u32,
83    num_bits_minus_one: u32,
84) -> Result<()> {
85    match num_bits_minus_one {
86        0 => (value as u8).encode(buf),
87        1 => (value as u16).encode(buf),
88        2 => {
89            let v: u24 = value.try_into().expect("should have already been checked");
90            v.encode(buf)
91        }
92        3 => value.encode(buf),
93        _ => Err(Error::InvalidSize),
94    }
95}
96
97impl AtomExt for Tfra {
98    const KIND_EXT: FourCC = FourCC::new(b"tfra");
99
100    type Ext = TfraExt;
101
102    fn decode_body_ext<B: Buf>(buf: &mut B, ext: TfraExt) -> Result<Self> {
103        let track_id = u32::decode(buf)?;
104        let lengths = u32::decode(buf)?;
105        // high 26 bits are reserved, low 6 bits indicate length used later
106        let length_size_of_sample_num = lengths & 0b11;
107        let length_size_of_trun_num = (lengths >> 2) & 0b11;
108        let length_size_of_traf_num = (lengths >> 4) & 0b11;
109        let number_of_entry = u32::decode(buf)?;
110        // Don't trust the entry count, just start with a small-ish reservation
111        let mut entries = Vec::with_capacity(std::cmp::min(128, number_of_entry as usize));
112        for _ in 0..number_of_entry {
113            let (time, moof_offset) = match ext.version {
114                TfraVersion::V1 => (u64::decode(buf)?, u64::decode(buf)?),
115                TfraVersion::V0 => (u32::decode(buf)?.into(), u32::decode(buf)?.into()),
116            };
117            let fragment_info = FragmentInfo {
118                time,
119                moof_offset,
120                traf_number: decode_variable_unsigned_int(buf, length_size_of_traf_num)?,
121                trun_number: decode_variable_unsigned_int(buf, length_size_of_trun_num)?,
122                sample_delta: decode_variable_unsigned_int(buf, length_size_of_sample_num)?,
123            };
124            entries.push(fragment_info);
125        }
126        Ok(Tfra { track_id, entries })
127    }
128
129    fn encode_body_ext<B: BufMut>(&self, buf: &mut B) -> Result<TfraExt> {
130        self.track_id.encode(buf)?;
131        let (length_size_of_traf_num, length_size_of_trun_num, length_size_of_sample_num, version) =
132            self.determine_required_lengths();
133        ((length_size_of_traf_num << 4)
134            | (length_size_of_trun_num << 2)
135            | (length_size_of_sample_num))
136            .encode(buf)?;
137        let number_of_entry: u32 = self
138            .entries
139            .len()
140            .try_into()
141            .map_err(|_| Error::TooLarge(Self::KIND))?;
142        number_of_entry.encode(buf)?;
143        for entry in &self.entries {
144            match version {
145                TfraVersion::V1 => {
146                    entry.time.encode(buf)?;
147                    entry.moof_offset.encode(buf)?
148                }
149                TfraVersion::V0 => {
150                    (entry.time as u32).encode(buf)?;
151                    (entry.moof_offset as u32).encode(buf)?;
152                }
153            }
154            encode_variable_unsigned_int(buf, entry.traf_number, length_size_of_traf_num)?;
155            encode_variable_unsigned_int(buf, entry.trun_number, length_size_of_trun_num)?;
156            encode_variable_unsigned_int(buf, entry.sample_delta, length_size_of_sample_num)?;
157        }
158        Ok(version.into())
159    }
160}
161
162#[cfg(test)]
163mod tests {
164
165    use super::*;
166
167    // We only test V1 here, V0 gets checked in the parent mod test
168
169    // From MPEG File Format Conformance suite: uvvu/Solekai007_1920_29_1x1_v7clear.uvu
170    // with a change to make it actually require V1
171    const ENCODED_TFRA: &[u8] = &[
172        0x00, 0x00, 0x00, 0x2c, 0x74, 0x66, 0x72, 0x61, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
173        0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
174        0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x09, 0x68, 0x45, 0x01, 0x02, 0xFF, 0xFF,
175    ];
176
177    #[test]
178    fn test_tfra_v1_decode() {
179        let buf: &mut std::io::Cursor<&&[u8]> = &mut std::io::Cursor::new(&ENCODED_TFRA);
180        let tfra = Tfra::decode(buf).expect("failed to decode tfra");
181        assert_eq!(
182            tfra,
183            Tfra {
184                track_id: 3,
185                entries: vec![FragmentInfo {
186                    time: 0,
187                    moof_offset: 1099512244293,
188                    traf_number: 1,
189                    trun_number: 2,
190                    sample_delta: 65535
191                }]
192            }
193        );
194    }
195
196    #[test]
197    fn test_tfra_v1_encode() {
198        let tfra = Tfra {
199            track_id: 3,
200            entries: vec![FragmentInfo {
201                time: 0,
202                moof_offset: 1099512244293,
203                traf_number: 1,
204                trun_number: 2,
205                sample_delta: 65535,
206            }],
207        };
208
209        let mut buf = Vec::new();
210        tfra.encode(&mut buf).unwrap();
211        assert_eq!(buf, ENCODED_TFRA);
212    }
213}