scuffle_mp4/boxes/types/
traf.rs

1use std::io;
2
3use bytes::{Buf, Bytes};
4
5use super::sbgp::Sbgp;
6use super::subs::Subs;
7use super::tfdt::Tfdt;
8use super::tfhd::Tfhd;
9use super::trun::Trun;
10use crate::boxes::DynBox;
11use crate::boxes::header::BoxHeader;
12use crate::boxes::traits::BoxType;
13
14#[derive(Debug, Clone, PartialEq)]
15/// Track Fragment Box
16/// ISO/IEC 14496-12:2022(E) - 8.8.6
17pub struct Traf {
18    pub header: BoxHeader,
19    pub tfhd: Tfhd,
20    pub trun: Option<Trun>,
21    pub sbgp: Option<Sbgp>,
22    pub subs: Option<Subs>,
23    pub tfdt: Option<Tfdt>,
24    pub unknown: Vec<DynBox>,
25}
26
27impl Traf {
28    pub fn new(tfhd: Tfhd, trun: Option<Trun>, tfdt: Option<Tfdt>) -> Self {
29        Self {
30            header: BoxHeader::new(Self::NAME),
31            tfhd,
32            trun,
33            sbgp: None,
34            subs: None,
35            tfdt,
36            unknown: Vec::new(),
37        }
38    }
39
40    /// This function will try to optimize the trun samples by using default
41    /// values from the tfhd box.
42    pub fn optimize(&mut self) {
43        let Some(trun) = &mut self.trun else {
44            return;
45        };
46
47        if trun.samples.is_empty() {
48            return;
49        }
50
51        let tfhd = &mut self.tfhd;
52
53        if tfhd.default_sample_flags.is_none()
54            && trun.samples.len() > 1
55            && trun.samples.iter().skip(2).all(|s| s.flags == trun.samples[1].flags)
56        {
57            tfhd.default_sample_flags = trun.samples[1].flags;
58
59            let first_sample = trun.samples.first().unwrap();
60            if first_sample.flags != tfhd.default_sample_flags {
61                trun.first_sample_flags = first_sample.flags;
62            }
63
64            trun.samples.iter_mut().for_each(|s| s.flags = None);
65        }
66
67        if trun.samples.iter().all(|s| s.composition_time_offset == Some(0)) {
68            trun.samples.iter_mut().for_each(|s| s.composition_time_offset = None);
69        }
70
71        if tfhd.default_sample_duration.is_none()
72            && trun.samples.iter().skip(1).all(|s| s.duration == trun.samples[0].duration)
73        {
74            tfhd.default_sample_duration = trun.samples[0].duration;
75            trun.samples.iter_mut().for_each(|s| s.duration = None);
76        }
77
78        if tfhd.default_sample_size.is_none() && trun.samples.iter().skip(1).all(|s| s.size == trun.samples[0].size) {
79            tfhd.default_sample_size = trun.samples[0].size;
80            trun.samples.iter_mut().for_each(|s| s.size = None);
81        }
82
83        trun.header.flags = if trun.data_offset.is_some() {
84            Trun::FLAG_DATA_OFFSET
85        } else {
86            0
87        } | if trun.first_sample_flags.is_some() {
88            Trun::FLAG_FIRST_SAMPLE_FLAGS
89        } else {
90            0
91        } | if trun.samples.first().and_then(|s| s.duration).is_some() {
92            Trun::FLAG_SAMPLE_DURATION
93        } else {
94            0
95        } | if trun.samples.first().and_then(|s| s.size).is_some() {
96            Trun::FLAG_SAMPLE_SIZE
97        } else {
98            0
99        } | if trun.samples.first().and_then(|s| s.flags).is_some() {
100            Trun::FLAG_SAMPLE_FLAGS
101        } else {
102            0
103        } | if trun.samples.first().and_then(|s| s.composition_time_offset).is_some() {
104            Trun::FLAG_SAMPLE_COMPOSITION_TIME_OFFSET
105        } else {
106            0
107        };
108
109        tfhd.header.flags = if tfhd.base_data_offset.is_some() {
110            Tfhd::BASE_DATA_OFFSET_FLAG
111        } else {
112            0
113        } | if tfhd.default_sample_duration.is_some() {
114            Tfhd::DEFAULT_SAMPLE_DURATION_FLAG
115        } else {
116            0
117        } | if tfhd.default_sample_flags.is_some() {
118            Tfhd::DEFAULT_SAMPLE_FLAGS_FLAG
119        } else {
120            0
121        } | if tfhd.default_sample_size.is_some() {
122            Tfhd::DEFAULT_SAMPLE_SIZE_FLAG
123        } else {
124            0
125        } | tfhd.header.flags & Tfhd::DEFAULT_BASE_IS_MOOF_FLAG;
126    }
127
128    pub fn duration(&self) -> u32 {
129        let tfhd = &self.tfhd;
130        let trun = &self.trun;
131
132        if let Some(trun) = trun {
133            let mut duration = 0;
134            for sample in &trun.samples {
135                if let Some(d) = sample.duration {
136                    duration += d;
137                } else if let Some(d) = tfhd.default_sample_duration {
138                    duration += d;
139                }
140            }
141
142            return duration;
143        }
144
145        0
146    }
147
148    pub fn contains_keyframe(&self) -> bool {
149        let tfhd = &self.tfhd;
150        let trun = &self.trun;
151
152        if let Some(trun) = trun {
153            if let Some(flags) = trun.first_sample_flags {
154                if flags.sample_depends_on == 2 {
155                    return true;
156                }
157            }
158
159            for sample in &trun.samples {
160                if let Some(flags) = sample.flags {
161                    if flags.sample_depends_on == 2 {
162                        return true;
163                    }
164                } else if let Some(flags) = tfhd.default_sample_flags {
165                    if flags.sample_depends_on == 2 {
166                        return true;
167                    }
168                }
169            }
170        }
171
172        false
173    }
174}
175
176impl BoxType for Traf {
177    const NAME: [u8; 4] = *b"traf";
178
179    fn demux(header: BoxHeader, data: Bytes) -> io::Result<Self> {
180        let mut reader = io::Cursor::new(data);
181
182        let mut tfhd = None;
183        let mut trun = None;
184        let mut sbgp = None;
185        let mut subs = None;
186        let mut tfdt = None;
187
188        let mut unknown = Vec::new();
189
190        while reader.has_remaining() {
191            let box_ = DynBox::demux(&mut reader)?;
192            match box_ {
193                DynBox::Tfhd(b) => {
194                    tfhd = Some(*b);
195                }
196                DynBox::Trun(b) => {
197                    trun = Some(*b);
198                }
199                DynBox::Sbgp(b) => {
200                    sbgp = Some(*b);
201                }
202                DynBox::Subs(b) => {
203                    subs = Some(*b);
204                }
205                DynBox::Tfdt(b) => {
206                    tfdt = Some(*b);
207                }
208                _ => unknown.push(box_),
209            }
210        }
211
212        let tfhd = tfhd.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "traf box must contain tfhd box"))?;
213
214        Ok(Self {
215            header,
216            tfhd,
217            trun,
218            sbgp,
219            subs,
220            tfdt,
221            unknown,
222        })
223    }
224
225    fn primitive_size(&self) -> u64 {
226        self.tfhd.size()
227            + self.trun.as_ref().map(|box_| box_.size()).unwrap_or(0)
228            + self.sbgp.as_ref().map(|box_| box_.size()).unwrap_or(0)
229            + self.subs.as_ref().map(|box_| box_.size()).unwrap_or(0)
230            + self.tfdt.as_ref().map(|box_| box_.size()).unwrap_or(0)
231            + self.unknown.iter().map(|box_| box_.size()).sum::<u64>()
232    }
233
234    fn primitive_mux<T: io::Write>(&self, writer: &mut T) -> io::Result<()> {
235        self.tfhd.mux(writer)?;
236
237        if let Some(box_) = &self.sbgp {
238            box_.mux(writer)?;
239        }
240
241        if let Some(box_) = &self.subs {
242            box_.mux(writer)?;
243        }
244
245        if let Some(box_) = &self.tfdt {
246            box_.mux(writer)?;
247        }
248
249        for box_ in &self.unknown {
250            box_.mux(writer)?;
251        }
252
253        if let Some(box_) = &self.trun {
254            box_.mux(writer)?;
255        }
256
257        Ok(())
258    }
259}