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        // entries
80        size + 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>()
95    }
96
97    fn primitive_mux<T: io::Write>(&self, writer: &mut T) -> io::Result<()> {
98        self.header.mux(writer)?;
99
100        writer.write_u32::<BigEndian>(self.entries.len() as u32)?;
101        for entry in &self.entries {
102            writer.write_u32::<BigEndian>(entry.sample_delta)?;
103            writer.write_u16::<BigEndian>(entry.subsamples.len() as u16)?;
104            for subsample in &entry.subsamples {
105                if self.header.version == 1 {
106                    writer.write_u32::<BigEndian>(subsample.subsample_size)?;
107                } else {
108                    writer.write_u16::<BigEndian>(subsample.subsample_size as u16)?;
109                }
110                writer.write_u8(subsample.subsample_priority)?;
111                writer.write_u8(subsample.discardable)?;
112                writer.write_u32::<BigEndian>(subsample.codec_specific_parameters)?;
113            }
114        }
115
116        Ok(())
117    }
118
119    fn validate(&self) -> io::Result<()> {
120        if self.header.version > 1 {
121            return Err(io::Error::new(io::ErrorKind::InvalidData, "subs version must be 0 or 1"));
122        }
123
124        if self.header.version == 0 {
125            for entry in &self.entries {
126                for subsample in &entry.subsamples {
127                    if subsample.subsample_size > u16::MAX as u32 {
128                        return Err(io::Error::new(
129                            io::ErrorKind::InvalidData,
130                            "subs subsample_size must be less than 2^16",
131                        ));
132                    }
133                }
134            }
135        }
136
137        Ok(())
138    }
139}