flowly_mp4/mp4box/
ctts.rs

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