scuffle_mp4/boxes/types/
stsd.rs

1use std::fmt::Debug;
2use std::io::{
3    Read, Write, {self},
4};
5
6use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
7use bytes::Bytes;
8
9use super::clap::Clap;
10use super::colr::Colr;
11use super::pasp::Pasp;
12use crate::boxes::DynBox;
13use crate::boxes::header::{BoxHeader, FullBoxHeader};
14use crate::boxes::traits::BoxType;
15
16#[derive(Debug, Clone, PartialEq)]
17/// Sample Description Box
18/// ISO/IEC 14496-12:2022(E) - 8.5.2
19pub struct Stsd {
20    pub header: FullBoxHeader,
21    pub entries: Vec<DynBox>,
22}
23
24impl Stsd {
25    pub fn new(entries: Vec<DynBox>) -> Self {
26        Self {
27            header: FullBoxHeader::new(Self::NAME, 0, 0),
28            entries,
29        }
30    }
31
32    pub fn get_codecs(&self) -> impl Iterator<Item = String> + '_ {
33        self.entries.iter().filter_map(|e| match e {
34            DynBox::Av01(av01) => av01.codec().ok().map(|c| c.to_string()),
35            DynBox::Avc1(avc1) => avc1.codec().ok().map(|c| c.to_string()),
36            DynBox::Hev1(hev1) => hev1.codec().ok().map(|c| c.to_string()),
37            DynBox::Opus(opus) => opus.codec().ok().map(|c| c.to_string()),
38            DynBox::Mp4a(mp4a) => mp4a.codec().ok().map(|c| c.to_string()),
39            _ => None,
40        })
41    }
42
43    pub fn is_audio(&self) -> bool {
44        self.entries.iter().any(|e| matches!(e, DynBox::Mp4a(_) | DynBox::Opus(_)))
45    }
46
47    pub fn is_video(&self) -> bool {
48        self.entries
49            .iter()
50            .any(|e| matches!(e, DynBox::Av01(_) | DynBox::Avc1(_) | DynBox::Hev1(_)))
51    }
52}
53
54#[derive(Debug, Clone, PartialEq)]
55/// Sample Entry Box
56/// Contains a template field for the Type of Sample Entry
57/// ISO/IEC 14496-12:2022(E) - 8.5.2.2
58pub struct SampleEntry<T: SampleEntryExtension> {
59    pub reserved: [u8; 6],
60    pub data_reference_index: u16,
61    pub extension: T,
62}
63
64impl<T: SampleEntryExtension> SampleEntry<T> {
65    pub fn new(extension: T) -> Self {
66        Self {
67            reserved: [0; 6],
68            data_reference_index: 1,
69            extension,
70        }
71    }
72}
73
74pub trait SampleEntryExtension: Debug + Clone + PartialEq {
75    fn demux<R: Read>(reader: &mut R) -> io::Result<Self>
76    where
77        Self: Sized;
78
79    fn size(&self) -> u64;
80
81    fn mux<W: Write>(&self, writer: &mut W) -> io::Result<()>;
82
83    fn validate(&self) -> io::Result<()> {
84        Ok(())
85    }
86}
87
88impl<T: SampleEntryExtension> SampleEntry<T> {
89    pub fn demux<R: Read>(reader: &mut R) -> io::Result<Self> {
90        let mut reserved = [0; 6];
91        reader.read_exact(&mut reserved)?;
92
93        let data_reference_index = reader.read_u16::<BigEndian>()?;
94
95        Ok(Self {
96            reserved,
97            data_reference_index,
98            extension: T::demux(reader)?,
99        })
100    }
101
102    pub fn size(&self) -> u64 {
103        6 // reserved
104        + 2 // data_reference_index
105        + self.extension.size()
106    }
107
108    pub fn mux<W: Write>(&self, writer: &mut W) -> io::Result<()> {
109        self.validate()?;
110
111        writer.write_all(&self.reserved)?;
112        writer.write_u16::<BigEndian>(self.data_reference_index)?;
113        self.extension.mux(writer)?;
114
115        Ok(())
116    }
117
118    pub fn validate(&self) -> io::Result<()> {
119        if self.reserved != [0; 6] {
120            return Err(io::Error::new(
121                io::ErrorKind::InvalidData,
122                "sample entry reserved field must be 0",
123            ));
124        }
125
126        self.extension.validate()?;
127
128        Ok(())
129    }
130}
131
132#[derive(Debug, Clone, PartialEq)]
133/// Audio Sample Entry Contents
134/// ISO/IEC 14496-12:2022(E) - 12.2.3.2
135pub struct AudioSampleEntry {
136    pub reserved: [u32; 2],
137    pub channel_count: u16,
138    pub sample_size: u16,
139    pub pre_defined: u16,
140    pub reserved2: u16,
141    pub sample_rate: u32,
142}
143
144impl AudioSampleEntry {
145    pub fn new(channel_count: u16, sample_size: u16, sample_rate: u32) -> Self {
146        Self {
147            reserved: [0, 0],
148            channel_count,
149            sample_size,
150            pre_defined: 0,
151            reserved2: 0,
152            sample_rate,
153        }
154    }
155}
156
157impl SampleEntryExtension for AudioSampleEntry {
158    fn demux<T: io::Read>(reader: &mut T) -> io::Result<Self> {
159        let reserved = [reader.read_u32::<BigEndian>()?, reader.read_u32::<BigEndian>()?];
160
161        let channel_count = reader.read_u16::<BigEndian>()?;
162        let sample_size = reader.read_u16::<BigEndian>()?;
163        let pre_defined = reader.read_u16::<BigEndian>()?;
164        let reserved2 = reader.read_u16::<BigEndian>()?;
165        let sample_rate = reader.read_u32::<BigEndian>()? >> 16;
166
167        Ok(Self {
168            reserved,
169            channel_count,
170            sample_size,
171            pre_defined,
172            reserved2,
173            sample_rate,
174        })
175    }
176
177    fn size(&self) -> u64 {
178        4 // reserved[0]
179        + 4 // reserved[1]
180        + 2 // channel_count
181        + 2 // sample_size
182        + 2 // pre_defined
183        + 2 // reserved2
184        + 4 // sample_rate
185    }
186
187    fn mux<T: io::Write>(&self, writer: &mut T) -> io::Result<()> {
188        writer.write_u32::<BigEndian>(self.reserved[0])?;
189        writer.write_u32::<BigEndian>(self.reserved[1])?;
190        writer.write_u16::<BigEndian>(self.channel_count)?;
191        writer.write_u16::<BigEndian>(self.sample_size)?;
192        writer.write_u16::<BigEndian>(self.pre_defined)?;
193        writer.write_u16::<BigEndian>(self.reserved2)?;
194        writer.write_u32::<BigEndian>(self.sample_rate << 16)?;
195
196        Ok(())
197    }
198
199    fn validate(&self) -> io::Result<()> {
200        if self.reserved != [0, 0] {
201            return Err(io::Error::new(io::ErrorKind::InvalidData, "reserved field must be 0"));
202        }
203
204        if self.pre_defined != 0 {
205            return Err(io::Error::new(io::ErrorKind::InvalidData, "pre_defined field must be 0"));
206        }
207
208        if self.reserved2 != 0 {
209            return Err(io::Error::new(io::ErrorKind::InvalidData, "reserved2 field must be 0"));
210        }
211
212        Ok(())
213    }
214}
215
216#[derive(Debug, Clone, PartialEq)]
217/// Visual Sample Entry Contents
218/// ISO/IEC 14496-12:2022(E) - 12.1.3.2
219pub struct VisualSampleEntry {
220    pub pre_defined: u16,
221    pub reserved: u16,
222    pub pre_defined2: [u32; 3],
223    pub width: u16,
224    pub height: u16,
225    pub horizresolution: u32,
226    pub vertresolution: u32,
227    pub reserved2: u32,
228    pub frame_count: u16,
229    pub compressorname: [u8; 32],
230    pub depth: u16,
231    pub pre_defined3: i16,
232    pub clap: Option<Clap>,
233    pub colr: Option<Colr>,
234    pub pasp: Option<Pasp>,
235}
236
237impl VisualSampleEntry {
238    pub fn new(width: u16, height: u16, colr: Option<Colr>) -> Self {
239        Self {
240            pre_defined: 0,
241            reserved: 0,
242            pre_defined2: [0, 0, 0],
243            width,
244            height,
245            horizresolution: 0x00480000,
246            vertresolution: 0x00480000,
247            reserved2: 0,
248            frame_count: 1,
249            compressorname: [0; 32],
250            depth: 0x0018,
251            pre_defined3: -1,
252            clap: None,
253            colr,
254            pasp: Some(Pasp::new()),
255        }
256    }
257}
258
259impl SampleEntryExtension for VisualSampleEntry {
260    fn demux<T: io::Read>(reader: &mut T) -> io::Result<Self> {
261        let pre_defined = reader.read_u16::<BigEndian>()?;
262        let reserved = reader.read_u16::<BigEndian>()?;
263        let pre_defined2 = [
264            reader.read_u32::<BigEndian>()?,
265            reader.read_u32::<BigEndian>()?,
266            reader.read_u32::<BigEndian>()?,
267        ];
268        let width = reader.read_u16::<BigEndian>()?;
269        let height = reader.read_u16::<BigEndian>()?;
270        let horizresolution = reader.read_u32::<BigEndian>()?;
271        let vertresolution = reader.read_u32::<BigEndian>()?;
272        let reserved2 = reader.read_u32::<BigEndian>()?;
273        let frame_count = reader.read_u16::<BigEndian>()?;
274        let mut compressorname = [0; 32];
275        reader.read_exact(&mut compressorname)?;
276        let depth = reader.read_u16::<BigEndian>()?;
277        let pre_defined3 = reader.read_i16::<BigEndian>()?;
278
279        Ok(Self {
280            pre_defined,
281            reserved,
282            pre_defined2,
283            width,
284            height,
285            horizresolution,
286            vertresolution,
287            reserved2,
288            frame_count,
289            compressorname,
290            depth,
291            pre_defined3,
292            colr: None,
293            clap: None,
294            pasp: None,
295        })
296    }
297
298    fn size(&self) -> u64 {
299        2 // pre_defined
300        + 2 // reserved
301        + 4 // pre_defined2[0]
302        + 4 // pre_defined2[1]
303        + 4 // pre_defined2[2]
304        + 2 // width
305        + 2 // height
306        + 4 // horizresolution
307        + 4 // vertresolution
308        + 4 // reserved2
309        + 2 // frame_count
310        + 32 // compressorname
311        + 2 // depth
312        + 2 // pre_defined3
313        + self.clap.as_ref().map_or(0, |clap| clap.size())
314        + self.pasp.as_ref().map_or(0, |pasp| pasp.size())
315        + self.colr.as_ref().map_or(0, |colr| colr.size())
316    }
317
318    fn mux<T: io::Write>(&self, writer: &mut T) -> io::Result<()> {
319        writer.write_u16::<BigEndian>(self.pre_defined).unwrap();
320        writer.write_u16::<BigEndian>(self.reserved).unwrap();
321        writer.write_u32::<BigEndian>(self.pre_defined2[0]).unwrap();
322        writer.write_u32::<BigEndian>(self.pre_defined2[1]).unwrap();
323        writer.write_u32::<BigEndian>(self.pre_defined2[2]).unwrap();
324        writer.write_u16::<BigEndian>(self.width).unwrap();
325        writer.write_u16::<BigEndian>(self.height).unwrap();
326        writer.write_u32::<BigEndian>(self.horizresolution).unwrap();
327        writer.write_u32::<BigEndian>(self.vertresolution).unwrap();
328        writer.write_u32::<BigEndian>(self.reserved2).unwrap();
329        writer.write_u16::<BigEndian>(self.frame_count).unwrap();
330        writer.write_all(&self.compressorname).unwrap();
331        writer.write_u16::<BigEndian>(self.depth).unwrap();
332        writer.write_i16::<BigEndian>(self.pre_defined3).unwrap();
333        if let Some(clap) = &self.clap {
334            clap.mux(writer).unwrap();
335        }
336        if let Some(pasp) = &self.pasp {
337            pasp.mux(writer).unwrap();
338        }
339        if let Some(colr) = &self.colr {
340            colr.mux(writer).unwrap();
341        }
342        Ok(())
343    }
344
345    fn validate(&self) -> io::Result<()> {
346        if self.pre_defined != 0 {
347            return Err(io::Error::new(io::ErrorKind::InvalidData, "pre_defined field must be 0"));
348        }
349
350        if self.reserved != 0 {
351            return Err(io::Error::new(io::ErrorKind::InvalidData, "reserved field must be 0"));
352        }
353
354        if self.pre_defined2 != [0, 0, 0] {
355            return Err(io::Error::new(io::ErrorKind::InvalidData, "pre_defined2 field must be 0"));
356        }
357
358        if self.reserved2 != 0 {
359            return Err(io::Error::new(io::ErrorKind::InvalidData, "reserved2 field must be 0"));
360        }
361
362        if self.pre_defined3 != -1 {
363            return Err(io::Error::new(io::ErrorKind::InvalidData, "pre_defined3 field must be -1"));
364        }
365
366        Ok(())
367    }
368}
369
370impl BoxType for Stsd {
371    const NAME: [u8; 4] = *b"stsd";
372
373    fn demux(header: BoxHeader, data: Bytes) -> io::Result<Self> {
374        let mut reader = io::Cursor::new(data);
375
376        let header = FullBoxHeader::demux(header, &mut reader)?;
377
378        let entry_count = reader.read_u32::<BigEndian>()?;
379        let mut entries = Vec::with_capacity(entry_count as usize);
380
381        for _ in 0..entry_count {
382            let entry = DynBox::demux(&mut reader)?;
383            entries.push(entry);
384        }
385
386        Ok(Self { header, entries })
387    }
388
389    fn primitive_size(&self) -> u64 {
390        self.header.size()
391            + 4 // entry_count
392            + self.entries.iter().map(|entry| entry.size()).sum::<u64>()
393    }
394
395    fn primitive_mux<T: io::Write>(&self, writer: &mut T) -> io::Result<()> {
396        self.header.mux(writer)?;
397        writer.write_u32::<BigEndian>(self.entries.len() as u32)?;
398        for entry in &self.entries {
399            entry.mux(writer)?;
400        }
401        Ok(())
402    }
403
404    fn validate(&self) -> io::Result<()> {
405        if self.header.flags != 0 {
406            return Err(io::Error::new(io::ErrorKind::InvalidData, "stsd flags must be 0"));
407        }
408
409        if self.header.version != 0 {
410            return Err(io::Error::new(io::ErrorKind::InvalidData, "stsd version must be 0"));
411        }
412
413        Ok(())
414    }
415}