isobmff/boxes/
track_time.rs

1use std::fmt::Debug;
2use std::io;
3
4use fixed::FixedI32;
5use fixed::types::extra::U16;
6use scuffle_bytes_util::zero_copy::{Deserialize, DeserializeSeed, Serialize, ZeroCopyReader};
7
8use crate::{BoxHeader, FullBoxHeader, IsoBox, IsoSized};
9
10/// Time to sample box
11///
12/// ISO/IEC 14496-12 - 8.6.1.2
13#[derive(IsoBox, Debug, PartialEq, Eq, Default)]
14#[iso_box(box_type = b"stts", crate_path = crate)]
15pub struct TimeToSampleBox {
16    /// The full box header.
17    pub full_header: FullBoxHeader,
18    /// An integer that gives the number of entries in the [`entries`](Self::entries) vec.
19    pub entry_count: u32,
20    /// `sample_count` and `sample_delta`.
21    #[iso_box(repeated)]
22    pub entries: Vec<TimeToSampleBoxEntry>,
23}
24
25/// Entry in the [`TimeToSampleBox`].
26#[derive(Debug, PartialEq, Eq)]
27pub struct TimeToSampleBoxEntry {
28    /// An integer that counts the number of consecutive samples that have the given duration.
29    pub sample_count: u32,
30    /// An integer that gives the difference between the decoding timestamp of the next
31    /// sample and this one, in the time-scale of the media.
32    pub sample_delta: u32,
33}
34
35impl<'a> Deserialize<'a> for TimeToSampleBoxEntry {
36    fn deserialize<R>(mut reader: R) -> io::Result<Self>
37    where
38        R: ZeroCopyReader<'a>,
39    {
40        Ok(Self {
41            sample_count: u32::deserialize(&mut reader)?,
42            sample_delta: u32::deserialize(&mut reader)?,
43        })
44    }
45}
46
47impl Serialize for TimeToSampleBoxEntry {
48    fn serialize<W>(&self, mut writer: W) -> io::Result<()>
49    where
50        W: std::io::Write,
51    {
52        self.sample_count.serialize(&mut writer)?;
53        self.sample_delta.serialize(&mut writer)?;
54        Ok(())
55    }
56}
57
58impl IsoSized for TimeToSampleBoxEntry {
59    fn size(&self) -> usize {
60        self.sample_count.size() + self.sample_delta.size()
61    }
62}
63
64/// Composition time to sample box
65///
66/// ISO/IEC 14496-12 - 8.6.1.3
67#[derive(IsoBox, PartialEq, Eq)]
68#[iso_box(box_type = b"ctts", skip_impl(deserialize_seed), crate_path = crate)]
69pub struct CompositionOffsetBox {
70    /// The full box header.
71    pub full_header: FullBoxHeader,
72    /// An integer that gives the number of entries in the [`entries`](Self::entries) vec.
73    pub entry_count: u32,
74    /// `sample_count` and `sample_offset`.
75    #[iso_box(repeated)]
76    pub entries: Vec<CompositionOffsetBoxEntry>,
77}
78
79impl<'a> DeserializeSeed<'a, BoxHeader> for CompositionOffsetBox {
80    fn deserialize_seed<R>(mut reader: R, _seed: BoxHeader) -> io::Result<Self>
81    where
82        R: ZeroCopyReader<'a>,
83    {
84        let full_header = FullBoxHeader::deserialize(&mut reader)?;
85        let entry_count = u32::deserialize(&mut reader)?;
86
87        let mut entries = Vec::new();
88        if full_header.version == 0 || full_header.version == 1 {
89            for _ in 0..entry_count {
90                entries.push(CompositionOffsetBoxEntry::deserialize_seed(&mut reader, full_header.version)?);
91            }
92        }
93
94        Ok(Self {
95            full_header,
96            entry_count,
97            entries,
98        })
99    }
100}
101
102impl Debug for CompositionOffsetBox {
103    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
104        f.debug_struct("CompositionOffsetBox")
105            .field("full_header", &self.full_header)
106            .field("entry_count", &self.entry_count)
107            .field("entries.len", &self.entries.len())
108            .finish()
109    }
110}
111
112/// Entry in the [`CompositionOffsetBox`].
113#[derive(Debug, PartialEq, Eq)]
114pub struct CompositionOffsetBoxEntry {
115    /// An integer that counts the number of consecutive samples that have the given offset.
116    pub sample_count: u32,
117    /// An integer that gives the offset between CT and DT, such that `CT[n] = DT[n] + sample_offset[n]`.
118    pub sample_offset: i64,
119}
120
121impl<'a> DeserializeSeed<'a, u8> for CompositionOffsetBoxEntry {
122    fn deserialize_seed<R>(mut reader: R, seed: u8) -> io::Result<Self>
123    where
124        R: ZeroCopyReader<'a>,
125    {
126        Ok(Self {
127            sample_count: u32::deserialize(&mut reader)?,
128            sample_offset: if seed == 0 {
129                u32::deserialize(&mut reader)? as i64
130            } else if seed == 1 {
131                i32::deserialize(&mut reader)? as i64
132            } else {
133                return Err(io::Error::new(
134                    io::ErrorKind::InvalidData,
135                    "cannot be called with version > 1",
136                ));
137            },
138        })
139    }
140}
141
142impl Serialize for CompositionOffsetBoxEntry {
143    fn serialize<W>(&self, mut writer: W) -> io::Result<()>
144    where
145        W: std::io::Write,
146    {
147        self.sample_count.serialize(&mut writer)?;
148        self.sample_offset.serialize(&mut writer)?;
149        Ok(())
150    }
151}
152
153impl IsoSized for CompositionOffsetBoxEntry {
154    fn size(&self) -> usize {
155        self.sample_count.size() + self.sample_offset.size()
156    }
157}
158
159/// Composition to decode box
160///
161/// ISO/IEC 14496-12 - 8.6.1.4
162#[derive(IsoBox, Debug, PartialEq, Eq)]
163#[iso_box(box_type = b"cslg", skip_impl(deserialize_seed, serialize, sized), crate_path = crate)]
164pub struct CompositionToDecodeBox {
165    /// The full box header.
166    pub full_header: FullBoxHeader,
167    /// If this value is added to the composition timestamps (as calculated by the CTS
168    /// offsets from the DTS), then for all samples, their CTS is guaranteed to be greater than or equal
169    /// to their DTS, and the buffer model implied by the indicated profile/level will be honoured; if
170    /// `leastDecodeToDisplayDelta` is positive or zero, this field can be 0; otherwise it should be at least
171    /// (`-leastDecodeToDisplayDelta`)
172    pub composition_to_dt_shift: i64,
173    /// The smallest composition offset in the CompositionOffsetBox in this track.
174    pub least_decode_to_display_delta: i64,
175    /// The largest composition offset in the CompositionOffsetBox in this track.
176    pub greatest_decode_to_display_delta: i64,
177    /// The smallest computed composition timestamp (CTS) for any sample in the media of this track.
178    pub composition_start_time: i64,
179    /// The composition timestamp plus the composition duration, of the sample with the
180    /// largest computed composition timestamp (CTS) in the media of this track; if this field takes the
181    /// value 0, the composition end time is unknown.
182    pub composition_end_time: i64,
183}
184
185impl<'a> DeserializeSeed<'a, BoxHeader> for CompositionToDecodeBox {
186    fn deserialize_seed<R>(mut reader: R, _seed: BoxHeader) -> io::Result<Self>
187    where
188        R: ZeroCopyReader<'a>,
189    {
190        let full_header = FullBoxHeader::deserialize(&mut reader)?;
191
192        let composition_to_dt_shift = if full_header.version == 0 {
193            i32::deserialize(&mut reader)? as i64
194        } else {
195            i64::deserialize(&mut reader)?
196        };
197        let least_decode_to_display_delta = if full_header.version == 0 {
198            i32::deserialize(&mut reader)? as i64
199        } else {
200            i64::deserialize(&mut reader)?
201        };
202        let greatest_decode_to_display_delta = if full_header.version == 0 {
203            i32::deserialize(&mut reader)? as i64
204        } else {
205            i64::deserialize(&mut reader)?
206        };
207        let composition_start_time = if full_header.version == 0 {
208            i32::deserialize(&mut reader)? as i64
209        } else {
210            i64::deserialize(&mut reader)?
211        };
212        let composition_end_time = if full_header.version == 0 {
213            i32::deserialize(&mut reader)? as i64
214        } else {
215            i64::deserialize(&mut reader)?
216        };
217
218        Ok(Self {
219            full_header,
220            composition_to_dt_shift,
221            least_decode_to_display_delta,
222            greatest_decode_to_display_delta,
223            composition_start_time,
224            composition_end_time,
225        })
226    }
227}
228
229impl Serialize for CompositionToDecodeBox {
230    fn serialize<W>(&self, mut writer: W) -> io::Result<()>
231    where
232        W: std::io::Write,
233    {
234        self.serialize_box_header(&mut writer)?;
235        self.full_header.serialize(&mut writer)?;
236        if self.full_header.version == 0 {
237            (self.composition_to_dt_shift as i32).serialize(&mut writer)?;
238            (self.least_decode_to_display_delta as i32).serialize(&mut writer)?;
239            (self.greatest_decode_to_display_delta as i32).serialize(&mut writer)?;
240            (self.composition_start_time as i32).serialize(&mut writer)?;
241            (self.composition_end_time as i32).serialize(&mut writer)?;
242        } else {
243            self.composition_to_dt_shift.serialize(&mut writer)?;
244            self.least_decode_to_display_delta.serialize(&mut writer)?;
245            self.greatest_decode_to_display_delta.serialize(&mut writer)?;
246            self.composition_start_time.serialize(&mut writer)?;
247            self.composition_end_time.serialize(&mut writer)?;
248        }
249        Ok(())
250    }
251}
252
253impl IsoSized for CompositionToDecodeBox {
254    fn size(&self) -> usize {
255        let mut size = self.full_header.size();
256        if self.full_header.version == 0 {
257            size += 4 + 4 + 4 + 4 + 4;
258        } else {
259            size += 8 + 8 + 8 + 8 + 8;
260        }
261
262        Self::add_header_size(size)
263    }
264}
265
266/// Sync sample box
267///
268/// ISO/IEC 14496-12 - 8.6.2
269#[derive(IsoBox, Debug, PartialEq, Eq)]
270#[iso_box(box_type = b"stss", crate_path = crate)]
271pub struct SyncSampleBox {
272    /// The full box header.
273    pub full_header: FullBoxHeader,
274    /// An integer that gives the number of entries in the [`sample_number`](Self::sample_number) vec.
275    /// If `entry_count` is zero, there are no sync samples within the stream and the
276    /// [`sample_number`](Self::sample_number) vec is empty.
277    pub entry_count: u32,
278    /// Gives, for each sync sample in the stream, its sample number.
279    #[iso_box(repeated)]
280    pub sample_number: Vec<u32>,
281}
282
283/// Shadow sync sample box
284///
285/// ISO/IEC 14496-12 - 8.6.3.2
286#[derive(IsoBox, Debug, PartialEq, Eq)]
287#[iso_box(box_type = b"stsh", crate_path = crate)]
288pub struct ShadowSyncSampleBox {
289    /// The full box header.
290    pub full_header: FullBoxHeader,
291    /// An integer that gives the number of entries in the [`entries`](Self::entries) vec.
292    pub entry_count: u32,
293    /// `shadowed_sample_number` and `sync_sample_number`.
294    #[iso_box(repeated)]
295    pub entries: Vec<ShadowSyncSampleBoxEntry>,
296}
297
298/// Entry in the [`ShadowSyncSampleBox`].
299#[derive(Debug, PartialEq, Eq)]
300pub struct ShadowSyncSampleBoxEntry {
301    /// Gives the number of a sample for which there is an alternative sync sample.
302    pub shadowed_sample_number: u32,
303    /// Gives the number of the alternative sync sample.
304    pub sync_sample_number: u32,
305}
306
307impl<'a> Deserialize<'a> for ShadowSyncSampleBoxEntry {
308    fn deserialize<R>(mut reader: R) -> io::Result<Self>
309    where
310        R: ZeroCopyReader<'a>,
311    {
312        Ok(Self {
313            shadowed_sample_number: u32::deserialize(&mut reader)?,
314            sync_sample_number: u32::deserialize(&mut reader)?,
315        })
316    }
317}
318
319impl Serialize for ShadowSyncSampleBoxEntry {
320    fn serialize<W>(&self, mut writer: W) -> io::Result<()>
321    where
322        W: std::io::Write,
323    {
324        self.shadowed_sample_number.serialize(&mut writer)?;
325        self.sync_sample_number.serialize(&mut writer)?;
326        Ok(())
327    }
328}
329
330impl IsoSized for ShadowSyncSampleBoxEntry {
331    fn size(&self) -> usize {
332        self.shadowed_sample_number.size() + self.sync_sample_number.size()
333    }
334}
335
336/// Independent and disposable samples box
337///
338/// ISO/IEC 14496-12 - 8.6.4
339#[derive(IsoBox, Debug, PartialEq, Eq)]
340#[iso_box(box_type = b"sdtp", crate_path = crate)]
341pub struct SampleDependencyTypeBox {
342    /// The full box header.
343    pub full_header: FullBoxHeader,
344    /// `is_leading`, `sample_depends_on`, `sample_is_depended_on`, and `sample_has_redundancy`.
345    #[iso_box(from = "u8", repeated)]
346    pub entries: Vec<SampleDependencyTypeBoxEntry>,
347}
348
349/// Entry in the [`SampleDependencyTypeBox`].
350#[derive(Debug, PartialEq, Eq, Clone, Copy)]
351pub struct SampleDependencyTypeBoxEntry {
352    /// - `0`: The leading nature of this sample is unknown;
353    /// - `1`: This sample is a leading sample that has a dependency before the referenced I-picture (and is
354    ///   therefore not decodable);
355    /// - `2`: This sample is not a leading sample;
356    /// - `3`: This sample is a leading sample that has no dependency before the referenced I-picture (and is
357    ///   therefore decodable);
358    pub is_leading: u8,
359    /// - `0`: The dependency of this sample is unknown;
360    /// - `1`: This sample does depend on others (not an I picture);
361    /// - `2`: This sample does not depend on others (I picture);
362    /// - `3`: Reserved;
363    pub sample_depends_on: u8,
364    /// - `0`: The dependency of other samples on this sample is unknown;
365    /// - `1`: Other samples may depend on this one (not disposable);
366    /// - `2`: No other sample depends on this one (disposable);
367    /// - `3`: Reserved;
368    pub sample_is_depended_on: u8,
369    /// - `0`: It is unknown whether there is redundant coding in this sample;
370    /// - `1`: There is redundant coding in this sample;
371    /// - `2`: There is no redundant coding in this sample;
372    /// - `3`: Reserved;
373    pub sample_has_redundancy: u8,
374}
375
376impl From<u8> for SampleDependencyTypeBoxEntry {
377    fn from(value: u8) -> Self {
378        Self {
379            is_leading: (value >> 6) & 0b11,
380            sample_depends_on: (value >> 4) & 0b11,
381            sample_is_depended_on: (value >> 2) & 0b11,
382            sample_has_redundancy: value & 0b11,
383        }
384    }
385}
386
387impl From<SampleDependencyTypeBoxEntry> for u8 {
388    fn from(val: SampleDependencyTypeBoxEntry) -> Self {
389        ((val.is_leading & 0b11) << 6)
390            | ((val.sample_depends_on & 0b11) << 4)
391            | ((val.sample_is_depended_on & 0b11) << 2)
392            | (val.sample_has_redundancy & 0b11)
393    }
394}
395
396impl IsoSized for SampleDependencyTypeBoxEntry {
397    fn size(&self) -> usize {
398        1
399    }
400}
401
402/// Edit box
403///
404/// ISO/IEC 14496-12 - 8.6.5
405#[derive(IsoBox, Debug, PartialEq, Eq)]
406#[iso_box(box_type = b"edts", crate_path = crate)]
407pub struct EditBox {
408    /// The contained [`EditListBox`]. (optional)
409    #[iso_box(nested_box(collect))]
410    pub elst: Option<EditListBox>,
411}
412
413/// Edit list box
414///
415/// ISO/IEC 14496-12 - 8.6.6
416#[derive(IsoBox, Debug, PartialEq, Eq)]
417#[iso_box(box_type = b"elst", skip_impl(deserialize_seed, serialize, sized), crate_path = crate)]
418pub struct EditListBox {
419    /// The full box header.
420    pub full_header: FullBoxHeader,
421    /// An integer that gives the number of entries in the [`entries`](Self::entries) vec.
422    pub entry_count: u32,
423    /// `edit_duration`, `media_time`, and `media_rate`.
424    #[iso_box(repeated)]
425    pub entries: Vec<EditListBoxEntry>,
426}
427
428impl<'a> DeserializeSeed<'a, BoxHeader> for EditListBox {
429    fn deserialize_seed<R>(mut reader: R, _seed: BoxHeader) -> io::Result<Self>
430    where
431        R: ZeroCopyReader<'a>,
432    {
433        let full_header = FullBoxHeader::deserialize(&mut reader)?;
434
435        let entry_count = u32::deserialize(&mut reader)?;
436
437        let mut entries = Vec::with_capacity(entry_count as usize);
438        for _ in 0..entry_count {
439            entries.push(EditListBoxEntry::deserialize_seed(&mut reader, full_header.version)?);
440        }
441
442        Ok(Self {
443            full_header,
444            entry_count,
445            entries,
446        })
447    }
448}
449
450impl Serialize for EditListBox {
451    fn serialize<W>(&self, mut writer: W) -> io::Result<()>
452    where
453        W: std::io::Write,
454    {
455        self.serialize_box_header(&mut writer)?;
456        self.full_header.serialize(&mut writer)?;
457        self.entry_count.serialize(&mut writer)?;
458
459        for entry in &self.entries {
460            entry.serialize(&mut writer, self.full_header.version)?;
461        }
462
463        Ok(())
464    }
465}
466
467impl IsoSized for EditListBox {
468    fn size(&self) -> usize {
469        let mut size = 0;
470        size += self.full_header.size();
471        size += self.entry_count.size();
472        size += self
473            .entries
474            .iter()
475            .map(|entry| entry.size(self.full_header.version))
476            .sum::<usize>();
477
478        Self::add_header_size(size)
479    }
480}
481
482/// Entry in the [`EditListBox`].
483#[derive(Debug, PartialEq, Eq)]
484pub struct EditListBoxEntry {
485    /// An integer that specifies the duration of this edit in units of the timescale in the
486    /// [`MovieHeaderBox`](super::MovieHeaderBox).
487    pub edit_duration: u64,
488    /// An integer containing the starting time within the media of this edit entry (in media time
489    /// scale units, in composition time). If this field is set to –1, it is an empty edit. The last edit in a track
490    /// shall never be an empty edit. Any difference between the duration in the [`MovieHeaderBox`](super::MovieHeaderBox),
491    /// and the track’s duration is expressed as an implicit empty edit at the end.
492    pub media_time: i64,
493    /// Specifies the relative rate at which to play the media corresponding to this edit entry. If
494    /// this value is 0, then the edit is specifying a 'dwell': the media at media-time is presented for the
495    /// edit_duration. The normal value, indicating normal-speed forward play, is 1.0.
496    pub media_rate: FixedI32<U16>,
497}
498
499impl<'a> DeserializeSeed<'a, u8> for EditListBoxEntry {
500    fn deserialize_seed<R>(mut reader: R, seed: u8) -> io::Result<Self>
501    where
502        R: ZeroCopyReader<'a>,
503    {
504        let edit_duration = if seed == 1 {
505            u64::deserialize(&mut reader)?
506        } else {
507            u32::deserialize(&mut reader)? as u64
508        };
509        let media_time = if seed == 1 {
510            i64::deserialize(&mut reader)?
511        } else {
512            i32::deserialize(&mut reader)? as i64
513        };
514        let media_rate = FixedI32::from_bits(i32::deserialize(&mut reader)?);
515
516        Ok(Self {
517            edit_duration,
518            media_time,
519            media_rate,
520        })
521    }
522}
523
524impl EditListBoxEntry {
525    fn serialize<W>(&self, mut writer: W, version: u8) -> io::Result<()>
526    where
527        W: std::io::Write,
528    {
529        if version == 1 {
530            self.edit_duration.serialize(&mut writer)?;
531            self.media_time.serialize(&mut writer)?;
532        } else {
533            (self.edit_duration as u32).serialize(&mut writer)?;
534            (self.media_time as i32).serialize(&mut writer)?;
535        }
536        self.media_rate.to_bits().serialize(&mut writer)?;
537
538        Ok(())
539    }
540}
541
542impl EditListBoxEntry {
543    /// Returns the size of this entry in bytes, depending on the version.
544    pub fn size(&self, version: u8) -> usize {
545        let mut size = 0;
546        if version == 1 {
547            size += 8 + 8;
548        } else {
549            size += 4 + 4;
550        }
551        size + 2 + 2
552    }
553}