Skip to main content

mp4/mp4box/
mvhd.rs

1use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
2use serde::Serialize;
3use std::io::{Read, Seek, Write};
4
5use crate::mp4box::*;
6
7#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
8pub struct MvhdBox {
9    pub version: u8,
10    pub flags: u32,
11    pub creation_time: u64,
12    pub modification_time: u64,
13    pub timescale: u32,
14    pub duration: u64,
15
16    #[serde(with = "value_u32")]
17    pub rate: FixedPointU16,
18    #[serde(with = "value_u8")]
19    pub volume: FixedPointU8,
20
21    pub matrix: tkhd::Matrix,
22
23    pub next_track_id: u32,
24}
25
26impl MvhdBox {
27    pub fn get_type(&self) -> BoxType {
28        BoxType::MvhdBox
29    }
30
31    pub fn get_size(&self) -> u64 {
32        let mut size = HEADER_SIZE + HEADER_EXT_SIZE;
33        if self.version == 1 {
34            size += 28;
35        } else if self.version == 0 {
36            size += 16;
37        }
38        size += 80;
39        size
40    }
41}
42
43impl Default for MvhdBox {
44    fn default() -> Self {
45        MvhdBox {
46            version: 0,
47            flags: 0,
48            creation_time: 0,
49            modification_time: 0,
50            timescale: 1000,
51            duration: 0,
52            rate: FixedPointU16::new(1),
53            matrix: tkhd::Matrix::default(),
54            volume: FixedPointU8::new(1),
55            next_track_id: 1,
56        }
57    }
58}
59
60impl Mp4Box for MvhdBox {
61    fn box_type(&self) -> BoxType {
62        self.get_type()
63    }
64
65    fn box_size(&self) -> u64 {
66        self.get_size()
67    }
68
69    fn to_json(&self) -> Result<String> {
70        Ok(serde_json::to_string(&self).unwrap())
71    }
72
73    fn summary(&self) -> Result<String> {
74        let s = format!(
75            "creation_time={} timescale={} duration={} rate={} volume={}, matrix={}, next_track_id={}",
76            self.creation_time,
77            self.timescale,
78            self.duration,
79            self.rate.value(),
80            self.volume.value(),
81            self.matrix,
82            self.next_track_id
83        );
84        Ok(s)
85    }
86}
87
88impl<R: Read + Seek> ReadBox<&mut R> for MvhdBox {
89    fn read_box(reader: &mut R, size: u64) -> Result<Self> {
90        let start = box_start(reader)?;
91
92        let (version, flags) = read_box_header_ext(reader)?;
93
94        let (creation_time, modification_time, timescale, duration) = if version == 1 {
95            (
96                reader.read_u64::<BigEndian>()?,
97                reader.read_u64::<BigEndian>()?,
98                reader.read_u32::<BigEndian>()?,
99                reader.read_u64::<BigEndian>()?,
100            )
101        } else if version == 0 {
102            (
103                reader.read_u32::<BigEndian>()? as u64,
104                reader.read_u32::<BigEndian>()? as u64,
105                reader.read_u32::<BigEndian>()?,
106                reader.read_u32::<BigEndian>()? as u64,
107            )
108        } else {
109            return Err(Error::InvalidData("version must be 0 or 1"));
110        };
111        let rate = FixedPointU16::new_raw(reader.read_u32::<BigEndian>()?);
112
113        let volume = FixedPointU8::new_raw(reader.read_u16::<BigEndian>()?);
114
115        reader.read_u16::<BigEndian>()?; // reserved = 0
116
117        reader.read_u64::<BigEndian>()?; // reserved = 0
118
119        let matrix = tkhd::Matrix {
120            a: reader.read_i32::<BigEndian>()?,
121            b: reader.read_i32::<BigEndian>()?,
122            u: reader.read_i32::<BigEndian>()?,
123            c: reader.read_i32::<BigEndian>()?,
124            d: reader.read_i32::<BigEndian>()?,
125            v: reader.read_i32::<BigEndian>()?,
126            x: reader.read_i32::<BigEndian>()?,
127            y: reader.read_i32::<BigEndian>()?,
128            w: reader.read_i32::<BigEndian>()?,
129        };
130
131        skip_bytes(reader, 24)?; // pre_defined = 0
132
133        let next_track_id = reader.read_u32::<BigEndian>()?;
134
135        skip_bytes_to(reader, start + size)?;
136
137        Ok(MvhdBox {
138            version,
139            flags,
140            creation_time,
141            modification_time,
142            timescale,
143            duration,
144            rate,
145            volume,
146            matrix,
147            next_track_id,
148        })
149    }
150}
151
152impl<W: Write> WriteBox<&mut W> for MvhdBox {
153    fn write_box(&self, writer: &mut W) -> Result<u64> {
154        let size = self.box_size();
155        BoxHeader::new(self.box_type(), size).write(writer)?;
156
157        write_box_header_ext(writer, self.version, self.flags)?;
158
159        if self.version == 1 {
160            writer.write_u64::<BigEndian>(self.creation_time)?;
161            writer.write_u64::<BigEndian>(self.modification_time)?;
162            writer.write_u32::<BigEndian>(self.timescale)?;
163            writer.write_u64::<BigEndian>(self.duration)?;
164        } else if self.version == 0 {
165            writer.write_u32::<BigEndian>(self.creation_time as u32)?;
166            writer.write_u32::<BigEndian>(self.modification_time as u32)?;
167            writer.write_u32::<BigEndian>(self.timescale)?;
168            writer.write_u32::<BigEndian>(self.duration as u32)?;
169        } else {
170            return Err(Error::InvalidData("version must be 0 or 1"));
171        }
172        writer.write_u32::<BigEndian>(self.rate.raw_value())?;
173
174        writer.write_u16::<BigEndian>(self.volume.raw_value())?;
175
176        writer.write_u16::<BigEndian>(0)?; // reserved = 0
177
178        writer.write_u64::<BigEndian>(0)?; // reserved = 0
179
180        writer.write_i32::<BigEndian>(self.matrix.a)?;
181        writer.write_i32::<BigEndian>(self.matrix.b)?;
182        writer.write_i32::<BigEndian>(self.matrix.u)?;
183        writer.write_i32::<BigEndian>(self.matrix.c)?;
184        writer.write_i32::<BigEndian>(self.matrix.d)?;
185        writer.write_i32::<BigEndian>(self.matrix.v)?;
186        writer.write_i32::<BigEndian>(self.matrix.x)?;
187        writer.write_i32::<BigEndian>(self.matrix.y)?;
188        writer.write_i32::<BigEndian>(self.matrix.w)?;
189
190        write_zeros(writer, 24)?; // pre_defined = 0
191
192        writer.write_u32::<BigEndian>(self.next_track_id)?;
193
194        Ok(size)
195    }
196}
197
198#[cfg(test)]
199mod tests {
200    use super::*;
201    use crate::mp4box::BoxHeader;
202    use std::io::Cursor;
203
204    #[test]
205    fn test_mvhd32() {
206        let src_box = MvhdBox {
207            version: 0,
208            flags: 0,
209            creation_time: 100,
210            modification_time: 200,
211            timescale: 1000,
212            duration: 634634,
213            rate: FixedPointU16::new(1),
214            volume: FixedPointU8::new(1),
215            matrix: tkhd::Matrix::default(),
216            next_track_id: 1,
217        };
218        let mut buf = Vec::new();
219        src_box.write_box(&mut buf).unwrap();
220        assert_eq!(buf.len(), src_box.box_size() as usize);
221
222        let mut reader = Cursor::new(&buf);
223        let header = BoxHeader::read(&mut reader).unwrap();
224        assert_eq!(header.name, BoxType::MvhdBox);
225        assert_eq!(src_box.box_size(), header.size);
226
227        let dst_box = MvhdBox::read_box(&mut reader, header.size).unwrap();
228        assert_eq!(src_box, dst_box);
229    }
230
231    #[test]
232    fn test_mvhd64() {
233        let src_box = MvhdBox {
234            version: 1,
235            flags: 0,
236            creation_time: 100,
237            modification_time: 200,
238            timescale: 1000,
239            duration: 634634,
240            rate: FixedPointU16::new(1),
241            volume: FixedPointU8::new(1),
242            matrix: tkhd::Matrix::default(),
243            next_track_id: 1,
244        };
245        let mut buf = Vec::new();
246        src_box.write_box(&mut buf).unwrap();
247        assert_eq!(buf.len(), src_box.box_size() as usize);
248
249        let mut reader = Cursor::new(&buf);
250        let header = BoxHeader::read(&mut reader).unwrap();
251        assert_eq!(header.name, BoxType::MvhdBox);
252        assert_eq!(src_box.box_size(), header.size);
253
254        let dst_box = MvhdBox::read_box(&mut reader, header.size).unwrap();
255        assert_eq!(src_box, dst_box);
256    }
257}