isobmff/boxes/
movie.rs

1//! Movie structure boxes defined in ISO/IEC 14496-12 - 8.2
2
3use fixed::traits::ToFixed;
4use fixed::types::extra::{U8, U16};
5use fixed::{FixedI16, FixedI32};
6use scuffle_bytes_util::zero_copy::{Deserialize, DeserializeSeed, Serialize};
7
8use super::{MetaBox, MovieExtendsBox, TrackBox, UserDataBox};
9use crate::{BoxHeader, FullBoxHeader, IsoBox, IsoSized, UnknownBox};
10
11/// Movie box
12///
13/// ISO/IEC 14496-12 - 8.2.1
14#[derive(IsoBox, Debug, PartialEq, Eq)]
15#[iso_box(box_type = b"moov", crate_path = crate)]
16pub struct MovieBox<'a> {
17    /// The contained [`MovieHeaderBox`]. (mandatory)
18    #[iso_box(nested_box)]
19    pub mvhd: MovieHeaderBox,
20    /// The contained [`MetaBox`]. (optional)
21    #[iso_box(nested_box(collect))]
22    pub meta: Option<MetaBox<'a>>,
23    /// The contained [`TrackBox`]es. (mandatory, at least one)
24    #[iso_box(nested_box(collect))]
25    pub trak: Vec<TrackBox<'a>>,
26    /// The contained [`MovieExtendsBox`]. (optional)
27    #[iso_box(nested_box(collect))]
28    pub mvex: Option<MovieExtendsBox>,
29    /// A list of unknown boxes that were not recognized during deserialization.
30    #[iso_box(nested_box(collect_unknown))]
31    pub unknown_boxes: Vec<UnknownBox<'a>>,
32    /// The contained [`UserDataBox`]. (optional)
33    #[iso_box(nested_box(collect))]
34    pub udta: Option<UserDataBox<'a>>,
35}
36
37/// Movie header box
38///
39/// ISO/IEC 14496-12 - 8.2.2
40#[derive(IsoBox, Debug, PartialEq, Eq)]
41#[iso_box(box_type = b"mvhd", skip_impl(deserialize_seed, serialize, sized), crate_path = crate)]
42pub struct MovieHeaderBox {
43    /// The full box header.
44    pub full_header: FullBoxHeader,
45    /// An integer that declares the creation time of the presentation (in seconds since
46    /// midnight, Jan. 1, 1904, in UTC time).
47    pub creation_time: u64,
48    /// An integer that declares the most recent time the presentation was modified (in
49    /// seconds since midnight, Jan. 1, 1904, in UTC time).
50    pub modification_time: u64,
51    /// An integer that specifies the time-scale for the entire presentation; this is the number of
52    /// time units that pass in one second. For example, a time coordinate system that measures time in
53    /// sixtieths of a second has a time scale of 60.
54    pub timescale: u32,
55    /// An integer that declares length of the presentation (in the indicated timescale). This property
56    /// is derived from the presentation’s tracks: the value of this field corresponds to the duration of the
57    /// longest track in the presentation. If the duration cannot be determined then duration is set to all 1s.
58    pub duration: u64,
59    /// Indicates the preferred rate to play the presentation; 1.0 is normal forward playback.
60    pub rate: FixedI32<U16>,
61    /// Indicates the preferred playback volume. 1.0 is full volume.
62    pub volume: FixedI16<U8>,
63    /// Reserved 16 bits, must be set to 0.
64    pub reserved1: u16,
65    /// Reserved 64 bits, must be set to 0.
66    pub reserved2: u64,
67    /// Provides a transformation matrix for the video; `(u,v,w)` are restricted here to `(0,0,1)`, hex values
68    /// `(0,0,0x40000000)`.
69    pub matrix: [i32; 9],
70    /// Reserved 6 * 32 bits, must be set to 0.
71    pub pre_defined: [u32; 6],
72    /// Non-zero integer that indicates a value to use for the `track_ID` of the next track to
73    /// be added to this presentation. Zero is not a valid `track_ID` value. The value of `next_track_ID` shall
74    /// be larger than the largest `track_ID` in use. If this value is equal to all 1s ([`u32::MAX`]), and a new
75    /// media track is to be added, then a search must be made in the file for an unused value of `track_ID`.
76    pub next_track_id: u32,
77}
78
79impl MovieHeaderBox {
80    /// Creates a new [`MovieHeaderBox`] with the specified parameters.
81    ///
82    /// All other fields are initialized to their default values.
83    pub fn new(creation_time: u64, modification_time: u64, timescale: u32, duration: u64, next_track_id: u32) -> Self {
84        Self {
85            full_header: FullBoxHeader::default(),
86            creation_time,
87            modification_time,
88            timescale,
89            duration,
90            rate: 1.to_fixed(),
91            volume: 1.to_fixed(),
92            reserved1: 0,
93            reserved2: 0,
94            matrix: [0x00010000, 0, 0, 0, 0x00010000, 0, 0, 0, 0x40000000],
95            pre_defined: [0; 6],
96            next_track_id,
97        }
98    }
99}
100
101impl<'a> DeserializeSeed<'a, BoxHeader> for MovieHeaderBox {
102    fn deserialize_seed<R>(mut reader: R, _seed: BoxHeader) -> std::io::Result<Self>
103    where
104        R: scuffle_bytes_util::zero_copy::ZeroCopyReader<'a>,
105    {
106        let full_header = FullBoxHeader::deserialize(&mut reader)?;
107
108        let creation_time = if full_header.version == 1 {
109            u64::deserialize(&mut reader)?
110        } else {
111            u32::deserialize(&mut reader)? as u64
112        };
113        let modification_time = if full_header.version == 1 {
114            u64::deserialize(&mut reader)?
115        } else {
116            u32::deserialize(&mut reader)? as u64
117        };
118        let timescale = u32::deserialize(&mut reader)?;
119        let duration = if full_header.version == 1 {
120            u64::deserialize(&mut reader)?
121        } else {
122            u32::deserialize(&mut reader)? as u64
123        };
124
125        let rate = FixedI32::from_bits(i32::deserialize(&mut reader)?);
126        let volume = FixedI16::from_bits(i16::deserialize(&mut reader)?);
127
128        let reserved1 = u16::deserialize(&mut reader)?;
129        let reserved2 = u64::deserialize(&mut reader)?;
130
131        let mut matrix = [0; 9];
132        for m in &mut matrix {
133            *m = i32::deserialize(&mut reader)?;
134        }
135
136        let mut pre_defined = [0; 6];
137        for p in &mut pre_defined {
138            *p = u32::deserialize(&mut reader)?;
139        }
140
141        let next_track_id = u32::deserialize(&mut reader)?;
142
143        Ok(Self {
144            full_header,
145            creation_time,
146            modification_time,
147            timescale,
148            duration,
149            rate,
150            volume,
151            reserved1,
152            reserved2,
153            matrix,
154            pre_defined,
155            next_track_id,
156        })
157    }
158}
159
160impl Serialize for MovieHeaderBox {
161    fn serialize<W>(&self, mut writer: W) -> std::io::Result<()>
162    where
163        W: std::io::Write,
164    {
165        self.serialize_box_header(&mut writer)?;
166        self.full_header.serialize(&mut writer)?;
167
168        if self.full_header.version == 1 {
169            self.creation_time.serialize(&mut writer)?;
170            self.modification_time.serialize(&mut writer)?;
171            self.timescale.serialize(&mut writer)?;
172            self.duration.serialize(&mut writer)?;
173        } else {
174            (self.creation_time as u32).serialize(&mut writer)?;
175            (self.modification_time as u32).serialize(&mut writer)?;
176            self.timescale.serialize(&mut writer)?;
177            (self.duration as u32).serialize(&mut writer)?;
178        }
179
180        self.rate.to_bits().serialize(&mut writer)?;
181        self.volume.to_bits().serialize(&mut writer)?;
182        self.reserved1.serialize(&mut writer)?;
183        self.reserved2.serialize(&mut writer)?;
184        self.matrix.serialize(&mut writer)?;
185        self.pre_defined.serialize(&mut writer)?;
186        self.next_track_id.serialize(writer)?;
187
188        Ok(())
189    }
190}
191
192impl IsoSized for MovieHeaderBox {
193    fn size(&self) -> usize {
194        let mut size = self.full_header.size();
195        if self.full_header.version == 1 {
196            size += 8 + 8 + 4 + 8; // creation_time, modification_time, timescale, duration
197        } else {
198            size += 4 + 4 + 4 + 4; // creation_time, modification_time, timescale, duration
199        }
200        size += 4 // rate
201            + 2 // volume
202            + 2 // reserved1
203            + 8 // reserved2
204            + self.matrix.size()
205            + self.pre_defined.size()
206            + 4; // next_track_id
207
208        Self::add_header_size(size)
209    }
210}