Skip to main content

mp4/mp4box/
tfhd.rs

1use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
2use serde::Serialize;
3use std::io::{Read, Seek, 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    fn box_type(&self) -> BoxType {
55        self.get_type()
56    }
57
58    fn box_size(&self) -> u64 {
59        self.get_size()
60    }
61
62    fn to_json(&self) -> Result<String> {
63        Ok(serde_json::to_string(&self).unwrap())
64    }
65
66    fn summary(&self) -> Result<String> {
67        let s = format!("track_id={}", self.track_id);
68        Ok(s)
69    }
70}
71
72impl<R: Read + Seek> ReadBox<&mut R> for TfhdBox {
73    fn read_box(reader: &mut R, size: u64) -> Result<Self> {
74        let start = box_start(reader)?;
75
76        let (version, flags) = read_box_header_ext(reader)?;
77        let track_id = reader.read_u32::<BigEndian>()?;
78        let base_data_offset = if TfhdBox::FLAG_BASE_DATA_OFFSET & flags > 0 {
79            Some(reader.read_u64::<BigEndian>()?)
80        } else {
81            None
82        };
83        let sample_description_index = if TfhdBox::FLAG_SAMPLE_DESCRIPTION_INDEX & flags > 0 {
84            Some(reader.read_u32::<BigEndian>()?)
85        } else {
86            None
87        };
88        let default_sample_duration = if TfhdBox::FLAG_DEFAULT_SAMPLE_DURATION & flags > 0 {
89            Some(reader.read_u32::<BigEndian>()?)
90        } else {
91            None
92        };
93        let default_sample_size = if TfhdBox::FLAG_DEFAULT_SAMPLE_SIZE & flags > 0 {
94            Some(reader.read_u32::<BigEndian>()?)
95        } else {
96            None
97        };
98        let default_sample_flags = if TfhdBox::FLAG_DEFAULT_SAMPLE_FLAGS & flags > 0 {
99            Some(reader.read_u32::<BigEndian>()?)
100        } else {
101            None
102        };
103
104        skip_bytes_to(reader, start + size)?;
105
106        Ok(TfhdBox {
107            version,
108            flags,
109            track_id,
110            base_data_offset,
111            sample_description_index,
112            default_sample_duration,
113            default_sample_size,
114            default_sample_flags,
115        })
116    }
117}
118
119impl<W: Write> WriteBox<&mut W> for TfhdBox {
120    fn write_box(&self, writer: &mut W) -> Result<u64> {
121        let size = self.box_size();
122        BoxHeader::new(self.box_type(), size).write(writer)?;
123
124        write_box_header_ext(writer, self.version, self.flags)?;
125        writer.write_u32::<BigEndian>(self.track_id)?;
126        if let Some(base_data_offset) = self.base_data_offset {
127            writer.write_u64::<BigEndian>(base_data_offset)?;
128        }
129        if let Some(sample_description_index) = self.sample_description_index {
130            writer.write_u32::<BigEndian>(sample_description_index)?;
131        }
132        if let Some(default_sample_duration) = self.default_sample_duration {
133            writer.write_u32::<BigEndian>(default_sample_duration)?;
134        }
135        if let Some(default_sample_size) = self.default_sample_size {
136            writer.write_u32::<BigEndian>(default_sample_size)?;
137        }
138        if let Some(default_sample_flags) = self.default_sample_flags {
139            writer.write_u32::<BigEndian>(default_sample_flags)?;
140        }
141
142        Ok(size)
143    }
144}
145
146#[cfg(test)]
147mod tests {
148    use super::*;
149    use crate::mp4box::BoxHeader;
150    use std::io::Cursor;
151
152    #[test]
153    fn test_tfhd() {
154        let src_box = TfhdBox {
155            version: 0,
156            flags: 0,
157            track_id: 1,
158            base_data_offset: None,
159            sample_description_index: None,
160            default_sample_duration: None,
161            default_sample_size: None,
162            default_sample_flags: None,
163        };
164        let mut buf = Vec::new();
165        src_box.write_box(&mut buf).unwrap();
166        assert_eq!(buf.len(), src_box.box_size() as usize);
167
168        let mut reader = Cursor::new(&buf);
169        let header = BoxHeader::read(&mut reader).unwrap();
170        assert_eq!(header.name, BoxType::TfhdBox);
171        assert_eq!(src_box.box_size(), header.size);
172
173        let dst_box = TfhdBox::read_box(&mut reader, header.size).unwrap();
174        assert_eq!(src_box, dst_box);
175    }
176
177    #[test]
178    fn test_tfhd_with_flags() {
179        let src_box = TfhdBox {
180            version: 0,
181            flags: TfhdBox::FLAG_SAMPLE_DESCRIPTION_INDEX
182                | TfhdBox::FLAG_DEFAULT_SAMPLE_DURATION
183                | TfhdBox::FLAG_DEFAULT_SAMPLE_FLAGS,
184            track_id: 1,
185            base_data_offset: None,
186            sample_description_index: Some(1),
187            default_sample_duration: Some(512),
188            default_sample_size: None,
189            default_sample_flags: Some(0x1010000),
190        };
191        let mut buf = Vec::new();
192        src_box.write_box(&mut buf).unwrap();
193        assert_eq!(buf.len(), src_box.box_size() as usize);
194
195        let mut reader = Cursor::new(&buf);
196        let header = BoxHeader::read(&mut reader).unwrap();
197        assert_eq!(header.name, BoxType::TfhdBox);
198        assert_eq!(src_box.box_size(), header.size);
199
200        let dst_box = TfhdBox::read_box(&mut reader, header.size).unwrap();
201        assert_eq!(src_box, dst_box);
202    }
203}