scuffle_mp4/boxes/types/
subs.rs

1use std::io;
2
3use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
4use bytes::Bytes;
5
6use crate::boxes::header::{BoxHeader, FullBoxHeader};
7use crate::boxes::traits::BoxType;
8
9#[derive(Debug, Clone, PartialEq)]
10/// Subsample Information Box
11/// ISO/IEC 14496-12:2022(E) - 8.7.7
12pub struct Subs {
13    pub header: FullBoxHeader,
14
15    pub entries: Vec<SubsEntry>,
16}
17
18#[derive(Debug, Clone, PartialEq)]
19/// Subs box entry
20pub struct SubsEntry {
21    pub sample_delta: u32,
22    pub subsamples: Vec<SubSampleEntry>,
23}
24
25#[derive(Debug, Clone, PartialEq)]
26/// Sub Sample Entry
27pub struct SubSampleEntry {
28    pub subsample_size: u32,
29    pub subsample_priority: u8,
30    pub discardable: u8,
31    pub codec_specific_parameters: u32,
32}
33
34impl BoxType for Subs {
35    const NAME: [u8; 4] = *b"subs";
36
37    fn demux(header: BoxHeader, data: Bytes) -> io::Result<Self> {
38        let mut reader = io::Cursor::new(data);
39
40        let header = FullBoxHeader::demux(header, &mut reader)?;
41
42        let entry_count = reader.read_u32::<BigEndian>()?;
43        let mut entries = Vec::with_capacity(entry_count as usize);
44
45        for _ in 0..entry_count {
46            let sample_delta = reader.read_u32::<BigEndian>()?;
47            let subsample_count = reader.read_u16::<BigEndian>()?;
48            let mut subsamples = Vec::with_capacity(subsample_count as usize);
49
50            for _ in 0..subsample_count {
51                let subsample_size = if header.version == 1 {
52                    reader.read_u32::<BigEndian>()?
53                } else {
54                    reader.read_u16::<BigEndian>()? as u32
55                };
56                let subsample_priority = reader.read_u8()?;
57                let discardable = reader.read_u8()?;
58                let codec_specific_parameters = reader.read_u32::<BigEndian>()?;
59                subsamples.push(SubSampleEntry {
60                    subsample_size,
61                    subsample_priority,
62                    discardable,
63                    codec_specific_parameters,
64                });
65            }
66
67            entries.push(SubsEntry {
68                sample_delta,
69                subsamples,
70            });
71        }
72
73        Ok(Self { header, entries })
74    }
75
76    fn primitive_size(&self) -> u64 {
77        let size = self.header.size();
78        let size = size + 4; // entry_count
79        let size = size
80            + self
81                .entries
82                .iter()
83                .map(|e| {
84                    let size = 4; // sample_delta
85                    let size = size + 2; // subsample_count
86
87                    size + e.subsamples.len() as u64
88                        * if self.header.version == 1 {
89                            4 + 1 + 1 + 4
90                        } else {
91                            2 + 1 + 1 + 4
92                        }
93                })
94                .sum::<u64>(); // entries
95        size
96    }
97
98    fn primitive_mux<T: io::Write>(&self, writer: &mut T) -> io::Result<()> {
99        self.header.mux(writer)?;
100
101        writer.write_u32::<BigEndian>(self.entries.len() as u32)?;
102        for entry in &self.entries {
103            writer.write_u32::<BigEndian>(entry.sample_delta)?;
104            writer.write_u16::<BigEndian>(entry.subsamples.len() as u16)?;
105            for subsample in &entry.subsamples {
106                if self.header.version == 1 {
107                    writer.write_u32::<BigEndian>(subsample.subsample_size)?;
108                } else {
109                    writer.write_u16::<BigEndian>(subsample.subsample_size as u16)?;
110                }
111                writer.write_u8(subsample.subsample_priority)?;
112                writer.write_u8(subsample.discardable)?;
113                writer.write_u32::<BigEndian>(subsample.codec_specific_parameters)?;
114            }
115        }
116
117        Ok(())
118    }
119
120    fn validate(&self) -> io::Result<()> {
121        if self.header.version > 1 {
122            return Err(io::Error::new(io::ErrorKind::InvalidData, "subs version must be 0 or 1"));
123        }
124
125        if self.header.version == 0 {
126            for entry in &self.entries {
127                for subsample in &entry.subsamples {
128                    if subsample.subsample_size > u16::MAX as u32 {
129                        return Err(io::Error::new(
130                            io::ErrorKind::InvalidData,
131                            "subs subsample_size must be less than 2^16",
132                        ));
133                    }
134                }
135            }
136        }
137
138        Ok(())
139    }
140}