scuffle_mp4/boxes/types/
mvhd.rs

1use std::io;
2
3use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
4use bytes::Bytes;
5use fixed::types::extra::{U8, U16};
6use fixed::{FixedI16, FixedI32};
7
8use crate::boxes::header::{BoxHeader, FullBoxHeader};
9use crate::boxes::traits::BoxType;
10
11#[derive(Debug, Clone, PartialEq)]
12/// Movie Header Box
13/// ISO/IEC 14496-12:2022(E) - 8.2.2
14pub struct Mvhd {
15    pub header: FullBoxHeader,
16    pub creation_time: u64,
17    pub modification_time: u64,
18    pub timescale: u32,
19    pub duration: u64,
20    pub rate: FixedI32<U16>,
21    pub volume: FixedI16<U8>,
22    pub reserved: u16,
23    pub reserved2: [u32; 2],
24    pub matrix: [u32; 9],
25    pub pre_defined: [u32; 6],
26    pub next_track_id: u32,
27}
28
29impl Mvhd {
30    pub fn new(creation_time: u64, modification_time: u64, timescale: u32, duration: u64, next_track_id: u32) -> Self {
31        Self {
32            header: FullBoxHeader::new(Self::NAME, 0, 0),
33            creation_time,
34            modification_time,
35            timescale,
36            duration,
37            rate: FixedI32::<U16>::from_num(1),
38            volume: FixedI16::<U8>::from_num(1),
39            reserved: 0,
40            reserved2: [0; 2],
41            matrix: Self::MATRIX,
42            pre_defined: [0; 6],
43            next_track_id,
44        }
45    }
46}
47
48impl Mvhd {
49    pub const MATRIX: [u32; 9] = [0x00010000, 0, 0, 0, 0x00010000, 0, 0, 0, 0x40000000];
50}
51
52impl BoxType for Mvhd {
53    const NAME: [u8; 4] = *b"mvhd";
54
55    fn demux(header: BoxHeader, data: Bytes) -> io::Result<Self> {
56        let mut reader = io::Cursor::new(data);
57
58        let header = FullBoxHeader::demux(header, &mut reader)?;
59
60        let (creation_time, modification_time, timescale, duration) = if header.version == 1 {
61            (
62                reader.read_u64::<BigEndian>()?, // creation_time
63                reader.read_u64::<BigEndian>()?, // modification_time
64                reader.read_u32::<BigEndian>()?, // timescale
65                reader.read_u64::<BigEndian>()?, // duration
66            )
67        } else {
68            (
69                reader.read_u32::<BigEndian>()? as u64, // creation_time
70                reader.read_u32::<BigEndian>()? as u64, // modification_time
71                reader.read_u32::<BigEndian>()?,        // timescale
72                reader.read_u32::<BigEndian>()? as u64, // duration
73            )
74        };
75
76        let rate = reader.read_i32::<BigEndian>()?;
77        let volume = reader.read_i16::<BigEndian>()?;
78
79        let reserved = reader.read_u16::<BigEndian>()?;
80        let mut reserved2 = [0; 2];
81        for v in reserved2.iter_mut() {
82            *v = reader.read_u32::<BigEndian>()?;
83        }
84
85        let mut matrix = [0; 9];
86        for v in matrix.iter_mut() {
87            *v = reader.read_u32::<BigEndian>()?;
88        }
89
90        let mut pre_defined = [0; 6];
91        for v in pre_defined.iter_mut() {
92            *v = reader.read_u32::<BigEndian>()?;
93        }
94
95        let next_track_id = reader.read_u32::<BigEndian>()?;
96
97        Ok(Self {
98            header,
99            creation_time,
100            modification_time,
101            timescale,
102            duration,
103            rate: FixedI32::from_bits(rate),
104            volume: FixedI16::from_bits(volume),
105            reserved,
106            reserved2,
107            matrix,
108            pre_defined,
109            next_track_id,
110        })
111    }
112
113    fn primitive_size(&self) -> u64 {
114        let mut size = self.header.size();
115
116        if self.header.version == 1 {
117            size += 8 + 8 + 4 + 8; // creation_time, modification_time,
118        // timescale, duration
119        } else {
120            size += 4 + 4 + 4 + 4; // creation_time, modification_time,
121            // timescale, duration
122        }
123
124        size += 4 + 2 + 2; // rate, volume, reserved
125        size += 4 * 2; // reserved2
126        size += 4 * 9; // matrix
127        size += 4 * 6; // pre_defined
128        size += 4; // next_track_id
129
130        size
131    }
132
133    fn primitive_mux<T: io::Write>(&self, writer: &mut T) -> io::Result<()> {
134        self.header.mux(writer)?;
135
136        if self.header.version == 1 {
137            writer.write_u64::<BigEndian>(self.creation_time)?;
138            writer.write_u64::<BigEndian>(self.modification_time)?;
139            writer.write_u32::<BigEndian>(self.timescale)?;
140            writer.write_u64::<BigEndian>(self.duration)?;
141        } else {
142            writer.write_u32::<BigEndian>(self.creation_time as u32)?;
143            writer.write_u32::<BigEndian>(self.modification_time as u32)?;
144            writer.write_u32::<BigEndian>(self.timescale)?;
145            writer.write_u32::<BigEndian>(self.duration as u32)?;
146        }
147
148        writer.write_i32::<BigEndian>(self.rate.to_bits())?;
149        writer.write_i16::<BigEndian>(self.volume.to_bits())?;
150
151        writer.write_u16::<BigEndian>(self.reserved)?;
152
153        for v in self.reserved2.iter() {
154            writer.write_u32::<BigEndian>(*v)?;
155        }
156
157        for v in self.matrix.iter() {
158            writer.write_u32::<BigEndian>(*v)?;
159        }
160
161        for v in self.pre_defined.iter() {
162            writer.write_u32::<BigEndian>(*v)?;
163        }
164
165        writer.write_u32::<BigEndian>(self.next_track_id)?;
166
167        Ok(())
168    }
169
170    fn validate(&self) -> io::Result<()> {
171        if self.header.flags != 0 {
172            return Err(io::Error::new(io::ErrorKind::InvalidData, "mvhd flags must be 0"));
173        }
174
175        if self.header.version > 1 {
176            return Err(io::Error::new(io::ErrorKind::InvalidData, "mvhd version must be 0 or 1"));
177        }
178
179        if self.header.version == 0 {
180            if self.creation_time > u32::MAX as u64 {
181                return Err(io::Error::new(
182                    io::ErrorKind::InvalidData,
183                    "mvhd creation_time must be less than 2^32",
184                ));
185            }
186
187            if self.modification_time > u32::MAX as u64 {
188                return Err(io::Error::new(
189                    io::ErrorKind::InvalidData,
190                    "mvhd modification_time must be less than 2^32",
191                ));
192            }
193
194            if self.duration > u32::MAX as u64 {
195                return Err(io::Error::new(
196                    io::ErrorKind::InvalidData,
197                    "mvhd duration must be less than 2^32",
198                ));
199            }
200        }
201
202        if self.reserved != 0 {
203            return Err(io::Error::new(io::ErrorKind::InvalidData, "mvhd reserved must be 0"));
204        }
205
206        if self.reserved2 != [0; 2] {
207            return Err(io::Error::new(io::ErrorKind::InvalidData, "mvhd reserved2 must be 0"));
208        }
209
210        if self.pre_defined != [0; 6] {
211            return Err(io::Error::new(io::ErrorKind::InvalidData, "mvhd pre_defined must be 0"));
212        }
213
214        Ok(())
215    }
216}