scuffle_mp4/boxes/types/
sbgp.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/// Sample to Group Box
11/// ISO/IEC 14496-12:2022(E) - 8.9.2
12pub struct Sbgp {
13    pub header: FullBoxHeader,
14    pub grouping_type: Option<u32>,
15    pub entries: Vec<SbgpEntry>,
16}
17
18#[derive(Debug, Clone, PartialEq)]
19pub struct SbgpEntry {
20    pub sample_count: u32,
21    pub group_description_index: u32,
22}
23
24impl BoxType for Sbgp {
25    const NAME: [u8; 4] = *b"sbgp";
26
27    fn demux(header: BoxHeader, data: Bytes) -> io::Result<Self> {
28        let mut data = io::Cursor::new(data);
29
30        let header = FullBoxHeader::demux(header, &mut data)?;
31
32        let grouping_type = if header.version == 1 {
33            Some(data.read_u32::<BigEndian>()?)
34        } else {
35            None
36        };
37        let entry_count = data.read_u32::<BigEndian>()?;
38
39        let mut entries = Vec::with_capacity(entry_count as usize);
40
41        for _ in 0..entry_count {
42            let sample_count = data.read_u32::<BigEndian>()?;
43            let group_description_index = data.read_u32::<BigEndian>()?;
44
45            entries.push(SbgpEntry {
46                sample_count,
47                group_description_index,
48            });
49        }
50
51        Ok(Self {
52            header,
53            grouping_type,
54            entries,
55        })
56    }
57
58    fn primitive_size(&self) -> u64 {
59        self.header.size()
60        + 4  // grouping_type
61        + 4 // entry_count
62        + (self.entries.len() as u64 * 8) // entries
63    }
64
65    fn primitive_mux<T: io::Write>(&self, writer: &mut T) -> io::Result<()> {
66        self.header.mux(writer)?;
67
68        if let Some(grouping_type) = self.grouping_type {
69            writer.write_u32::<BigEndian>(grouping_type)?;
70        }
71
72        writer.write_u32::<BigEndian>(self.entries.len() as u32)?;
73
74        for entry in &self.entries {
75            writer.write_u32::<BigEndian>(entry.sample_count)?;
76            writer.write_u32::<BigEndian>(entry.group_description_index)?;
77        }
78
79        Ok(())
80    }
81
82    fn validate(&self) -> io::Result<()> {
83        if self.header.version > 1 {
84            return Err(io::Error::new(io::ErrorKind::InvalidData, "sbgp box version must be 0 or 1"));
85        }
86
87        if self.header.flags != 0 {
88            return Err(io::Error::new(io::ErrorKind::InvalidData, "sbgp box flags must be 0"));
89        }
90
91        if self.header.version == 1 && self.grouping_type.is_none() {
92            return Err(io::Error::new(
93                io::ErrorKind::InvalidData,
94                "sbgp box grouping_type must be present when version is 1",
95            ));
96        } else if self.header.version == 0 && self.grouping_type.is_some() {
97            return Err(io::Error::new(
98                io::ErrorKind::InvalidData,
99                "sbgp box grouping_type must not be present when version is 0",
100            ));
101        }
102
103        Ok(())
104    }
105}