1use m3u8_rs::{MasterPlaylist, MediaPlaylist, Playlist};
52
53pub fn load_master(content: &[u8]) -> Result<MasterPlaylist, String> {
54 match m3u8_rs::parse_playlist(content) {
55 Result::Ok((_, Playlist::MasterPlaylist(pl))) => Ok(pl),
56 Result::Ok((_, Playlist::MediaPlaylist(_))) => Err("must be a master playlist".to_string()),
57 Result::Err(e) => Err(e.to_string()),
58 }
59}
60
61pub fn load_media(content: &[u8]) -> Result<MediaPlaylist, String> {
62 match m3u8_rs::parse_playlist(content) {
63 Result::Ok((_, Playlist::MediaPlaylist(pl))) => Ok(pl),
64 Result::Ok((_, Playlist::MasterPlaylist(_))) => Err("must be a media playlist".to_string()),
65 Result::Err(e) => Err(e.to_string()),
66 }
67}
68
69pub struct Master {
72 pub playlist: MasterPlaylist,
73}
74
75pub struct Media {
78 pub playlist: MediaPlaylist,
79}
80
81impl Master {
82 pub fn filter_fps(&mut self, rate: Option<f64>) -> &mut Self {
84 if let Some(r) = rate {
85 self.playlist.variants.retain(|v| v.frame_rate == Some(r));
86 }
87 self
88 }
89
90 pub fn filter_bandwidth(&mut self, min: Option<u64>, max: Option<u64>) -> &mut Self {
98 let min = min.unwrap_or(0);
99 let max = max.unwrap_or(u64::MAX);
100
101 self.playlist
102 .variants
103 .retain(|v| v.bandwidth >= min && v.bandwidth <= max);
104 self
105 }
106
107 pub fn first_variant_by_index(&mut self, index: Option<u64>) -> &mut Self {
117 if let Some(i) = index {
118 if i as usize <= self.playlist.variants.len() {
119 self.playlist.variants.swap(0, i.try_into().unwrap());
120 }
121 }
122 self
123 }
124
125 pub fn first_variant_by_closest_bandwidth(
132 &mut self,
133 closest_bandwidth: Option<u64>,
134 ) -> &mut Self {
135 if let Some(c) = closest_bandwidth {
136 let (idx, _) = self
137 .playlist
138 .variants
139 .iter()
140 .enumerate()
141 .min_by_key(|(_, v)| (c as i64 - v.bandwidth as i64).abs())
142 .unwrap();
143 let fv = self.playlist.variants.remove(idx);
144 self.playlist.variants.insert(0, fv);
145 }
146 self
147 }
148}
149
150impl Media {
151 pub fn filter_dvr(&mut self, seconds: Option<u64>) -> &mut Self {
155 let mut acc = 0;
156 let total_segments = self.playlist.segments.len();
157
158 if let Some(s) = seconds {
159 self.playlist.segments = self
160 .playlist
161 .segments
162 .iter()
163 .rev()
164 .take_while(|segment| {
165 acc += segment.duration as u64;
166 acc <= s
167 })
168 .cloned()
169 .collect();
170 self.playlist.media_sequence += (total_segments - self.playlist.segments.len()) as u64;
171 }
172 self
173 }
174
175 pub fn trim(&mut self, start: Option<u64>, end: Option<u64>) -> &mut Self {
178 let start = start.unwrap_or(0);
179 let end = end.unwrap_or_else(|| self.playlist.segments.len().try_into().unwrap());
180
181 let segments = &self.playlist.segments[start as usize..end as usize];
182 let total_segments = self.playlist.segments.len();
183 self.playlist.segments = segments.to_vec();
184 self.playlist.media_sequence += (total_segments - self.playlist.segments.len()) as u64;
185 self
186 }
187}
188
189#[cfg(test)]
190mod tests {
191 use super::*;
192 use std::io::Read;
193
194 fn build_master() -> Master {
195 let mut file = std::fs::File::open("manifests/master.m3u8").unwrap();
196 let mut content: Vec<u8> = Vec::new();
197 file.read_to_end(&mut content).unwrap();
198
199 let (_, master_playlist) = m3u8_rs::parse_master_playlist(&content).unwrap();
200 Master {
201 playlist: master_playlist,
202 }
203 }
204
205 fn build_media() -> Media {
206 let mut file = std::fs::File::open("manifests/media.m3u8").unwrap();
207 let mut content: Vec<u8> = Vec::new();
208 file.read_to_end(&mut content).unwrap();
209
210 let (_, media_playlist) = m3u8_rs::parse_media_playlist(&content).unwrap();
211 Media {
212 playlist: media_playlist,
213 }
214 }
215
216 #[test]
217 fn filter_60_fps() {
218 let mut master = build_master();
219 master.filter_fps(Some(60.0));
220
221 assert_eq!(master.playlist.variants.len(), 2);
222 }
223
224 #[test]
225 fn filter_min_bandwidth() {
226 let mut master = build_master();
227
228 master.filter_bandwidth(Some(800000), None);
229
230 assert_eq!(master.playlist.variants.len(), 3);
231 }
232
233 #[test]
234 fn filter_max_bandwidth() {
235 let mut master = build_master();
236
237 master.filter_bandwidth(None, Some(800000));
238
239 assert_eq!(master.playlist.variants.len(), 6);
240 }
241
242 #[test]
243 fn filter_min_and_max_bandwidth() {
244 let mut master = build_master();
245
246 master.filter_bandwidth(Some(800000), Some(2000000));
247
248 assert_eq!(master.playlist.variants.len(), 3);
249 }
250
251 #[test]
252 fn set_first_variant_by_index() {
253 let mut master = build_master();
254
255 master.first_variant_by_index(Some(1));
256
257 assert_eq!(master.playlist.variants[0].bandwidth, 800000);
258 assert_eq!(master.playlist.variants[1].bandwidth, 600000);
259 }
260
261 #[test]
262 fn set_first_variant_by_out_of_bounds_index() {
263 let mut master = build_master();
264
265 master.first_variant_by_index(Some(100));
266
267 assert_eq!(master.playlist.variants[0].bandwidth, 600000);
268 assert_eq!(master.playlist.variants[1].bandwidth, 800000);
269 }
270
271 #[test]
272 fn set_first_variant_by_closest_bandwidth() {
273 let mut master = build_master();
274
275 master.first_variant_by_closest_bandwidth(Some(1650000));
276 assert_eq!(master.playlist.variants[0].bandwidth, 1500000);
277 assert_eq!(master.playlist.variants[1].bandwidth, 600000);
278 }
279
280 #[test]
281 fn filter_dvr_with_short_duration() {
282 let mut media = build_media();
283
284 media.filter_dvr(Some(15));
285
286 assert_eq!(media.playlist.segments.len(), 3);
287 assert_eq!(media.playlist.media_sequence, 320035373);
288 }
289
290 #[test]
291 fn filter_dvr_with_long_duration() {
292 let mut media = build_media();
293
294 media.filter_dvr(Some(u64::MAX));
295
296 assert_eq!(media.playlist.segments.len(), 20);
297 assert_eq!(media.playlist.media_sequence, 320035356);
298 }
299
300 #[test]
301 fn trim_media_playlist_with_start_only() {
302 let mut media = build_media();
303
304 media.trim(Some(5), None);
305
306 assert_eq!(media.playlist.segments.len(), 15);
307 assert_eq!(media.playlist.media_sequence, 320035361);
308 }
309
310 #[test]
311 fn trim_media_playlist_with_end_only() {
312 let mut media = build_media();
313
314 media.trim(None, Some(5));
315
316 assert_eq!(media.playlist.segments.len(), 5);
317 assert_eq!(media.playlist.media_sequence, 320035371);
318 }
319
320 #[test]
321 fn trim_media_playlist_with_start_and_end() {
322 let mut media = build_media();
323
324 media.trim(Some(5), Some(18));
325
326 assert_eq!(media.playlist.segments.len(), 13);
327 assert_eq!(media.playlist.media_sequence, 320035363);
328 }
329}