flowly_mp4/mp4box/
tfdt.rs

1use byteorder::{BigEndian, WriteBytesExt};
2use serde::Serialize;
3use std::io::Write;
4
5use crate::mp4box::*;
6
7#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize)]
8pub struct TfdtBox {
9    pub version: u8,
10    pub flags: u32,
11    pub base_media_decode_time: u64,
12}
13
14impl TfdtBox {
15    pub fn get_type(&self) -> BoxType {
16        BoxType::TfdtBox
17    }
18
19    pub fn get_size(&self) -> u64 {
20        let mut sum = HEADER_SIZE + HEADER_EXT_SIZE;
21        if self.version == 1 {
22            sum += 8;
23        } else {
24            sum += 4;
25        }
26        sum
27    }
28}
29
30impl Mp4Box for TfdtBox {
31    const TYPE: BoxType = BoxType::TfdtBox;
32
33    fn box_size(&self) -> u64 {
34        self.get_size()
35    }
36
37    fn to_json(&self) -> Result<String, Error> {
38        Ok(serde_json::to_string(&self).unwrap())
39    }
40
41    fn summary(&self) -> Result<String, Error> {
42        let s = format!("base_media_decode_time={}", self.base_media_decode_time);
43        Ok(s)
44    }
45}
46
47impl BlockReader for TfdtBox {
48    fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result<Self, Error> {
49        let (version, flags) = read_box_header_ext(reader);
50
51        let base_media_decode_time = if version == 1 {
52            reader.get_u64()
53        } else if version == 0 {
54            reader.get_u32() as u64
55        } else {
56            return Err(Error::InvalidData("version must be 0 or 1"));
57        };
58
59        Ok(TfdtBox {
60            version,
61            flags,
62            base_media_decode_time,
63        })
64    }
65
66    fn size_hint() -> usize {
67        8
68    }
69}
70
71impl<W: Write> WriteBox<&mut W> for TfdtBox {
72    fn write_box(&self, writer: &mut W) -> Result<u64, Error> {
73        let size = self.box_size();
74        BoxHeader::new(Self::TYPE, size).write(writer)?;
75
76        write_box_header_ext(writer, self.version, self.flags)?;
77
78        if self.version == 1 {
79            writer.write_u64::<BigEndian>(self.base_media_decode_time)?;
80        } else if self.version == 0 {
81            writer.write_u32::<BigEndian>(self.base_media_decode_time as u32)?;
82        } else {
83            return Err(Error::InvalidData("version must be 0 or 1"));
84        }
85
86        Ok(size)
87    }
88}
89
90#[cfg(test)]
91mod tests {
92    use super::*;
93    use crate::mp4box::BoxHeader;
94
95    #[tokio::test]
96    async fn test_tfdt32() {
97        let src_box = TfdtBox {
98            version: 0,
99            flags: 0,
100            base_media_decode_time: 0,
101        };
102        let mut buf = Vec::new();
103        src_box.write_box(&mut buf).unwrap();
104        assert_eq!(buf.len(), src_box.box_size() as usize);
105
106        let mut reader = buf.as_slice();
107        let header = BoxHeader::read(&mut reader, &mut 0).await.unwrap().unwrap();
108        assert_eq!(header.kind, BoxType::TfdtBox);
109        assert_eq!(src_box.box_size(), header.size);
110
111        let dst_box = TfdtBox::read_block(&mut reader).unwrap();
112        assert_eq!(src_box, dst_box);
113    }
114
115    #[tokio::test]
116    async fn test_tfdt64() {
117        let src_box = TfdtBox {
118            version: 1,
119            flags: 0,
120            base_media_decode_time: 0,
121        };
122        let mut buf = Vec::new();
123        src_box.write_box(&mut buf).unwrap();
124        assert_eq!(buf.len(), src_box.box_size() as usize);
125
126        let mut reader = buf.as_slice();
127        let header = BoxHeader::read(&mut reader, &mut 0).await.unwrap().unwrap();
128        assert_eq!(header.kind, BoxType::TfdtBox);
129        assert_eq!(src_box.box_size(), header.size);
130
131        let dst_box = TfdtBox::read_block(&mut reader).unwrap();
132        assert_eq!(src_box, dst_box);
133    }
134}