Skip to main content

mp4/mp4box/
stsc.rs

1use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
2use serde::Serialize;
3use std::io::{Read, Seek, Write};
4use std::mem::size_of;
5
6use crate::mp4box::*;
7
8#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize)]
9pub struct StscBox {
10    pub version: u8,
11    pub flags: u32,
12
13    #[serde(skip_serializing)]
14    pub entries: Vec<StscEntry>,
15}
16
17impl StscBox {
18    pub fn get_type(&self) -> BoxType {
19        BoxType::StscBox
20    }
21
22    pub fn get_size(&self) -> u64 {
23        HEADER_SIZE + HEADER_EXT_SIZE + 4 + (12 * self.entries.len() as u64)
24    }
25}
26
27#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize)]
28pub struct StscEntry {
29    pub first_chunk: u32,
30    pub samples_per_chunk: u32,
31    pub sample_description_index: u32,
32    pub first_sample: u32,
33}
34
35impl Mp4Box for StscBox {
36    fn box_type(&self) -> BoxType {
37        self.get_type()
38    }
39
40    fn box_size(&self) -> u64 {
41        self.get_size()
42    }
43
44    fn to_json(&self) -> Result<String> {
45        Ok(serde_json::to_string(&self).unwrap())
46    }
47
48    fn summary(&self) -> Result<String> {
49        let s = format!("entries={}", self.entries.len());
50        Ok(s)
51    }
52}
53
54impl<R: Read + Seek> ReadBox<&mut R> for StscBox {
55    fn read_box(reader: &mut R, size: u64) -> Result<Self> {
56        let start = box_start(reader)?;
57
58        let (version, flags) = read_box_header_ext(reader)?;
59
60        let header_size = HEADER_SIZE + HEADER_EXT_SIZE;
61        let other_size = size_of::<u32>(); // entry_count
62        let entry_size = size_of::<u32>() + size_of::<u32>() + size_of::<u32>(); // first_chunk + samples_per_chunk + sample_description_index
63        let entry_count = reader.read_u32::<BigEndian>()?;
64        if u64::from(entry_count)
65            > size
66                .saturating_sub(header_size)
67                .saturating_sub(other_size as u64)
68                / entry_size as u64
69        {
70            return Err(Error::InvalidData(
71                "stsc entry_count indicates more entries than could fit in the box",
72            ));
73        }
74        let mut entries = Vec::with_capacity(entry_count as usize);
75        for _ in 0..entry_count {
76            let entry = StscEntry {
77                first_chunk: reader.read_u32::<BigEndian>()?,
78                samples_per_chunk: reader.read_u32::<BigEndian>()?,
79                sample_description_index: reader.read_u32::<BigEndian>()?,
80                first_sample: 0,
81            };
82            entries.push(entry);
83        }
84
85        let mut sample_id = 1;
86        for i in 0..entry_count {
87            let (first_chunk, samples_per_chunk) = {
88                let entry = entries.get_mut(i as usize).unwrap();
89                entry.first_sample = sample_id;
90                (entry.first_chunk, entry.samples_per_chunk)
91            };
92            if i < entry_count - 1 {
93                let next_entry = entries.get(i as usize + 1).unwrap();
94                sample_id = next_entry
95                    .first_chunk
96                    .checked_sub(first_chunk)
97                    .and_then(|n| n.checked_mul(samples_per_chunk))
98                    .and_then(|n| n.checked_add(sample_id))
99                    .ok_or(Error::InvalidData(
100                        "attempt to calculate stsc sample_id with overflow",
101                    ))?;
102            }
103        }
104
105        skip_bytes_to(reader, start + size)?;
106
107        Ok(StscBox {
108            version,
109            flags,
110            entries,
111        })
112    }
113}
114
115impl<W: Write> WriteBox<&mut W> for StscBox {
116    fn write_box(&self, writer: &mut W) -> Result<u64> {
117        let size = self.box_size();
118        BoxHeader::new(self.box_type(), size).write(writer)?;
119
120        write_box_header_ext(writer, self.version, self.flags)?;
121
122        writer.write_u32::<BigEndian>(self.entries.len() as u32)?;
123        for entry in self.entries.iter() {
124            writer.write_u32::<BigEndian>(entry.first_chunk)?;
125            writer.write_u32::<BigEndian>(entry.samples_per_chunk)?;
126            writer.write_u32::<BigEndian>(entry.sample_description_index)?;
127        }
128
129        Ok(size)
130    }
131}
132
133#[cfg(test)]
134mod tests {
135    use super::*;
136    use crate::mp4box::BoxHeader;
137    use std::io::Cursor;
138
139    #[test]
140    fn test_stsc() {
141        let src_box = StscBox {
142            version: 0,
143            flags: 0,
144            entries: vec![
145                StscEntry {
146                    first_chunk: 1,
147                    samples_per_chunk: 1,
148                    sample_description_index: 1,
149                    first_sample: 1,
150                },
151                StscEntry {
152                    first_chunk: 19026,
153                    samples_per_chunk: 14,
154                    sample_description_index: 1,
155                    first_sample: 19026,
156                },
157            ],
158        };
159        let mut buf = Vec::new();
160        src_box.write_box(&mut buf).unwrap();
161        assert_eq!(buf.len(), src_box.box_size() as usize);
162
163        let mut reader = Cursor::new(&buf);
164        let header = BoxHeader::read(&mut reader).unwrap();
165        assert_eq!(header.name, BoxType::StscBox);
166        assert_eq!(src_box.box_size(), header.size);
167
168        let dst_box = StscBox::read_box(&mut reader, header.size).unwrap();
169        assert_eq!(src_box, dst_box);
170    }
171}