flowly_mp4/mp4box/
tfhd.rs1use byteorder::{BigEndian, WriteBytesExt};
2use serde::Serialize;
3use std::io::Write;
4
5use crate::mp4box::*;
6
7#[derive(Debug, Clone, PartialEq, Eq, Serialize, Default)]
8pub struct TfhdBox {
9 pub version: u8,
10 pub flags: u32,
11 pub track_id: u32,
12 pub base_data_offset: Option<u64>,
13 pub sample_description_index: Option<u32>,
14 pub default_sample_duration: Option<u32>,
15 pub default_sample_size: Option<u32>,
16 pub default_sample_flags: Option<u32>,
17}
18
19impl TfhdBox {
20 pub const FLAG_BASE_DATA_OFFSET: u32 = 0x01;
21 pub const FLAG_SAMPLE_DESCRIPTION_INDEX: u32 = 0x02;
22 pub const FLAG_DEFAULT_SAMPLE_DURATION: u32 = 0x08;
23 pub const FLAG_DEFAULT_SAMPLE_SIZE: u32 = 0x10;
24 pub const FLAG_DEFAULT_SAMPLE_FLAGS: u32 = 0x20;
25 pub const FLAG_DURATION_IS_EMPTY: u32 = 0x10000;
26 pub const FLAG_DEFAULT_BASE_IS_MOOF: u32 = 0x20000;
27
28 pub fn get_type(&self) -> BoxType {
29 BoxType::TfhdBox
30 }
31
32 pub fn get_size(&self) -> u64 {
33 let mut sum = HEADER_SIZE + HEADER_EXT_SIZE + 4;
34 if TfhdBox::FLAG_BASE_DATA_OFFSET & self.flags > 0 {
35 sum += 8;
36 }
37 if TfhdBox::FLAG_SAMPLE_DESCRIPTION_INDEX & self.flags > 0 {
38 sum += 4;
39 }
40 if TfhdBox::FLAG_DEFAULT_SAMPLE_DURATION & self.flags > 0 {
41 sum += 4;
42 }
43 if TfhdBox::FLAG_DEFAULT_SAMPLE_SIZE & self.flags > 0 {
44 sum += 4;
45 }
46 if TfhdBox::FLAG_DEFAULT_SAMPLE_FLAGS & self.flags > 0 {
47 sum += 4;
48 }
49 sum
50 }
51}
52
53impl Mp4Box for TfhdBox {
54 const TYPE: BoxType = BoxType::TfhdBox;
55
56 fn box_size(&self) -> u64 {
57 self.get_size()
58 }
59
60 fn to_json(&self) -> Result<String, Error> {
61 Ok(serde_json::to_string(&self).unwrap())
62 }
63
64 fn summary(&self) -> Result<String, Error> {
65 let s = format!("track_id={}", self.track_id);
66 Ok(s)
67 }
68}
69
70impl BlockReader for TfhdBox {
71 fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result<Self, Error> {
72 let (version, flags) = read_box_header_ext(reader);
73 let track_id = reader.get_u32();
74
75 let base_data_offset = if TfhdBox::FLAG_BASE_DATA_OFFSET & flags > 0 {
76 Some(reader.get_u64())
77 } else {
78 None
79 };
80
81 let sample_description_index = if TfhdBox::FLAG_SAMPLE_DESCRIPTION_INDEX & flags > 0 {
82 Some(reader.get_u32())
83 } else {
84 None
85 };
86
87 let default_sample_duration = if TfhdBox::FLAG_DEFAULT_SAMPLE_DURATION & flags > 0 {
88 Some(reader.get_u32())
89 } else {
90 None
91 };
92
93 let default_sample_size = if TfhdBox::FLAG_DEFAULT_SAMPLE_SIZE & flags > 0 {
94 Some(reader.get_u32())
95 } else {
96 None
97 };
98
99 let default_sample_flags = if TfhdBox::FLAG_DEFAULT_SAMPLE_FLAGS & flags > 0 {
100 Some(reader.get_u32())
101 } else {
102 None
103 };
104
105 Ok(TfhdBox {
106 version,
107 flags,
108 track_id,
109 base_data_offset,
110 sample_description_index,
111 default_sample_duration,
112 default_sample_size,
113 default_sample_flags,
114 })
115 }
116
117 fn size_hint() -> usize {
118 8
119 }
120}
121
122impl<W: Write> WriteBox<&mut W> for TfhdBox {
123 fn write_box(&self, writer: &mut W) -> Result<u64, Error> {
124 let size = self.box_size();
125 BoxHeader::new(Self::TYPE, size).write(writer)?;
126
127 write_box_header_ext(writer, self.version, self.flags)?;
128 writer.write_u32::<BigEndian>(self.track_id)?;
129 if let Some(base_data_offset) = self.base_data_offset {
130 writer.write_u64::<BigEndian>(base_data_offset)?;
131 }
132 if let Some(sample_description_index) = self.sample_description_index {
133 writer.write_u32::<BigEndian>(sample_description_index)?;
134 }
135 if let Some(default_sample_duration) = self.default_sample_duration {
136 writer.write_u32::<BigEndian>(default_sample_duration)?;
137 }
138 if let Some(default_sample_size) = self.default_sample_size {
139 writer.write_u32::<BigEndian>(default_sample_size)?;
140 }
141 if let Some(default_sample_flags) = self.default_sample_flags {
142 writer.write_u32::<BigEndian>(default_sample_flags)?;
143 }
144
145 Ok(size)
146 }
147}
148
149#[cfg(test)]
150mod tests {
151 use super::*;
152 use crate::mp4box::BoxHeader;
153
154 #[tokio::test]
155 async fn test_tfhd() {
156 let src_box = TfhdBox {
157 version: 0,
158 flags: 0,
159 track_id: 1,
160 base_data_offset: None,
161 sample_description_index: None,
162 default_sample_duration: None,
163 default_sample_size: None,
164 default_sample_flags: None,
165 };
166 let mut buf = Vec::new();
167 src_box.write_box(&mut buf).unwrap();
168 assert_eq!(buf.len(), src_box.box_size() as usize);
169
170 let mut reader = buf.as_slice();
171 let header = BoxHeader::read(&mut reader, &mut 0).await.unwrap().unwrap();
172 assert_eq!(header.kind, BoxType::TfhdBox);
173 assert_eq!(src_box.box_size(), header.size);
174
175 let dst_box = TfhdBox::read_block(&mut reader).unwrap();
176 assert_eq!(src_box, dst_box);
177 }
178
179 #[tokio::test]
180 async fn test_tfhd_with_flags() {
181 let src_box = TfhdBox {
182 version: 0,
183 flags: TfhdBox::FLAG_SAMPLE_DESCRIPTION_INDEX
184 | TfhdBox::FLAG_DEFAULT_SAMPLE_DURATION
185 | TfhdBox::FLAG_DEFAULT_SAMPLE_FLAGS,
186 track_id: 1,
187 base_data_offset: None,
188 sample_description_index: Some(1),
189 default_sample_duration: Some(512),
190 default_sample_size: None,
191 default_sample_flags: Some(0x1010000),
192 };
193 let mut buf = Vec::new();
194 src_box.write_box(&mut buf).unwrap();
195 assert_eq!(buf.len(), src_box.box_size() as usize);
196
197 let mut reader = buf.as_slice();
198 let header = BoxHeader::read(&mut reader, &mut 0).await.unwrap().unwrap();
199 assert_eq!(header.kind, BoxType::TfhdBox);
200 assert_eq!(src_box.box_size(), header.size);
201
202 let dst_box = TfhdBox::read_block(&mut reader).unwrap();
203 assert_eq!(src_box, dst_box);
204 }
205}