scuffle_mp4/boxes/types/
tfhd.rs1use std::io;
2
3use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
4use bytes::Bytes;
5
6use super::trun::TrunSampleFlag;
7use crate::boxes::header::{BoxHeader, FullBoxHeader};
8use crate::boxes::traits::BoxType;
9
10#[derive(Debug, Clone, PartialEq)]
11pub struct Tfhd {
14 pub header: FullBoxHeader,
15 pub track_id: u32,
16 pub base_data_offset: Option<u64>,
17 pub sample_description_index: Option<u32>,
18 pub default_sample_duration: Option<u32>,
19 pub default_sample_size: Option<u32>,
20 pub default_sample_flags: Option<TrunSampleFlag>,
21}
22
23impl Tfhd {
24 pub const BASE_DATA_OFFSET_FLAG: u32 = 0x000001;
25 pub const DEFAULT_BASE_IS_MOOF_FLAG: u32 = 0x020000;
26 pub const DEFAULT_SAMPLE_DURATION_FLAG: u32 = 0x000008;
27 pub const DEFAULT_SAMPLE_FLAGS_FLAG: u32 = 0x000020;
28 pub const DEFAULT_SAMPLE_SIZE_FLAG: u32 = 0x000010;
29 pub const DURATION_IS_EMPTY_FLAG: u32 = 0x010000;
30 pub const SAMPLE_DESCRIPTION_INDEX_FLAG: u32 = 0x000002;
31
32 pub fn new(
33 track_id: u32,
34 base_data_offset: Option<u64>,
35 sample_description_index: Option<u32>,
36 default_sample_duration: Option<u32>,
37 default_sample_size: Option<u32>,
38 default_sample_flags: Option<TrunSampleFlag>,
39 ) -> Self {
40 let flags = if base_data_offset.is_some() {
41 Self::BASE_DATA_OFFSET_FLAG
42 } else {
43 0
44 } | if sample_description_index.is_some() {
45 Self::SAMPLE_DESCRIPTION_INDEX_FLAG
46 } else {
47 0
48 } | if default_sample_duration.is_some() {
49 Self::DEFAULT_SAMPLE_DURATION_FLAG
50 } else {
51 0
52 } | if default_sample_size.is_some() {
53 Self::DEFAULT_SAMPLE_SIZE_FLAG
54 } else {
55 0
56 } | if default_sample_flags.is_some() {
57 Self::DEFAULT_SAMPLE_FLAGS_FLAG
58 } else {
59 0
60 } | Self::DEFAULT_BASE_IS_MOOF_FLAG;
61
62 Self {
63 header: FullBoxHeader::new(Self::NAME, 0, flags),
64 track_id,
65 base_data_offset,
66 sample_description_index,
67 default_sample_duration,
68 default_sample_size,
69 default_sample_flags,
70 }
71 }
72}
73
74impl BoxType for Tfhd {
75 const NAME: [u8; 4] = *b"tfhd";
76
77 fn demux(header: BoxHeader, data: Bytes) -> io::Result<Self> {
78 let mut reader = io::Cursor::new(data);
79
80 let header = FullBoxHeader::demux(header, &mut reader)?;
81
82 let track_id = reader.read_u32::<BigEndian>()?;
83
84 let base_data_offset = if header.flags & Self::BASE_DATA_OFFSET_FLAG != 0 {
85 Some(reader.read_u64::<BigEndian>()?)
86 } else {
87 None
88 };
89
90 let sample_description_index = if header.flags & Self::SAMPLE_DESCRIPTION_INDEX_FLAG != 0 {
91 Some(reader.read_u32::<BigEndian>()?)
92 } else {
93 None
94 };
95
96 let default_sample_duration = if header.flags & Self::DEFAULT_SAMPLE_DURATION_FLAG != 0 {
97 Some(reader.read_u32::<BigEndian>()?)
98 } else {
99 None
100 };
101
102 let default_sample_size = if header.flags & Self::DEFAULT_SAMPLE_SIZE_FLAG != 0 {
103 Some(reader.read_u32::<BigEndian>()?)
104 } else {
105 None
106 };
107
108 let default_sample_flags = if header.flags & Self::DEFAULT_SAMPLE_FLAGS_FLAG != 0 {
109 Some(reader.read_u32::<BigEndian>()?.into())
110 } else {
111 None
112 };
113
114 Ok(Self {
115 header,
116 track_id,
117 base_data_offset,
118 sample_description_index,
119 default_sample_duration,
120 default_sample_size,
121 default_sample_flags,
122 })
123 }
124
125 fn primitive_size(&self) -> u64 {
126 self.header.size()
127 + 4
128 + self.base_data_offset.map_or(0, |_| 8)
129 + self.sample_description_index.map_or(0, |_| 4)
130 + self.default_sample_duration.map_or(0, |_| 4)
131 + self.default_sample_size.map_or(0, |_| 4)
132 + self.default_sample_flags.map_or(0, |_| 4)
133 }
134
135 fn primitive_mux<T: io::Write>(&self, writer: &mut T) -> io::Result<()> {
136 self.header.mux(writer)?;
137
138 writer.write_u32::<BigEndian>(self.track_id)?;
139
140 if let Some(base_data_offset) = self.base_data_offset {
141 writer.write_u64::<BigEndian>(base_data_offset)?;
142 }
143
144 if let Some(sample_description_index) = self.sample_description_index {
145 writer.write_u32::<BigEndian>(sample_description_index)?;
146 }
147
148 if let Some(default_sample_duration) = self.default_sample_duration {
149 writer.write_u32::<BigEndian>(default_sample_duration)?;
150 }
151
152 if let Some(default_sample_size) = self.default_sample_size {
153 writer.write_u32::<BigEndian>(default_sample_size)?;
154 }
155
156 if let Some(default_sample_flags) = self.default_sample_flags {
157 writer.write_u32::<BigEndian>(default_sample_flags.into())?;
158 }
159
160 Ok(())
161 }
162
163 fn validate(&self) -> io::Result<()> {
164 if self.header.version != 0 {
165 return Err(io::Error::new(io::ErrorKind::InvalidData, "tfhd version must be 0"));
166 }
167
168 if self.header.flags & Self::BASE_DATA_OFFSET_FLAG != 0 && self.base_data_offset.is_none() {
169 return Err(io::Error::new(
170 io::ErrorKind::InvalidData,
171 "tfhd base_data_offset flag is set but base_data_offset is not present",
172 ));
173 } else if self.header.flags & Self::BASE_DATA_OFFSET_FLAG == 0 && self.base_data_offset.is_some() {
174 return Err(io::Error::new(
175 io::ErrorKind::InvalidData,
176 "tfhd base_data_offset flag is not set but base_data_offset is present",
177 ));
178 }
179
180 if self.header.flags & Self::SAMPLE_DESCRIPTION_INDEX_FLAG != 0 && self.sample_description_index.is_none() {
181 return Err(io::Error::new(
182 io::ErrorKind::InvalidData,
183 "tfhd sample_description_index flag is set but sample_description_index is not present",
184 ));
185 } else if self.header.flags & Self::SAMPLE_DESCRIPTION_INDEX_FLAG == 0 && self.sample_description_index.is_some() {
186 return Err(io::Error::new(
187 io::ErrorKind::InvalidData,
188 "tfhd sample_description_index flag is not set but sample_description_index is present",
189 ));
190 }
191
192 if self.header.flags & Self::DEFAULT_SAMPLE_DURATION_FLAG != 0 && self.default_sample_duration.is_none() {
193 return Err(io::Error::new(
194 io::ErrorKind::InvalidData,
195 "tfhd default_sample_duration flag is set but default_sample_duration is not present",
196 ));
197 } else if self.header.flags & Self::DEFAULT_SAMPLE_DURATION_FLAG == 0 && self.default_sample_duration.is_some() {
198 return Err(io::Error::new(
199 io::ErrorKind::InvalidData,
200 "tfhd default_sample_duration flag is not set but default_sample_duration is present",
201 ));
202 }
203
204 if self.header.flags & Self::DEFAULT_SAMPLE_SIZE_FLAG != 0 && self.default_sample_size.is_none() {
205 return Err(io::Error::new(
206 io::ErrorKind::InvalidData,
207 "tfhd default_sample_size flag is set but default_sample_size is not present",
208 ));
209 } else if self.header.flags & Self::DEFAULT_SAMPLE_SIZE_FLAG == 0 && self.default_sample_size.is_some() {
210 return Err(io::Error::new(
211 io::ErrorKind::InvalidData,
212 "tfhd default_sample_size flag is not set but default_sample_size is present",
213 ));
214 }
215
216 if self.header.flags & Self::DEFAULT_SAMPLE_FLAGS_FLAG != 0 && self.default_sample_flags.is_none() {
217 return Err(io::Error::new(
218 io::ErrorKind::InvalidData,
219 "tfhd default_sample_flags flag is set but default_sample_flags is not present",
220 ));
221 } else if self.header.flags & Self::DEFAULT_SAMPLE_FLAGS_FLAG == 0 && self.default_sample_flags.is_some() {
222 return Err(io::Error::new(
223 io::ErrorKind::InvalidData,
224 "tfhd default_sample_flags flag is not set but default_sample_flags is present",
225 ));
226 }
227
228 if let Some(default_sample_flags) = self.default_sample_flags {
229 default_sample_flags.validate()?;
230 }
231
232 Ok(())
233 }
234}