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)]
12pub struct Tkhd {
15 pub header: FullBoxHeader,
16 pub creation_time: u64,
17 pub modification_time: u64,
18 pub track_id: u32,
19 pub reserved: u32,
20 pub duration: u64,
21 pub reserved2: [u32; 2],
22 pub layer: u16,
23 pub alternate_group: u16,
24 pub volume: FixedI16<U8>,
25 pub reserved3: u16,
26 pub matrix: [u32; 9],
27 pub width: FixedI32<U16>,
28 pub height: FixedI32<U16>,
29}
30
31impl Tkhd {
32 pub fn new(
33 creation_time: u64,
34 modification_time: u64,
35 track_id: u32,
36 duration: u64,
37 width_height: Option<(u32, u32)>,
38 ) -> Self {
39 let version = if creation_time > u32::MAX as u64 || modification_time > u32::MAX as u64 || duration > u32::MAX as u64
40 {
41 1
42 } else {
43 0
44 };
45
46 let (width, height) = width_height.unwrap_or((0, 0));
47 let volume = if width_height.is_some() {
48 FixedI16::<U8>::from_num(0)
49 } else {
50 FixedI16::<U8>::from_num(1)
51 };
52
53 Self {
54 header: FullBoxHeader::new(Self::NAME, version, Self::TRACK_ENABLED_FLAG | Self::TRACK_IN_MOVIE_FLAG),
55 creation_time,
56 modification_time,
57 track_id,
58 reserved: 0,
59 duration,
60 reserved2: [0; 2],
61 layer: 0,
62 alternate_group: 0,
63 volume,
64 reserved3: 0,
65 matrix: Self::MATRIX,
66 width: FixedI32::<U16>::from_num(width),
67 height: FixedI32::<U16>::from_num(height),
68 }
69 }
70}
71
72impl Tkhd {
73 pub const MATRIX: [u32; 9] = [0x00010000, 0, 0, 0, 0x00010000, 0, 0, 0, 0x40000000];
74 pub const TRACK_ENABLED_FLAG: u32 = 0x000001;
75 pub const TRACK_IN_MOVIE_FLAG: u32 = 0x000002;
76 pub const TRACK_IN_PREVIEW_FLAG: u32 = 0x000004;
77 pub const TRACK_SIZE_IS_ASPECT_RATIO_FLAG: u32 = 0x000008;
78}
79
80impl BoxType for Tkhd {
81 const NAME: [u8; 4] = *b"tkhd";
82
83 fn demux(header: BoxHeader, data: Bytes) -> io::Result<Self> {
84 let mut reader = io::Cursor::new(data);
85
86 let header = FullBoxHeader::demux(header, &mut reader)?;
87
88 let (creation_time, modification_time, track_id, reserved, duration) = if header.version == 1 {
89 (
90 reader.read_u64::<BigEndian>()?, reader.read_u64::<BigEndian>()?, reader.read_u32::<BigEndian>()?, reader.read_u32::<BigEndian>()?, reader.read_u64::<BigEndian>()?, )
96 } else {
97 (
98 reader.read_u32::<BigEndian>()? as u64, reader.read_u32::<BigEndian>()? as u64, reader.read_u32::<BigEndian>()?, reader.read_u32::<BigEndian>()?, reader.read_u32::<BigEndian>()? as u64, )
104 };
105
106 let mut reserved2 = [0; 2];
107 for v in reserved2.iter_mut() {
108 *v = reader.read_u32::<BigEndian>()?;
109 }
110
111 let layer = reader.read_u16::<BigEndian>()?;
112 let alternate_group = reader.read_u16::<BigEndian>()?;
113 let volume = reader.read_i16::<BigEndian>()?;
114
115 let reserved3 = reader.read_u16::<BigEndian>()?;
116
117 let mut matrix = [0; 9];
118 for v in matrix.iter_mut() {
119 *v = reader.read_u32::<BigEndian>()?;
120 }
121
122 let width = reader.read_i32::<BigEndian>()?;
123 let height = reader.read_i32::<BigEndian>()?;
124
125 Ok(Self {
126 header,
127 creation_time,
128 modification_time,
129 track_id,
130 reserved,
131 duration,
132 reserved2,
133 layer,
134 alternate_group,
135 volume: FixedI16::<U8>::from_bits(volume),
136 reserved3,
137 matrix,
138 width: FixedI32::<U16>::from_bits(width),
139 height: FixedI32::<U16>::from_bits(height),
140 })
141 }
142
143 fn primitive_size(&self) -> u64 {
144 let mut size = self.header.size();
145 size += if self.header.version == 1 {
146 8 + 8 + 4 + 4 + 8 } else {
149 4 + 4 + 4 + 4 + 4 };
152
153 size += 4 * 2; size += 2; size += 2; size += 2; size += 2; size += 4 * 9; size += 4; size += 4; size
163 }
164
165 fn primitive_mux<T: io::Write>(&self, writer: &mut T) -> io::Result<()> {
166 self.header.mux(writer)?;
167
168 if self.header.version == 1 {
169 writer.write_u64::<BigEndian>(self.creation_time)?;
170 writer.write_u64::<BigEndian>(self.modification_time)?;
171 writer.write_u32::<BigEndian>(self.track_id)?;
172 writer.write_u32::<BigEndian>(self.reserved)?;
173 writer.write_u64::<BigEndian>(self.duration)?;
174 } else {
175 writer.write_u32::<BigEndian>(self.creation_time as u32)?;
176 writer.write_u32::<BigEndian>(self.modification_time as u32)?;
177 writer.write_u32::<BigEndian>(self.track_id)?;
178 writer.write_u32::<BigEndian>(self.reserved)?;
179 writer.write_u32::<BigEndian>(self.duration as u32)?;
180 }
181
182 for v in self.reserved2.iter() {
183 writer.write_u32::<BigEndian>(*v)?;
184 }
185
186 writer.write_u16::<BigEndian>(self.layer)?;
187 writer.write_u16::<BigEndian>(self.alternate_group)?;
188 writer.write_i16::<BigEndian>(self.volume.to_bits())?;
189 writer.write_u16::<BigEndian>(self.reserved3)?;
190
191 for v in self.matrix.iter() {
192 writer.write_u32::<BigEndian>(*v)?;
193 }
194
195 writer.write_i32::<BigEndian>(self.width.to_bits())?;
196 writer.write_i32::<BigEndian>(self.height.to_bits())?;
197
198 Ok(())
199 }
200
201 fn validate(&self) -> io::Result<()> {
202 if self.header.version > 1 {
203 return Err(io::Error::new(io::ErrorKind::InvalidData, "tkhd version must be 0 or 1"));
204 }
205
206 if self.track_id == 0 {
207 return Err(io::Error::new(io::ErrorKind::InvalidData, "tkhd track_id must not be 0"));
208 }
209
210 if self.reserved != 0 {
211 return Err(io::Error::new(io::ErrorKind::InvalidData, "tkhd reserved must be 0"));
212 }
213
214 if self.reserved2 != [0; 2] {
215 return Err(io::Error::new(io::ErrorKind::InvalidData, "tkhd reserved2 must be 0"));
216 }
217
218 if self.reserved3 != 0 {
219 return Err(io::Error::new(io::ErrorKind::InvalidData, "tkhd reserved3 must be 0"));
220 }
221
222 if self.header.version == 0 {
223 if self.creation_time > u32::MAX as u64 {
224 return Err(io::Error::new(
225 io::ErrorKind::InvalidData,
226 "tkhd creation_time must be less than 2^32",
227 ));
228 }
229
230 if self.modification_time > u32::MAX as u64 {
231 return Err(io::Error::new(
232 io::ErrorKind::InvalidData,
233 "tkhd modification_time must be less than 2^32",
234 ));
235 }
236
237 if self.duration > u32::MAX as u64 {
238 return Err(io::Error::new(
239 io::ErrorKind::InvalidData,
240 "tkhd duration must be less than 2^32",
241 ));
242 }
243 }
244
245 Ok(())
246 }
247}