isobmff/boxes/
track.rs

1//! Track structure boxes defined in ISO/IEC 14496-12 - 8.3
2
3use fixed::traits::ToFixed;
4use fixed::types::extra::{U8, U16};
5use fixed::{FixedI16, FixedU32};
6use scuffle_bytes_util::zero_copy::{Deserialize, DeserializeSeed, Serialize, U24Be};
7
8use super::{Brand, EditBox, MediaBox, MetaBox, UserDataBox};
9use crate::{BoxHeader, IsoBox, IsoSized, UnknownBox};
10
11/// Track box
12///
13/// ISO/IEC 14496-12 - 8.3.1
14#[derive(IsoBox, Debug, PartialEq, Eq)]
15#[iso_box(box_type = b"trak", crate_path = crate)]
16pub struct TrackBox<'a> {
17    /// The contained [`TrackHeaderBox`]. (mandatory)
18    #[iso_box(nested_box)]
19    pub tkhd: TrackHeaderBox,
20    /// The contained [`TrackReferenceBox`]. (optional)
21    #[iso_box(nested_box(collect))]
22    pub tref: Option<TrackReferenceBox<'a>>,
23    /// The contained [`TrackGroupBox`]. (optional)
24    #[iso_box(nested_box(collect))]
25    pub trgr: Option<TrackGroupBox<'a>>,
26    /// The contained [`EditBox`]. (optional)
27    #[iso_box(nested_box(collect))]
28    pub edts: Option<EditBox>,
29    /// The contained [`TrackTypeBox`]. (optional)
30    #[iso_box(nested_box(collect))]
31    pub ttyp: Option<TrackTypeBox>,
32    /// The contained [`MetaBox`]. (optional)
33    #[iso_box(nested_box(collect))]
34    pub meta: Option<MetaBox<'a>>,
35    /// The contained [`MediaBox`]. (mandatory)
36    #[iso_box(nested_box)]
37    pub mdia: MediaBox<'a>,
38    /// A list of unknown boxes that were not recognized during deserialization.
39    #[iso_box(nested_box(collect_unknown))]
40    pub unknown_boxes: Vec<UnknownBox<'a>>,
41    /// The contained [`UserDataBox`]. (optional)
42    #[iso_box(nested_box(collect))]
43    pub udta: Option<UserDataBox<'a>>,
44}
45
46impl<'a> TrackBox<'a> {
47    /// Creates a new [`TrackBox`] with the given `tkhd`, optional `edts`, and `mdia`.
48    pub fn new(tkhd: TrackHeaderBox, edts: Option<EditBox>, mdia: MediaBox<'a>) -> Self {
49        Self {
50            tkhd,
51            tref: None,
52            trgr: None,
53            edts,
54            ttyp: None,
55            meta: None,
56            mdia,
57            unknown_boxes: vec![],
58            udta: None,
59        }
60    }
61}
62
63bitflags::bitflags! {
64    /// Track header box flags
65    #[derive(Debug, PartialEq, Eq, Clone, Copy)]
66    pub struct TrackHeaderBoxFlags: u32 {
67        /// If this flag is set, it indicates that the track is enabled.
68        /// A disabled track (when this flag is not set) is treated as if it were not present.
69        const TrackEnabled = 0x000001;
70        /// If this flag is set, it indicates that the track, or one of its
71        /// alternatives (if any) forms a direct part of the presentation.
72        /// If this flag is unset, it indicates that the track does not represent a direct part of the presentation.
73        const TrackInMovie = 0x000002;
74        /// This flag currently has no assigned meaning, and the
75        /// value should be ignored by readers. In the absence of further guidance (e.g. from derived
76        /// specifications), the same value as for [`Self::TrackInMovie`] should be written.
77        const TrackInPreview = 0x000004;
78        /// If this flag is set, it indicates that the width and
79        /// height fields are not expressed in pixel units. The values have the same units but these units
80        /// are not specified. The values are only an indication of the desired aspect ratio. If the aspect
81        /// ratios of this track and other related tracks are not identical, then the respective positioning of
82        /// the tracks is undefined, possibly defined by external contexts.
83        const TrackSizeIsAspectRatio = 0x000008;
84    }
85}
86
87impl<'a> Deserialize<'a> for TrackHeaderBoxFlags {
88    fn deserialize<R>(reader: R) -> std::io::Result<Self>
89    where
90        R: scuffle_bytes_util::zero_copy::ZeroCopyReader<'a>,
91    {
92        let flags = U24Be::deserialize(reader)?;
93        Ok(Self::from_bits_truncate(*flags))
94    }
95}
96
97impl Serialize for TrackHeaderBoxFlags {
98    fn serialize<W>(&self, writer: W) -> std::io::Result<()>
99    where
100        W: std::io::Write,
101    {
102        U24Be(self.bits()).serialize(writer)
103    }
104}
105
106impl IsoSized for TrackHeaderBoxFlags {
107    fn size(&self) -> usize {
108        3
109    }
110}
111
112/// Track header box
113///
114/// ISO/IEC 14496-12 - 8.3.2
115#[derive(IsoBox, Debug, PartialEq, Eq)]
116#[iso_box(box_type = b"tkhd", skip_impl(deserialize_seed, serialize, sized), crate_path = crate)]
117pub struct TrackHeaderBox {
118    // full header:
119    /// An integer that specifies the version of this box.
120    ///
121    /// Part of full box header.
122    pub version: u8,
123    /// The flags for this box.
124    ///
125    /// Part of full box header.
126    pub flags: TrackHeaderBoxFlags,
127    // body:
128    /// An integer that declares the creation time of this track (in seconds since midnight,
129    /// Jan. 1, 1904, in UTC time).
130    pub creation_time: u64,
131    /// An integer that declares the most recent time the track was modified (in seconds
132    /// since midnight, Jan. 1, 1904, in UTC time).
133    pub modification_time: u64,
134    /// An integer that uniquely identifies this track over the entire life-time of this presentation;
135    /// `track_ID`s are never re-used and cannot be zero.
136    pub track_id: u32,
137    /// Reserved 32 bits, must be set to 0.
138    pub reserved1: u32,
139    /// An integer that indicates the duration of this track (in the timescale indicated in the
140    /// [`MovieHeaderBox`](super::MovieHeaderBox)) This duration field may be indefinite (all 1s) when either there is no edit list
141    /// and the [`MediaHeaderBox`](super::MediaHeaderBox) duration is indefinite (i.e. all 1s), or when an indefinitely repeated edit
142    /// list is desired (see subclause 8.6.6 for repeated edits). If there is no edit list and the duration is
143    /// not indefinite, then the duration shall be equal to the media duration given in the [`MediaHeaderBox`](super::MediaHeaderBox),
144    /// converted into the timescale in the [`MovieHeaderBox`](super::MovieHeaderBox). Otherwise the value of this field is equal to the
145    /// sum of the durations of all of the track’s edits (possibly including repetitions).
146    pub duration: u64,
147    /// Reserved 64 bits, must be set to 0.
148    pub reserved2: u64,
149    /// Specifies the front-to-back ordering of video tracks; tracks with lower numbers are closer to the
150    /// viewer. 0 is the normal value, and -1 would be in front of track 0, and so on
151    pub layer: i16,
152    /// An integer that specifies a group or collection of tracks. If this field is 0 there is
153    /// no information on possible relations to other tracks. If this field is not 0, it should be the same for
154    /// tracks that contain alternate data for one another and different for tracks belonging to different
155    /// such groups. Only one track within an alternate group should be played or streamed at any one
156    /// time, and shall be distinguishable from other tracks in the group via attributes such as bitrate,
157    /// codec, language, packet size etc. A group may have only one member.
158    pub alternate_group: i16,
159    /// Specifies the track's relative audio volume. Full volume is 1.0
160    /// and is the normal value. Its value is irrelevant for a purely visual track. Tracks may be composed
161    /// by combining them according to their volume, and then using the overall [`MovieHeaderBox`](super::MovieHeaderBox) volume
162    /// setting; or more complex audio composition (e.g. MPEG-4 BIFS) may be used.
163    pub volume: FixedI16<U8>,
164    /// Reserved 16 bits, must be set to 0.
165    pub reserved3: u16,
166    /// Provides a transformation matrix for the video; `(u,v,w)` are restricted here to `(0,0,1)`, hex
167    /// `(0,0,0x40000000)`.
168    pub matrix: [i32; 9],
169    /// [`width`](Self::width) and [`height`](Self::height) are track-dependent as follows:
170    ///
171    /// For text and subtitle tracks, they may, depending on the coding format, describe the suggested size of
172    /// the rendering area. For such tracks, the value `0x0` may also be used to indicate that the data may
173    /// be rendered at any size, that no preferred size has been indicated and that the actual size may be
174    /// determined by the external context or by reusing the width and height of another track. For those
175    /// tracks, the flag [`TrackSizeIsAspectRatio`](TrackHeaderBoxFlags::TrackSizeIsAspectRatio) may also be used.
176    ///
177    /// For non-visual tracks (e.g. audio), they should be set to zero.
178    ///
179    /// For all other tracks, they specify the track's visual presentation size. These need not be the same as the
180    /// pixel dimensions of the images, which is documented in the sample description(s); all images in the
181    /// sequence are scaled to this size, before any overall transformation of the track represented by the
182    /// matrix. The pixel dimensions of the images are the default values.
183    pub width: FixedU32<U16>,
184    /// See [`width`](Self::width).
185    pub height: FixedU32<U16>,
186}
187
188impl TrackHeaderBox {
189    /// Creates a new [`TrackHeaderBox`] with the specified parameters.
190    ///
191    /// All other fields are initialized to their default values.
192    pub fn new(
193        creation_time: u64,
194        modification_time: u64,
195        track_id: u32,
196        duration: u64,
197        dimensions: Option<(u32, u32)>,
198    ) -> Self {
199        let version = if creation_time > u32::MAX as u64 || modification_time > u32::MAX as u64 || duration > u32::MAX as u64
200        {
201            1
202        } else {
203            0
204        };
205
206        let (width, height) = dimensions.unwrap_or((0, 0));
207        let volume = if dimensions.is_some() { 0 } else { 1 };
208
209        Self {
210            version,
211            flags: TrackHeaderBoxFlags::TrackEnabled | TrackHeaderBoxFlags::TrackInMovie,
212            creation_time,
213            modification_time,
214            track_id,
215            reserved1: 0,
216            duration,
217            reserved2: 0,
218            layer: 0,
219            alternate_group: 0,
220            volume: volume.to_fixed(),
221            reserved3: 0,
222            matrix: [0x00010000, 0, 0, 0, 0x00010000, 0, 0, 0, 0x40000000],
223            width: width.to_fixed(),
224            height: height.to_fixed(),
225        }
226    }
227}
228
229impl<'a> DeserializeSeed<'a, BoxHeader> for TrackHeaderBox {
230    fn deserialize_seed<R>(mut reader: R, _seed: BoxHeader) -> std::io::Result<Self>
231    where
232        R: scuffle_bytes_util::zero_copy::ZeroCopyReader<'a>,
233    {
234        let version = u8::deserialize(&mut reader)?;
235        let flags = TrackHeaderBoxFlags::deserialize(&mut reader)?;
236
237        let creation_time = if version == 1 {
238            u64::deserialize(&mut reader)?
239        } else {
240            u32::deserialize(&mut reader)? as u64
241        };
242        let modification_time = if version == 1 {
243            u64::deserialize(&mut reader)?
244        } else {
245            u32::deserialize(&mut reader)? as u64
246        };
247        let track_id = u32::deserialize(&mut reader)?;
248        let reserved1 = u32::deserialize(&mut reader)?;
249        let duration = if version == 1 {
250            u64::deserialize(&mut reader)?
251        } else {
252            u32::deserialize(&mut reader)? as u64
253        };
254
255        let reserved2 = u64::deserialize(&mut reader)?;
256
257        let layer = i16::deserialize(&mut reader)?;
258        let alternate_group = i16::deserialize(&mut reader)?;
259        let volume = FixedI16::from_bits(i16::deserialize(&mut reader)?);
260
261        let reserved3 = u16::deserialize(&mut reader)?;
262
263        let mut matrix = [0; 9];
264        for m in &mut matrix {
265            *m = i32::deserialize(&mut reader)?;
266        }
267
268        let width = FixedU32::from_bits(u32::deserialize(&mut reader)?);
269        let height = FixedU32::from_bits(u32::deserialize(&mut reader)?);
270
271        Ok(Self {
272            version,
273            flags,
274            creation_time,
275            modification_time,
276            track_id,
277            reserved1,
278            duration,
279            reserved2,
280            layer,
281            alternate_group,
282            volume,
283            reserved3,
284            matrix,
285            width,
286            height,
287        })
288    }
289}
290
291impl Serialize for TrackHeaderBox {
292    fn serialize<W>(&self, mut writer: W) -> std::io::Result<()>
293    where
294        W: std::io::Write,
295    {
296        self.serialize_box_header(&mut writer)?;
297        self.version.serialize(&mut writer)?;
298        self.flags.serialize(&mut writer)?;
299
300        if self.version == 1 {
301            self.creation_time.serialize(&mut writer)?;
302            self.modification_time.serialize(&mut writer)?;
303        } else {
304            (self.creation_time as u32).serialize(&mut writer)?;
305            (self.modification_time as u32).serialize(&mut writer)?;
306        }
307        self.track_id.serialize(&mut writer)?;
308        self.reserved1.serialize(&mut writer)?;
309        if self.version == 1 {
310            self.duration.serialize(&mut writer)?;
311        } else {
312            (self.duration as u32).serialize(&mut writer)?;
313        }
314
315        self.reserved2.serialize(&mut writer)?;
316
317        self.layer.serialize(&mut writer)?;
318        self.alternate_group.serialize(&mut writer)?;
319        self.volume.to_bits().serialize(&mut writer)?;
320
321        self.reserved3.serialize(&mut writer)?;
322
323        self.matrix.serialize(&mut writer)?;
324
325        self.width.to_bits().serialize(&mut writer)?;
326        self.height.to_bits().serialize(&mut writer)?;
327
328        Ok(())
329    }
330}
331
332impl IsoSized for TrackHeaderBox {
333    fn size(&self) -> usize {
334        let mut size = self.version.size() + self.flags.size();
335        if self.version == 1 {
336            size += 8 + 8; // creation_time, modification_time
337        } else {
338            size += 4 + 4; // creation_time, modification_time
339        }
340        size += 4; // track_id
341        size += 4; // reserved1
342        if self.version == 1 {
343            size += 8; // duration
344        } else {
345            size += 4; // duration
346        }
347        size += 8; // reserved2
348        size += 2; // layer
349        size += 2; // alternate_group
350        size += 2; // volume
351        size += 2; // reserved3
352        size += self.matrix.size(); // matrix
353        size += 4; // width
354        size += 4; // height
355
356        Self::add_header_size(size)
357    }
358}
359
360/// Track reference box
361///
362/// ISO/IEC 14496-12 - 8.3.3
363#[derive(IsoBox, Debug, PartialEq, Eq)]
364#[iso_box(box_type = b"tref", crate_path = crate)]
365pub struct TrackReferenceBox<'a> {
366    /// Any unknown `TrackReferenceTypeBox`es that are contained in this box.
367    #[iso_box(nested_box(collect_unknown))]
368    pub track_reference_type: Vec<UnknownBox<'a>>,
369}
370
371/// Track group box
372///
373/// ISO/IEC 14496-12 - 8.3.4
374#[derive(IsoBox, Debug, PartialEq, Eq)]
375#[iso_box(box_type = b"trgr", crate_path = crate)]
376pub struct TrackGroupBox<'a> {
377    /// Any unknown `TrackGroupTypeBox`es that are contained in this box.
378    #[iso_box(nested_box(collect_unknown))]
379    pub track_group_type: Vec<UnknownBox<'a>>,
380}
381
382/// Track type box
383///
384/// ISO/IEC 14496-12 - 8.3.5
385#[derive(IsoBox, Debug, PartialEq, Eq)]
386#[iso_box(box_type = b"ttyp", crate_path = crate)]
387pub struct TrackTypeBox {
388    /// The "best use" brand of the file which will provide the greatest compatibility.
389    #[iso_box(from = "[u8; 4]")]
390    pub major_brand: Brand,
391    /// Minor version of the major brand.
392    pub minor_version: u32,
393    /// A list of compatible brands.
394    #[iso_box(repeated, from = "[u8; 4]")]
395    pub compatible_brands: Vec<Brand>,
396}