1use crate::*;
2
3ext! {
4 name: Elst,
5 versions: [0, 1],
6 flags: {}
7}
8
9#[derive(Debug, Clone, PartialEq, Eq, Default)]
10#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
11pub struct Elst {
12 pub entries: Vec<ElstEntry>,
13}
14
15#[derive(Debug, Clone, PartialEq, Eq, Default)]
16#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
17pub struct ElstEntry {
18 pub segment_duration: u64,
19 pub media_time: Option<u64>,
25 pub media_rate: FixedPoint<i16>,
29}
30
31impl AtomExt for Elst {
32 type Ext = ElstExt;
33
34 const KIND_EXT: FourCC = FourCC::new(b"elst");
35
36 fn decode_body_ext<B: Buf>(buf: &mut B, ext: ElstExt) -> Result<Self> {
37 let entry_count = u32::decode(buf)?;
38
39 let mut entries = Vec::new();
40 for _ in 0..entry_count {
41 let (segment_duration, media_time) = match ext.version {
44 ElstVersion::V1 => (u64::decode(buf)?, i64::decode(buf)?),
45 ElstVersion::V0 => (u32::decode(buf)? as u64, i32::decode(buf)? as i64),
46 };
47
48 let media_time = match media_time {
51 -1 => None,
52 t if t >= 0 => Some(t as u64),
53 _ => {
54 return Err(Error::Unsupported(
55 "elst media_time must be -1 or non-negative",
56 ))
57 }
58 };
59
60 entries.push(ElstEntry {
61 segment_duration,
62 media_time,
63 media_rate: FixedPoint::decode(buf)?,
64 });
65 }
66
67 Ok(Elst { entries })
68 }
69
70 fn encode_body_ext<B: BufMut>(&self, buf: &mut B) -> Result<ElstExt> {
71 fn wire_media_time(media_time: Option<u64>) -> Result<i64> {
74 match media_time {
75 None => Ok(-1),
76 Some(t) => i64::try_from(t)
77 .map_err(|_| Error::Unsupported("elst media_time exceeds i64::MAX")),
78 }
79 }
80
81 let use_v0 = !self.entries.iter().any(|e| {
87 e.segment_duration > u32::MAX as u64
88 || e.media_time.is_some_and(|t| t > i32::MAX as u64)
89 });
90
91 (self.entries.len() as u32).encode(buf)?;
92
93 for entry in &self.entries {
94 let media_time = wire_media_time(entry.media_time)?;
95 if use_v0 {
96 (entry.segment_duration as u32).encode(buf)?;
97 (media_time as i32).encode(buf)?;
98 } else {
99 entry.segment_duration.encode(buf)?;
100 media_time.encode(buf)?;
101 }
102 entry.media_rate.encode(buf)?;
103 }
104
105 Ok(if use_v0 {
106 ElstVersion::V0.into()
107 } else {
108 ElstVersion::V1.into()
109 })
110 }
111}
112
113#[cfg(test)]
114mod tests {
115 use super::*;
116
117 #[test]
118 fn test_elst32() {
119 let expected = Elst {
120 entries: vec![ElstEntry {
121 segment_duration: 634634,
122 media_time: Some(0),
123 media_rate: 1.into(),
124 }],
125 };
126 let mut buf = Vec::new();
127 expected.encode(&mut buf).unwrap();
128 assert_eq!(buf[8], 0, "values within 32 bits encode as version 0");
129
130 let mut buf = buf.as_ref();
131 let decoded = Elst::decode(&mut buf).unwrap();
132 assert_eq!(decoded, expected);
133 }
134
135 #[test]
136 fn test_elst64() {
137 let expected = Elst {
138 entries: vec![ElstEntry {
139 segment_duration: 5_000_000_000,
140 media_time: Some(5_000_000_000),
141 media_rate: 1.into(),
142 }],
143 };
144 let mut buf = Vec::new();
145 expected.encode(&mut buf).unwrap();
146 assert_eq!(buf[8], 1, "values beyond 32 bits force version 1");
147
148 let mut buf = buf.as_ref();
149 let decoded = Elst::decode(&mut buf).unwrap();
150 assert_eq!(decoded, expected);
151 }
152
153 #[test]
157 fn test_elst_media_time_above_i32_forces_v1() {
158 let expected = Elst {
159 entries: vec![ElstEntry {
160 segment_duration: 0,
161 media_time: Some(i32::MAX as u64 + 1),
162 media_rate: 1.into(),
163 }],
164 };
165 let mut buf = Vec::new();
166 expected.encode(&mut buf).unwrap();
167 assert_eq!(buf[8], 1, "media_time above i32::MAX must use version 1");
168
169 let decoded = Elst::decode(&mut buf.as_ref()).unwrap();
170 assert_eq!(decoded, expected);
171 }
172
173 #[test]
178 fn test_elst_empty_edit_sentinel() {
179 let expected = Elst {
180 entries: vec![
181 ElstEntry {
182 segment_duration: 23,
183 media_time: None,
184 media_rate: 1.into(),
185 },
186 ElstEntry {
187 segment_duration: 0,
188 media_time: Some(0),
189 media_rate: 1.into(),
190 },
191 ],
192 };
193 let mut buf = Vec::new();
194 expected.encode(&mut buf).unwrap();
195 let decoded = Elst::decode(&mut buf.as_ref()).unwrap();
196 assert_eq!(decoded, expected);
197 }
198
199 #[test]
203 fn test_elst_v0_neg1_decode_reencode() {
204 let raw: &[u8] = &[
205 0x00, 0x00, 0x00, 0x1C, b'e', b'l', b's', b't', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x03, 0xE8, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x01, 0x00, 0x00, ];
213 let decoded = Elst::decode(&mut &raw[..]).unwrap();
214 assert_eq!(decoded.entries[0].media_time, None);
215
216 let mut buf = Vec::new();
219 decoded.encode(&mut buf).unwrap();
220 assert_eq!(buf.as_slice(), raw);
221 }
222
223 #[test]
226 fn test_elst_negative_media_time_rejected_v0() {
227 let raw: &[u8] = &[
228 0x00, 0x00, 0x00, 0x1C, b'e', b'l', b's', b't', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x03, 0xE8, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x01, 0x00, 0x00, ];
236 assert!(Elst::decode(&mut &raw[..]).is_err());
237 }
238
239 #[test]
240 fn test_elst_negative_media_time_rejected_v1() {
241 let raw: &[u8] = &[
242 0x00, 0x00, 0x00, 0x24, b'e', b'l', b's', b't', 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
247 0xE8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
249 0xFE, 0x00, 0x01, 0x00, 0x00, ];
252 assert!(Elst::decode(&mut &raw[..]).is_err());
253 }
254
255 #[test]
257 fn test_elst_media_time_too_large_rejected() {
258 let elst = Elst {
259 entries: vec![ElstEntry {
260 segment_duration: 0,
261 media_time: Some(u64::MAX),
262 media_rate: 1.into(),
263 }],
264 };
265 let mut buf = Vec::new();
266 assert!(elst.encode(&mut buf).is_err());
267 }
268}