Skip to main content

mp4_atom/moov/trak/mdia/minf/stbl/stsd/
evte.rs

1use crate::*;
2
3/// `EventMessageSampleEntry` ('evte'), used by CMAF/DASH "Event Message tracks"
4/// to carry timed [`Emsg`] boxes as samples instead of inline in fragments.
5///
6/// See ISO/IEC 23001-18:2022, "Carriage of event messages in the ISO base media
7/// file format", Section 7.2. Each sample is itself contains one or more
8/// [`Emib`] boxes or a single [`Emeb`] box.
9#[derive(Debug, Clone, PartialEq, Eq)]
10#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
11pub struct Evte {
12    pub metadata: MetaData,
13    pub btrt: Option<Btrt>,
14    pub silb: Option<Silb>,
15}
16
17impl Atom for Evte {
18    const KIND: FourCC = FourCC::new(b"evte");
19
20    fn decode_body<B: Buf>(buf: &mut B) -> Result<Self> {
21        let metadata = MetaData::decode(buf)?;
22
23        let mut btrt = None;
24        let mut silb = None;
25        while let Some(atom) = Any::decode_maybe(buf)? {
26            match atom {
27                Any::Btrt(atom) => btrt = atom.into(),
28                Any::Silb(atom) => silb = atom.into(),
29                unknown => Self::decode_unknown(&unknown)?,
30            }
31        }
32        skip_trailing_padding(buf);
33
34        Ok(Self {
35            metadata,
36            btrt,
37            silb,
38        })
39    }
40
41    fn encode_body<B: BufMut>(&self, buf: &mut B) -> Result<()> {
42        self.metadata.encode(buf)?;
43        self.btrt.encode(buf)?;
44        self.silb.encode(buf)?;
45        Ok(())
46    }
47}
48
49/// A single scheme entry in a [`Silb`] box.
50#[derive(Debug, Clone, PartialEq, Eq)]
51#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
52pub struct SchemeIdEntry {
53    pub scheme_id_uri: String,
54    pub value: String,
55    /// Whether the track contains at least one event instance of this scheme.
56    pub atleast_once: bool,
57}
58
59/// `SchemeIdListBox` ('silb'), an optional child of [`Evte`] declaring the
60/// event schemes that may appear in the track.
61///
62/// See ISO/IEC 23001-18:2022 Section 7.3.
63#[derive(Debug, Clone, PartialEq, Eq)]
64#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
65pub struct Silb {
66    pub schemes: Vec<SchemeIdEntry>,
67    /// Whether the track may contain schemes other than the ones listed here.
68    pub other_schemes: bool,
69}
70
71impl AtomExt for Silb {
72    type Ext = ();
73
74    const KIND_EXT: FourCC = FourCC::new(b"silb");
75
76    fn decode_body_ext<B: Buf>(buf: &mut B, _ext: ()) -> Result<Self> {
77        let number_of_schemes = u32::decode(buf)?;
78        // Each entry is at least 3 bytes (two null-terminated strings and a
79        // flags byte); reject counts that cannot possibly fit before
80        // allocating, as with the FLAC Vorbis comment field count (#154).
81        if number_of_schemes as u64 > buf.remaining() as u64 / 3 {
82            return Err(Error::OutOfBounds);
83        }
84
85        let mut schemes = Vec::with_capacity((number_of_schemes as usize).min(16));
86        for _ in 0..number_of_schemes {
87            let scheme_id_uri = String::decode(buf)?;
88            let value = String::decode(buf)?;
89            let flags = u8::decode(buf)?;
90            schemes.push(SchemeIdEntry {
91                scheme_id_uri,
92                value,
93                atleast_once: flags & 0x1 != 0,
94            });
95        }
96
97        let other_schemes_byte = u8::decode(buf)?;
98
99        Ok(Self {
100            schemes,
101            other_schemes: other_schemes_byte & 0x1 != 0,
102        })
103    }
104
105    fn encode_body_ext<B: BufMut>(&self, buf: &mut B) -> Result<()> {
106        (self.schemes.len() as u32).encode(buf)?;
107        for scheme in &self.schemes {
108            scheme.scheme_id_uri.as_str().encode(buf)?;
109            scheme.value.as_str().encode(buf)?;
110            let flags: u8 = if scheme.atleast_once { 0x1 } else { 0 };
111            flags.encode(buf)?;
112        }
113
114        let other_schemes_byte: u8 = if self.other_schemes { 0x1 } else { 0 };
115        other_schemes_byte.encode(buf)?;
116
117        Ok(())
118    }
119}
120
121#[cfg(test)]
122mod tests {
123    use super::*;
124
125    // A bare `evte` sample entry: reserved(6) + data_reference_index(2), no
126    // optional child boxes (ISO/IEC 23001-18:2022).
127    const ENCODED_EVTE: &[u8] = &[
128        0x00, 0x00, 0x00, 0x10, 0x65, 0x76, 0x74, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
129        0x01,
130    ];
131
132    fn decoded_evte() -> Evte {
133        Evte {
134            metadata: MetaData {
135                data_reference_index: 1,
136            },
137            btrt: None,
138            silb: None,
139        }
140    }
141
142    #[test]
143    fn test_evte_decode() {
144        let buf = &mut std::io::Cursor::new(ENCODED_EVTE);
145        let evte = Evte::decode(buf).expect("failed to decode evte");
146        assert_eq!(evte, decoded_evte());
147    }
148
149    #[test]
150    fn test_evte_encode() {
151        let mut buf = Vec::new();
152        decoded_evte().encode(&mut buf).unwrap();
153        assert_eq!(buf.as_slice(), ENCODED_EVTE);
154    }
155
156    #[test]
157    fn test_evte_via_codec() {
158        let evte = decoded_evte();
159
160        let mut buf = Vec::new();
161        Codec::from(evte.clone()).encode(&mut buf).unwrap();
162        assert_eq!(buf.as_slice(), ENCODED_EVTE);
163
164        let decoded = Codec::decode(&mut buf.as_slice()).expect("failed to decode evte codec");
165        assert_eq!(decoded, Codec::Evte(evte));
166    }
167
168    #[test]
169    fn test_silb_roundtrip() {
170        let silb = Silb {
171            schemes: vec![
172                SchemeIdEntry {
173                    scheme_id_uri: "urn:scte:scte35:2014:xml+bin".into(),
174                    value: "1".into(),
175                    atleast_once: true,
176                },
177                SchemeIdEntry {
178                    scheme_id_uri: "urn:mpeg:dash:event:2012".into(),
179                    value: "".into(),
180                    atleast_once: false,
181                },
182            ],
183            other_schemes: true,
184        };
185
186        let mut buf = Vec::new();
187        silb.encode(&mut buf).unwrap();
188
189        let decoded = Silb::decode(&mut buf.as_slice()).expect("failed to decode silb");
190        assert_eq!(decoded, silb);
191    }
192
193    #[test]
194    fn test_evte_with_btrt_and_silb_roundtrip() {
195        let evte = Evte {
196            metadata: MetaData {
197                data_reference_index: 1,
198            },
199            btrt: Some(Btrt {
200                buffer_size_db: 0,
201                max_bitrate: 1000,
202                avg_bitrate: 500,
203            }),
204            silb: Some(Silb {
205                schemes: vec![SchemeIdEntry {
206                    scheme_id_uri: "urn:mpeg:dash:event:2012".into(),
207                    value: "".into(),
208                    atleast_once: true,
209                }],
210                other_schemes: false,
211            }),
212        };
213
214        let mut buf = Vec::new();
215        evte.encode(&mut buf).unwrap();
216
217        let decoded = Evte::decode(&mut buf.as_slice()).expect("failed to decode evte");
218        assert_eq!(decoded, evte);
219    }
220
221    // Regression-style guard, mirroring the FLAC Vorbis comment fix (#154):
222    // a huge number_of_schemes must fail cleanly rather than trying to
223    // allocate an enormous Vec upfront.
224    #[test]
225    fn test_silb_huge_count() {
226        let mut buf = Vec::new();
227        u32::MAX.encode(&mut buf).unwrap(); // number_of_schemes
228
229        assert!(matches!(
230            Silb::decode_body_ext(&mut buf.as_slice(), ()),
231            Err(Error::OutOfBounds)
232        ));
233    }
234}