scuffle_mp4/boxes/types/
stz2.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/// Compact Sample Size Box
11/// ISO/IEC 14496-12:2022(E) - 8.7.3.3
12pub struct Stz2 {
13    pub header: FullBoxHeader,
14    pub reserved: u32,
15    pub field_size: u8,
16    pub samples: Vec<u16>,
17}
18
19impl BoxType for Stz2 {
20    const NAME: [u8; 4] = *b"stz2";
21
22    fn demux(header: BoxHeader, data: Bytes) -> io::Result<Self> {
23        let mut reader = io::Cursor::new(data);
24
25        let header = FullBoxHeader::demux(header, &mut reader)?;
26
27        let reserved = reader.read_u24::<BigEndian>()?;
28
29        let field_size = reader.read_u8()?;
30        let sample_count = reader.read_u32::<BigEndian>()?;
31
32        let mut samples = Vec::with_capacity(sample_count as usize);
33
34        let mut sample_idx = 0;
35        while sample_idx < sample_count {
36            let sample = match field_size {
37                4 => {
38                    let byte = reader.read_u8()?;
39                    samples.push((byte >> 4) as u16);
40                    sample_idx += 1;
41                    if sample_idx >= sample_count {
42                        break;
43                    }
44
45                    (byte & 0x0F) as u16
46                }
47                8 => reader.read_u8()? as u16,
48                16 => reader.read_u16::<BigEndian>()?,
49                _ => {
50                    return Err(io::Error::new(io::ErrorKind::InvalidData, "Invalid field size"));
51                }
52            };
53
54            sample_idx += 1;
55            samples.push(sample);
56        }
57
58        Ok(Self {
59            header,
60            reserved,
61            field_size,
62            samples,
63        })
64    }
65
66    fn primitive_size(&self) -> u64 {
67        let size = self.header.size();
68        let size = size + 8; // reserved + field_size + sample_count
69
70        size + (self.samples.len() as u64 * self.field_size as u64)
71    }
72
73    fn primitive_mux<T: io::Write>(&self, writer: &mut T) -> io::Result<()> {
74        self.header.mux(writer)?;
75
76        writer.write_u24::<BigEndian>(self.reserved)?;
77        writer.write_u8(self.field_size)?;
78        writer.write_u32::<BigEndian>(self.samples.len() as u32)?;
79
80        let mut sample_idx = 0;
81        while sample_idx < self.samples.len() {
82            let sample = self.samples[sample_idx];
83            match self.field_size {
84                4 => {
85                    let byte = (sample << 4) as u8;
86                    sample_idx += 1;
87                    if sample_idx >= self.samples.len() {
88                        writer.write_u8(byte)?;
89                        break;
90                    }
91
92                    let sample = self.samples[sample_idx];
93                    let byte = byte | (sample & 0x0F) as u8;
94                    writer.write_u8(byte)?;
95                }
96                8 => writer.write_u8(sample as u8)?,
97                16 => writer.write_u16::<BigEndian>(sample)?,
98                _ => {
99                    return Err(io::Error::new(io::ErrorKind::InvalidData, "Invalid field size"));
100                }
101            };
102
103            sample_idx += 1;
104        }
105
106        Ok(())
107    }
108
109    fn validate(&self) -> io::Result<()> {
110        if self.header.version != 0 {
111            return Err(io::Error::new(io::ErrorKind::InvalidData, "stz2 version must be 0"));
112        }
113
114        if self.reserved != 0 {
115            return Err(io::Error::new(io::ErrorKind::InvalidData, "stz2 reserved must be 0"));
116        }
117
118        if self.field_size != 4 && self.field_size != 8 && self.field_size != 16 {
119            return Err(io::Error::new(
120                io::ErrorKind::InvalidData,
121                "stz2 field_size must be 4, 8 or 16",
122            ));
123        }
124
125        if self.field_size != 16 {
126            for sample in &self.samples {
127                if self.field_size == 4 {
128                    if *sample > 0x0F {
129                        return Err(io::Error::new(io::ErrorKind::InvalidData, "stz2 sample value must be 4 bits"));
130                    }
131                } else if self.field_size == 8 && *sample > 0xFF {
132                    return Err(io::Error::new(io::ErrorKind::InvalidData, "stz2 sample value must be 8 bits"));
133                }
134            }
135        }
136
137        Ok(())
138    }
139}