isobmff/boxes/
track_media.rs

1//! Track media structure boxes defined in ISO/IEC 14496-12 - 8.4
2
3use nutype_enum::nutype_enum;
4use scuffle_bytes_util::zero_copy::{Deserialize, DeserializeSeed, Serialize, U24Be};
5
6use super::{
7    DataInformationBox, HintMediaHeaderBox, SampleTableBox, SoundMediaHeaderBox, SubtitleMediaHeaderBox,
8    VideoMediaHeaderBox, VolumetricVisualMediaHeaderBox,
9};
10use crate::common_types::Utf8String;
11use crate::{BoxHeader, FullBoxHeader, IsoBox, IsoSized, Langauge, UnknownBox};
12
13/// Media box
14///
15/// ISO/IEC 14496-12 - 8.4.1
16#[derive(IsoBox, Debug, PartialEq, Eq)]
17#[iso_box(box_type = b"mdia", crate_path = crate)]
18pub struct MediaBox<'a> {
19    /// The contained [`MediaHeaderBox`]. (mandatory)
20    #[iso_box(nested_box)]
21    pub mdhd: MediaHeaderBox,
22    /// The contained [`HandlerBox`]. (mandatory)
23    #[iso_box(nested_box)]
24    pub hdlr: HandlerBox,
25    /// The optional [`ExtendedLanguageBox`]. (optional)
26    #[iso_box(nested_box(collect))]
27    pub elng: Option<ExtendedLanguageBox>,
28    /// The contained [`MediaInformationBox`]. (mandatory)
29    #[iso_box(nested_box)]
30    pub minf: MediaInformationBox<'a>,
31    /// A list of unknown boxes that were not recognized during deserialization.
32    #[iso_box(nested_box(collect_unknown))]
33    pub unknown_boxes: Vec<UnknownBox<'a>>,
34}
35
36impl<'a> MediaBox<'a> {
37    /// Creates a new `MediaBox` with the given `mdhd`, `hdlr`, and `minf`.
38    pub fn new(mdhd: MediaHeaderBox, hdlr: HandlerBox, minf: MediaInformationBox<'a>) -> Self {
39        Self {
40            mdhd,
41            hdlr,
42            elng: None,
43            minf,
44            unknown_boxes: vec![],
45        }
46    }
47}
48
49/// Media header box
50///
51/// ISO/IEC 14496-12 - 8.4.2
52#[derive(IsoBox, Debug, PartialEq, Eq)]
53#[iso_box(box_type = b"mdhd", skip_impl(deserialize_seed, serialize, sized), crate_path = crate)]
54pub struct MediaHeaderBox {
55    /// The full box header.
56    pub full_header: FullBoxHeader,
57    /// An integer that declares the creation time of the media in this track (in seconds since
58    /// midnight, Jan. 1, 1904, in UTC time).
59    pub creation_time: u64,
60    /// An integer that declares the most recent time the media in this track was modified
61    /// (in seconds since midnight, Jan. 1, 1904, in UTC time).
62    pub modification_time: u64,
63    /// An integer that specifies the number of time units that pass in one second for this media.
64    /// For example, a time coordinate system that measures time in sixtieths of a second has a time scale
65    /// of 60.
66    pub timescale: u32,
67    /// An integer that declares the duration of this media (in the scale of the timescale) and should
68    /// be the largest composition timestamp plus the duration of that sample. If the duration cannot be
69    /// determined then duration is set to all 1s.
70    pub duration: u64,
71    /// Declares the language code for this media, as a packed three-character code defined in
72    /// ISO 639-2.
73    pub language: Langauge,
74    /// Pre-defined 16 bits, must be set to 0.
75    pub pre_defined: u16,
76}
77
78impl MediaHeaderBox {
79    /// Creates a new [`MediaHeaderBox`] with the specified parameters.
80    ///
81    /// All other fields are set to their default values.
82    pub fn new(creation_time: u64, modification_time: u64, timescale: u32, duration: u64) -> Self {
83        let version = if creation_time > u32::MAX as u64 || modification_time > u32::MAX as u64 || duration > u32::MAX as u64
84        {
85            1
86        } else {
87            0
88        };
89
90        Self {
91            full_header: FullBoxHeader {
92                version,
93                flags: U24Be(0),
94            },
95            creation_time,
96            modification_time,
97            timescale,
98            duration,
99            language: Langauge::UNDETERMINED,
100            pre_defined: 0,
101        }
102    }
103}
104
105impl<'a> DeserializeSeed<'a, BoxHeader> for MediaHeaderBox {
106    fn deserialize_seed<R>(mut reader: R, _seed: BoxHeader) -> std::io::Result<Self>
107    where
108        R: scuffle_bytes_util::zero_copy::ZeroCopyReader<'a>,
109    {
110        let full_header = FullBoxHeader::deserialize(&mut reader)?;
111
112        let creation_time = if full_header.version == 1 {
113            u64::deserialize(&mut reader)?
114        } else {
115            u32::deserialize(&mut reader)? as u64
116        };
117        let modification_time = if full_header.version == 1 {
118            u64::deserialize(&mut reader)?
119        } else {
120            u32::deserialize(&mut reader)? as u64
121        };
122        let timescale = u32::deserialize(&mut reader)?;
123        let duration = if full_header.version == 1 {
124            u64::deserialize(&mut reader)?
125        } else {
126            u32::deserialize(&mut reader)? as u64
127        };
128
129        let language = Langauge::deserialize(&mut reader)?;
130        let pre_defined = u16::deserialize(&mut reader)?;
131
132        Ok(Self {
133            full_header,
134            creation_time,
135            modification_time,
136            timescale,
137            duration,
138            language,
139            pre_defined,
140        })
141    }
142}
143
144impl Serialize for MediaHeaderBox {
145    fn serialize<W>(&self, mut writer: W) -> std::io::Result<()>
146    where
147        W: std::io::Write,
148    {
149        self.serialize_box_header(&mut writer)?;
150        self.full_header.serialize(&mut writer)?;
151
152        if self.full_header.version == 1 {
153            self.creation_time.serialize(&mut writer)?;
154            self.modification_time.serialize(&mut writer)?;
155            self.timescale.serialize(&mut writer)?;
156            self.duration.serialize(&mut writer)?;
157        } else {
158            (self.creation_time as u32).serialize(&mut writer)?;
159            (self.modification_time as u32).serialize(&mut writer)?;
160            self.timescale.serialize(&mut writer)?;
161            (self.duration as u32).serialize(&mut writer)?;
162        }
163
164        self.language.serialize(&mut writer)?;
165        self.pre_defined.serialize(&mut writer)?;
166
167        Ok(())
168    }
169}
170
171impl IsoSized for MediaHeaderBox {
172    fn size(&self) -> usize {
173        let mut size = self.full_header.size();
174        if self.full_header.version == 1 {
175            size += 8 + 8 + 4 + 8; // creation_time, modification_time, timescale, duration
176        } else {
177            size += 4 + 4 + 4 + 4; // creation_time, modification_time, timescale, duration
178        }
179        size += self.language.size(); // language
180        size += 2; // pre_defined
181
182        Self::add_header_size(size)
183    }
184}
185
186nutype_enum! {
187    /// Handler type as defined in ISO/IEC 14496-12 - 12.
188    pub enum HandlerType([u8; 4]) {
189        /// `null`
190        Null = *b"null",
191        /// `vide`
192        Video = *b"vide",
193        /// `auxv`
194        AuxiliaryVideo = *b"auxv",
195        /// `soun`
196        Audio = *b"soun",
197        /// `meta`
198        Metadata = *b"meta",
199        /// `mp7t`
200        MetadataMpeg7t = *b"mp7t",
201        /// `mp7b`
202        MetadataMpeg7b = *b"mp7b",
203        /// `hint`
204        Hint = *b"hint",
205        /// `text`
206        Text = *b"text",
207        /// `subt`
208        Subtitle = *b"subt",
209        /// `fdsm`
210        Font = *b"fdsm",
211        /// `volv`
212        VolumetricVisual = *b"volv",
213        /// `hapt`
214        Haptic = *b"hapt",
215    }
216}
217
218impl IsoSized for HandlerType {
219    fn size(&self) -> usize {
220        4
221    }
222}
223
224/// Handler reference box
225///
226/// ISO/IEC 14496-12 - 8.4.3
227#[derive(IsoBox, Debug, PartialEq, Eq)]
228#[iso_box(box_type = b"hdlr", crate_path = crate)]
229pub struct HandlerBox {
230    /// The full box header.
231    pub full_header: FullBoxHeader,
232    /// Pre-defined 32 bits, must be set to 0.
233    pub pre_defined: u32,
234    /// - When present in a [`MediaBox`], contains a value as defined in Clause 12, or a value from a derived
235    ///   specification, or registration.
236    /// - When present in a [`MetaBox`](super::MetaBox), contains an appropriate value to indicate the format of the
237    ///   [`MetaBox`](super::MetaBox) contents. The value 'null' can be used in the primary [`MetaBox`](super::MetaBox)
238    ///   to indicate that it is merely being used to hold resources.
239    #[iso_box(from = "[u8; 4]")]
240    pub handler_type: HandlerType,
241    /// Reserved 64 bits, must be set to 0.
242    pub reserved: [u32; 3],
243    /// Gives a human-readable name for the track type (for debugging and inspection purposes).
244    pub name: Utf8String,
245}
246
247impl HandlerBox {
248    /// Creates a new [`HandlerBox`] with the specified `handler_type` and `name`.
249    pub fn new(handler_type: HandlerType, name: Utf8String) -> Self {
250        Self {
251            full_header: FullBoxHeader::default(),
252            pre_defined: 0,
253            handler_type,
254            reserved: [0; 3],
255            name,
256        }
257    }
258}
259
260/// Media information box
261///
262/// ISO/IEC 14496-12 - 8.4.4
263#[derive(IsoBox, Debug, PartialEq, Eq)]
264#[iso_box(box_type = b"minf", crate_path = crate)]
265pub struct MediaInformationBox<'a> {
266    /// The optional [`VideoMediaHeaderBox`]. (optional)
267    #[iso_box(nested_box(collect))]
268    pub vmhd: Option<VideoMediaHeaderBox>,
269    /// The optional [`SoundMediaHeaderBox`]. (optional)
270    #[iso_box(nested_box(collect))]
271    pub smhd: Option<SoundMediaHeaderBox>,
272    /// The optional [`HintMediaHeaderBox`]. (optional)
273    #[iso_box(nested_box(collect))]
274    pub hmhd: Option<HintMediaHeaderBox>,
275    /// The optional [`SubtitleMediaHeaderBox`]. (optional)
276    #[iso_box(nested_box(collect))]
277    pub sthd: Option<SubtitleMediaHeaderBox>,
278    /// The optional [`VolumetricVisualMediaHeaderBox`]. (optional)
279    #[iso_box(nested_box(collect))]
280    pub vvhd: Option<VolumetricVisualMediaHeaderBox>,
281    /// A list of unknown boxes that were not recognized during deserialization.
282    #[iso_box(nested_box(collect_unknown))]
283    pub unknown_boxes: Vec<UnknownBox<'a>>,
284    /// The contained [`DataInformationBox`]. (mandatory)
285    #[iso_box(nested_box)]
286    pub dinf: DataInformationBox<'a>,
287    /// The contained [`SampleTableBox`]. (mandatory)
288    #[iso_box(nested_box)]
289    pub stbl: SampleTableBox<'a>,
290}
291
292impl<'a> MediaInformationBox<'a> {
293    /// Creates a new [`MediaInformationBox`] with the given `stbl`, `vmhd`, and `smhd`.
294    pub fn new(stbl: SampleTableBox<'a>, vmhd: Option<VideoMediaHeaderBox>, smhd: Option<SoundMediaHeaderBox>) -> Self {
295        Self {
296            vmhd,
297            smhd,
298            hmhd: None,
299            sthd: None,
300            vvhd: None,
301            unknown_boxes: Vec::new(),
302            dinf: DataInformationBox::default(),
303            stbl,
304        }
305    }
306}
307
308/// Null media header box
309///
310/// ISO/IEC 14496-12 - 8.4.5.2
311#[derive(IsoBox, Debug, PartialEq, Eq)]
312#[iso_box(box_type = b"nmhd", crate_path = crate)]
313pub struct NullMediaHeaderBox {
314    /// The full box header.
315    pub full_header: FullBoxHeader,
316}
317
318/// Extended language tag
319///
320/// ISO/IEC 14496-12 - 8.4.6
321#[derive(IsoBox, Debug, PartialEq, Eq)]
322#[iso_box(box_type = b"elng", crate_path = crate)]
323pub struct ExtendedLanguageBox {
324    /// The full box header.
325    pub full_header: FullBoxHeader,
326    /// Contains an IETF BCP 47 compliant language tag string, such as "en-US", "fr-FR", or
327    /// "zh-CN".
328    pub extended_language: Utf8String,
329}