mp4_atom/moov/trak/mdia/minf/stbl/
sbgp.rs

1use crate::*;
2
3/// SampleToGroupBox, ISO/IEC 14496-12:2024 Sect 8.9.2
4#[derive(Debug, Clone, PartialEq, Eq)]
5#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
6pub struct Sbgp {
7    pub grouping_type: FourCC,
8    pub grouping_type_parameter: Option<u32>,
9    pub entries: Vec<SbgpEntry>,
10}
11
12#[derive(Debug, Clone, PartialEq, Eq)]
13#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
14pub struct SbgpEntry {
15    pub sample_count: u32,
16    pub group_description_index: u32,
17}
18
19ext! {
20    name: Sbgp,
21    versions: [0, 1],
22    flags: {}
23}
24
25impl AtomExt for Sbgp {
26    type Ext = SbgpExt;
27
28    const KIND_EXT: FourCC = FourCC::new(b"sbgp");
29
30    fn decode_body_ext<B: Buf>(buf: &mut B, ext: Self::Ext) -> Result<Self> {
31        let grouping_type = FourCC::decode(buf)?;
32        let grouping_type_parameter = if ext.version == SbgpVersion::V1 {
33            Some(u32::decode(buf)?)
34        } else {
35            None
36        };
37        let entry_count = u32::decode(buf)?;
38        // Use with_capacity to reduce the number of times that the Vec will reallocate to grow for
39        // more entries; however, limit to a max of 1024 entries to start with, as the `entry_count`
40        // is a number defined from outside data (that is being decoded), and so is an attack vector
41        // if a malicious actor set a very high number.
42        let mut entries = Vec::with_capacity((entry_count as usize).min(1024));
43        for _ in 0..entry_count {
44            let sample_count = u32::decode(buf)?;
45            let group_description_index = u32::decode(buf)?;
46            entries.push(SbgpEntry {
47                sample_count,
48                group_description_index,
49            });
50        }
51        Ok(Self {
52            grouping_type,
53            grouping_type_parameter,
54            entries,
55        })
56    }
57
58    fn encode_body_ext<B: BufMut>(&self, buf: &mut B) -> Result<Self::Ext> {
59        let ext = if self.grouping_type_parameter.is_some() {
60            SbgpExt {
61                version: SbgpVersion::V1,
62            }
63        } else {
64            SbgpExt {
65                version: SbgpVersion::V0,
66            }
67        };
68        self.grouping_type.encode(buf)?;
69        if let Some(grouping_type_parameter) = self.grouping_type_parameter {
70            grouping_type_parameter.encode(buf)?;
71        }
72        (self.entries.len() as u32).encode(buf)?;
73        for entry in &self.entries {
74            entry.sample_count.encode(buf)?;
75            entry.group_description_index.encode(buf)?
76        }
77        Ok(ext)
78    }
79}
80
81#[cfg(test)]
82mod tests {
83    use super::*;
84    use std::io::Cursor;
85
86    // This example was taken from:
87    // https://mpeggroup.github.io/FileFormatConformance/files/published/isobmff/a9-aac-samplegroups-edit.mp4
88    //
89    // I just extracted the bytes for the sbgp atom location.
90    const SIMPLE_SBGP: &[u8] = &[
91        0x00, 0x00, 0x00, 0x1C, 0x73, 0x62, 0x67, 0x70, 0x00, 0x00, 0x00, 0x00, 0x72, 0x6F, 0x6C,
92        0x6C, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x01,
93    ];
94
95    #[test]
96    fn sbgp_decodes_from_bytes_correctly() {
97        let mut buf = Cursor::new(SIMPLE_SBGP);
98        let sbgp = Sbgp::decode(&mut buf).expect("sbgp should decode successfully");
99        assert_eq!(
100            sbgp,
101            Sbgp {
102                grouping_type: FourCC::from(b"roll"),
103                grouping_type_parameter: None,
104                entries: vec![SbgpEntry {
105                    sample_count: 48,
106                    group_description_index: 1,
107                }],
108            }
109        )
110    }
111
112    #[test]
113    fn sbgp_encodes_from_type_correctly() {
114        let sbgp = Sbgp {
115            grouping_type: FourCC::from(b"roll"),
116            grouping_type_parameter: None,
117            entries: vec![SbgpEntry {
118                sample_count: 48,
119                group_description_index: 1,
120            }],
121        };
122        let mut buf = Vec::new();
123        sbgp.encode(&mut buf).expect("encode should be successful");
124        assert_eq!(SIMPLE_SBGP, &buf);
125    }
126}