1use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
2use serde::Serialize;
3use std::io::{Read, Seek, Write};
4
5use crate::mp4box::*;
6
7pub enum TrackFlag {
8 TrackEnabled = 0x000001,
9 }
12
13#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
14pub struct TkhdBox {
15 pub version: u8,
16 pub flags: u32,
17 pub creation_time: u64,
18 pub modification_time: u64,
19 pub track_id: u32,
20 pub duration: u64,
21 pub layer: u16,
22 pub alternate_group: u16,
23
24 #[serde(with = "value_u8")]
25 pub volume: FixedPointU8,
26 pub matrix: Matrix,
27
28 #[serde(with = "value_u32")]
29 pub width: FixedPointU16,
30
31 #[serde(with = "value_u32")]
32 pub height: FixedPointU16,
33}
34
35impl Default for TkhdBox {
36 fn default() -> Self {
37 TkhdBox {
38 version: 0,
39 flags: TrackFlag::TrackEnabled as u32,
40 creation_time: 0,
41 modification_time: 0,
42 track_id: 0,
43 duration: 0,
44 layer: 0,
45 alternate_group: 0,
46 volume: FixedPointU8::new(1),
47 matrix: Matrix::default(),
48 width: FixedPointU16::new(0),
49 height: FixedPointU16::new(0),
50 }
51 }
52}
53
54#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
55pub struct Matrix {
56 pub a: i32,
57 pub b: i32,
58 pub u: i32,
59 pub c: i32,
60 pub d: i32,
61 pub v: i32,
62 pub x: i32,
63 pub y: i32,
64 pub w: i32,
65}
66
67impl std::fmt::Display for Matrix {
68 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
69 write!(
70 f,
71 "{:#x} {:#x} {:#x} {:#x} {:#x} {:#x} {:#x} {:#x} {:#x}",
72 self.a, self.b, self.u, self.c, self.d, self.v, self.x, self.y, self.w
73 )
74 }
75}
76
77impl Default for Matrix {
78 fn default() -> Self {
79 Self {
80 a: 0x00010000,
82 b: 0,
83 u: 0,
84 c: 0,
85 d: 0x00010000,
86 v: 0,
87 x: 0,
88 y: 0,
89 w: 0x40000000,
90 }
91 }
92}
93
94impl TkhdBox {
95 pub fn get_type(&self) -> BoxType {
96 BoxType::TkhdBox
97 }
98
99 pub fn get_size(&self) -> u64 {
100 let mut size = HEADER_SIZE + HEADER_EXT_SIZE;
101 if self.version == 1 {
102 size += 32;
103 } else if self.version == 0 {
104 size += 20;
105 }
106 size += 60;
107 size
108 }
109
110 pub fn set_width(&mut self, width: u16) {
111 self.width = FixedPointU16::new(width);
112 }
113
114 pub fn set_height(&mut self, height: u16) {
115 self.height = FixedPointU16::new(height);
116 }
117}
118
119impl Mp4Box for TkhdBox {
120 fn box_type(&self) -> BoxType {
121 self.get_type()
122 }
123
124 fn box_size(&self) -> u64 {
125 self.get_size()
126 }
127
128 fn to_json(&self) -> Result<String> {
129 Ok(serde_json::to_string(&self).unwrap())
130 }
131
132 fn summary(&self) -> Result<String> {
133 let s = format!(
134 "creation_time={} track_id={} duration={} layer={} volume={} matrix={} width={} height={}",
135 self.creation_time,
136 self.track_id,
137 self.duration,
138 self.layer,
139 self.volume.value(),
140 self.matrix,
141 self.width.value(),
142 self.height.value()
143 );
144 Ok(s)
145 }
146}
147
148impl<R: Read + Seek> ReadBox<&mut R> for TkhdBox {
149 fn read_box(reader: &mut R, size: u64) -> Result<Self> {
150 let start = box_start(reader)?;
151
152 let (version, flags) = read_box_header_ext(reader)?;
153
154 let (creation_time, modification_time, track_id, _, duration) = if version == 1 {
155 (
156 reader.read_u64::<BigEndian>()?,
157 reader.read_u64::<BigEndian>()?,
158 reader.read_u32::<BigEndian>()?,
159 reader.read_u32::<BigEndian>()?,
160 reader.read_u64::<BigEndian>()?,
161 )
162 } else if version == 0 {
163 (
164 reader.read_u32::<BigEndian>()? as u64,
165 reader.read_u32::<BigEndian>()? as u64,
166 reader.read_u32::<BigEndian>()?,
167 reader.read_u32::<BigEndian>()?,
168 reader.read_u32::<BigEndian>()? as u64,
169 )
170 } else {
171 return Err(Error::InvalidData("version must be 0 or 1"));
172 };
173 reader.read_u64::<BigEndian>()?; let layer = reader.read_u16::<BigEndian>()?;
175 let alternate_group = reader.read_u16::<BigEndian>()?;
176 let volume = FixedPointU8::new_raw(reader.read_u16::<BigEndian>()?);
177
178 reader.read_u16::<BigEndian>()?; let matrix = Matrix {
180 a: reader.read_i32::<BigEndian>()?,
181 b: reader.read_i32::<BigEndian>()?,
182 u: reader.read_i32::<BigEndian>()?,
183 c: reader.read_i32::<BigEndian>()?,
184 d: reader.read_i32::<BigEndian>()?,
185 v: reader.read_i32::<BigEndian>()?,
186 x: reader.read_i32::<BigEndian>()?,
187 y: reader.read_i32::<BigEndian>()?,
188 w: reader.read_i32::<BigEndian>()?,
189 };
190
191 let width = FixedPointU16::new_raw(reader.read_u32::<BigEndian>()?);
192 let height = FixedPointU16::new_raw(reader.read_u32::<BigEndian>()?);
193
194 skip_bytes_to(reader, start + size)?;
195
196 Ok(TkhdBox {
197 version,
198 flags,
199 creation_time,
200 modification_time,
201 track_id,
202 duration,
203 layer,
204 alternate_group,
205 volume,
206 matrix,
207 width,
208 height,
209 })
210 }
211}
212
213impl<W: Write> WriteBox<&mut W> for TkhdBox {
214 fn write_box(&self, writer: &mut W) -> Result<u64> {
215 let size = self.box_size();
216 BoxHeader::new(self.box_type(), size).write(writer)?;
217
218 write_box_header_ext(writer, self.version, self.flags)?;
219
220 if self.version == 1 {
221 writer.write_u64::<BigEndian>(self.creation_time)?;
222 writer.write_u64::<BigEndian>(self.modification_time)?;
223 writer.write_u32::<BigEndian>(self.track_id)?;
224 writer.write_u32::<BigEndian>(0)?; writer.write_u64::<BigEndian>(self.duration)?;
226 } else if self.version == 0 {
227 writer.write_u32::<BigEndian>(self.creation_time as u32)?;
228 writer.write_u32::<BigEndian>(self.modification_time as u32)?;
229 writer.write_u32::<BigEndian>(self.track_id)?;
230 writer.write_u32::<BigEndian>(0)?; writer.write_u32::<BigEndian>(self.duration as u32)?;
232 } else {
233 return Err(Error::InvalidData("version must be 0 or 1"));
234 }
235
236 writer.write_u64::<BigEndian>(0)?; writer.write_u16::<BigEndian>(self.layer)?;
238 writer.write_u16::<BigEndian>(self.alternate_group)?;
239 writer.write_u16::<BigEndian>(self.volume.raw_value())?;
240
241 writer.write_u16::<BigEndian>(0)?; writer.write_i32::<BigEndian>(self.matrix.a)?;
244 writer.write_i32::<BigEndian>(self.matrix.b)?;
245 writer.write_i32::<BigEndian>(self.matrix.u)?;
246 writer.write_i32::<BigEndian>(self.matrix.c)?;
247 writer.write_i32::<BigEndian>(self.matrix.d)?;
248 writer.write_i32::<BigEndian>(self.matrix.v)?;
249 writer.write_i32::<BigEndian>(self.matrix.x)?;
250 writer.write_i32::<BigEndian>(self.matrix.y)?;
251 writer.write_i32::<BigEndian>(self.matrix.w)?;
252
253 writer.write_u32::<BigEndian>(self.width.raw_value())?;
254 writer.write_u32::<BigEndian>(self.height.raw_value())?;
255
256 Ok(size)
257 }
258}
259
260#[cfg(test)]
261mod tests {
262 use super::*;
263 use crate::mp4box::BoxHeader;
264 use std::io::Cursor;
265
266 #[test]
267 fn test_tkhd32() {
268 let src_box = TkhdBox {
269 version: 0,
270 flags: TrackFlag::TrackEnabled as u32,
271 creation_time: 100,
272 modification_time: 200,
273 track_id: 1,
274 duration: 634634,
275 layer: 0,
276 alternate_group: 0,
277 volume: FixedPointU8::new(1),
278 matrix: Matrix::default(),
279 width: FixedPointU16::new(512),
280 height: FixedPointU16::new(288),
281 };
282 let mut buf = Vec::new();
283 src_box.write_box(&mut buf).unwrap();
284 assert_eq!(buf.len(), src_box.box_size() as usize);
285
286 let mut reader = Cursor::new(&buf);
287 let header = BoxHeader::read(&mut reader).unwrap();
288 assert_eq!(header.name, BoxType::TkhdBox);
289 assert_eq!(src_box.box_size(), header.size);
290
291 let dst_box = TkhdBox::read_box(&mut reader, header.size).unwrap();
292 assert_eq!(src_box, dst_box);
293 }
294
295 #[test]
296 fn test_tkhd64() {
297 let src_box = TkhdBox {
298 version: 1,
299 flags: TrackFlag::TrackEnabled as u32,
300 creation_time: 100,
301 modification_time: 200,
302 track_id: 1,
303 duration: 634634,
304 layer: 0,
305 alternate_group: 0,
306 volume: FixedPointU8::new(1),
307 matrix: Matrix::default(),
308 width: FixedPointU16::new(512),
309 height: FixedPointU16::new(288),
310 };
311 let mut buf = Vec::new();
312 src_box.write_box(&mut buf).unwrap();
313 assert_eq!(buf.len(), src_box.box_size() as usize);
314
315 let mut reader = Cursor::new(&buf);
316 let header = BoxHeader::read(&mut reader).unwrap();
317 assert_eq!(header.name, BoxType::TkhdBox);
318 assert_eq!(src_box.box_size(), header.size);
319
320 let dst_box = TkhdBox::read_box(&mut reader, header.size).unwrap();
321 assert_eq!(src_box, dst_box);
322 }
323}