scuffle_mp4/boxes/types/
tfhd.rs

1use 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)]
11/// Track Fragment Header Box
12/// ISO/IEC 14496-12:2022(E) - 8.8.7
13pub 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}