mp4_atom/
sidx.rs

1use crate::*;
2
3// SegmentIndexBox, ISO/IEC 14496-12 Section 8.16.3
4// This is called out in CMAF (23000-19) and DASH (23009-1).
5
6ext! {
7    name: Sidx,
8    versions: [0, 1],
9    flags: {}
10}
11
12#[derive(Debug, Clone, PartialEq, Eq)]
13#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
14pub struct SegmentReference {
15    pub reference_type: bool,
16    pub reference_size: u32,
17    pub subsegment_duration: u32,
18    pub starts_with_sap: bool,
19    pub sap_type: u8,
20    pub sap_delta_time: u32,
21}
22
23#[derive(Debug, Clone, PartialEq, Eq, Default)]
24#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
25pub struct Sidx {
26    pub reference_id: u32,
27    pub timescale: u32,
28    pub earliest_presentation_time: u64,
29    pub first_offset: u64,
30    pub references: Vec<SegmentReference>,
31}
32
33impl AtomExt for Sidx {
34    type Ext = SidxExt;
35
36    const KIND_EXT: FourCC = FourCC::new(b"sidx");
37
38    fn decode_body_ext<B: Buf>(buf: &mut B, ext: SidxExt) -> Result<Self> {
39        let reference_id = u32::decode(buf)?;
40        let timescale = u32::decode(buf)?;
41        let (earliest_presentation_time, first_offset) = if ext.version == SidxVersion::V0 {
42            (u32::decode(buf)?.into(), u32::decode(buf)?.into())
43        } else {
44            (u64::decode(buf)?, u64::decode(buf)?)
45        };
46        let _reserved = u16::decode(buf)?;
47        let reference_count = u16::decode(buf)?;
48        let mut references = Vec::with_capacity(std::cmp::min(reference_count as usize, 128));
49        for _ in 0..reference_count {
50            let reference_type_and_size = u32::decode(buf)?;
51            let reference_type = (reference_type_and_size & 0x8000_0000) == 0x8000_0000;
52            let reference_size = reference_type_and_size & 0x7FFF_FFFF;
53            let subsegment_duration = u32::decode(buf)?;
54            let sap_flag_and_type_and_delta_time = u32::decode(buf)?;
55            let starts_with_sap = (sap_flag_and_type_and_delta_time & 0x8000_0000) == 0x8000_0000;
56            let sap_type = ((sap_flag_and_type_and_delta_time >> 28) & 0b111) as u8;
57            let sap_delta_time = sap_flag_and_type_and_delta_time & 0x0FFF_FFFF;
58            let reference = SegmentReference {
59                reference_type,
60                reference_size,
61                subsegment_duration,
62                starts_with_sap,
63                sap_type,
64                sap_delta_time,
65            };
66            references.push(reference);
67        }
68
69        Ok(Sidx {
70            reference_id,
71            timescale,
72            earliest_presentation_time,
73            first_offset,
74            references,
75        })
76    }
77
78    fn encode_body_ext<B: BufMut>(&self, buf: &mut B) -> Result<SidxExt> {
79        self.reference_id.encode(buf)?;
80        self.timescale.encode(buf)?;
81        let version = match (
82            u32::try_from(self.earliest_presentation_time),
83            u32::try_from(self.first_offset),
84        ) {
85            (Ok(earliest_presentation_time), Ok(first_offset)) => {
86                earliest_presentation_time.encode(buf)?;
87                first_offset.encode(buf)?;
88                SidxVersion::V0
89            }
90            _ => {
91                self.earliest_presentation_time.encode(buf)?;
92                self.first_offset.encode(buf)?;
93                SidxVersion::V1
94            }
95        };
96        0u16.encode(buf)?; // reserved
97        let reference_count: u16 = self
98            .references
99            .len()
100            .try_into()
101            .map_err(|_| Error::TooLarge(Self::KIND))?;
102        reference_count.encode(buf)?;
103        for reference in &self.references {
104            let reference_type_and_size: u32 = match reference.reference_type {
105                true => 0x8000_0000 | reference.reference_size,
106                false => reference.reference_size,
107            };
108            reference_type_and_size.encode(buf)?;
109            reference.subsegment_duration.encode(buf)?;
110            let sap_flag_and_type_and_delta_time = match reference.starts_with_sap {
111                true => {
112                    0x8000_0000
113                        | ((reference.sap_type as u32 & 0b111) << 28)
114                        | (reference.sap_delta_time & 0x0FFF_FFFF)
115                }
116                false => {
117                    ((reference.sap_type as u32 & 0b111) << 28)
118                        | (reference.sap_delta_time & 0x0FFF_FFFF)
119                }
120            };
121            sap_flag_and_type_and_delta_time.encode(buf)?;
122        }
123        Ok(SidxExt { version })
124    }
125}
126
127#[cfg(test)]
128mod tests {
129    use super::*;
130
131    // From MPEG File Format Conformance suite: 21_segment.mp4
132    const ENCODED_SIDX: &[u8] = &[
133        0x00, 0x00, 0x00, 0x2c, 0x73, 0x69, 0x64, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
134        0x01, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
135        0x00, 0x01, 0x00, 0x04, 0xfc, 0x80, 0x00, 0x00, 0x13, 0x80, 0x90, 0x00, 0x00, 0x00,
136    ];
137
138    // Decoded values per 21_segment_gpac.json
139    /*
140    "SegmentIndexBox": {
141      "@Size": "44",
142      "@Type": "sidx",
143      "@Version": "0",
144      "@Flags": "0",
145      "@Specification": "p12",
146      "@Container": "file",
147      "@reference_ID": "1",
148      "@timescale": "100",
149      "@earliest_presentation_time": "0",
150      "@first_offset": "0",
151      "Reference": {
152        "@type": "0",
153        "@size": "326784",
154        "@duration": "4992",
155        "@startsWithSAP": "1",
156        "@SAP_type": "1",
157        "@SAPDeltaTime": "0"
158      }
159      */
160
161    #[test]
162    fn test_sidx_v0_decode() {
163        let buf: &mut std::io::Cursor<&&[u8]> = &mut std::io::Cursor::new(&ENCODED_SIDX);
164        let sidx = Sidx::decode(buf).expect("failed to decode sidx");
165        assert_eq!(
166            sidx,
167            Sidx {
168                reference_id: 1,
169                timescale: 100,
170                earliest_presentation_time: 0,
171                first_offset: 0,
172                references: vec![SegmentReference {
173                    reference_type: false,
174                    reference_size: 326784,
175                    subsegment_duration: 4992,
176                    starts_with_sap: true,
177                    sap_type: 1,
178                    sap_delta_time: 0,
179                }],
180            }
181        );
182    }
183
184    #[test]
185    fn test_sidx_v0_encode() {
186        let mut buf = Vec::new();
187        let sidx = Sidx {
188            reference_id: 1,
189            timescale: 100,
190            earliest_presentation_time: 0,
191            first_offset: 0,
192            references: vec![SegmentReference {
193                reference_type: false,
194                reference_size: 326784,
195                subsegment_duration: 4992,
196                starts_with_sap: true,
197                sap_type: 1,
198                sap_delta_time: 0,
199            }],
200        };
201        sidx.encode(&mut buf).unwrap();
202
203        assert_eq!(buf.as_slice(), ENCODED_SIDX);
204    }
205}