use m3u8_rs::{MasterPlaylist, MediaPlaylist, Playlist};
pub fn load_master(content: &[u8]) -> Result<MasterPlaylist, String> {
match m3u8_rs::parse_playlist(content) {
Result::Ok((_, Playlist::MasterPlaylist(pl))) => Ok(pl),
Result::Ok((_, Playlist::MediaPlaylist(_))) => Err("must be a master playlist".to_string()),
Result::Err(e) => Err(e.to_string()),
}
}
pub fn load_media(content: &[u8]) -> Result<MediaPlaylist, String> {
match m3u8_rs::parse_playlist(content) {
Result::Ok((_, Playlist::MediaPlaylist(pl))) => Ok(pl),
Result::Ok((_, Playlist::MasterPlaylist(_))) => Err("must be a media playlist".to_string()),
Result::Err(e) => Err(e.to_string()),
}
}
pub struct Master {
pub playlist: MasterPlaylist,
}
pub struct Media {
pub playlist: MediaPlaylist,
}
impl Master {
pub fn filter_fps(&mut self, rate: Option<f64>) -> &mut Self {
if let Some(r) = rate {
self.playlist.variants.retain(|v| v.frame_rate == Some(r));
}
self
}
pub fn filter_bandwidth(&mut self, min: Option<u64>, max: Option<u64>) -> &mut Self {
let min = min.unwrap_or(0);
let max = max.unwrap_or(u64::MAX);
self.playlist
.variants
.retain(|v| v.bandwidth >= min && v.bandwidth <= max);
self
}
pub fn first_variant_by_index(&mut self, index: Option<u64>) -> &mut Self {
if let Some(i) = index {
if i as usize <= self.playlist.variants.len() {
self.playlist.variants.swap(0, i.try_into().unwrap());
}
}
self
}
pub fn first_variant_by_closest_bandwidth(
&mut self,
closest_bandwidth: Option<u64>,
) -> &mut Self {
if let Some(c) = closest_bandwidth {
let (idx, _) = self
.playlist
.variants
.iter()
.enumerate()
.min_by_key(|(_, v)| (c as i64 - v.bandwidth as i64).abs())
.unwrap();
let fv = self.playlist.variants.remove(idx);
self.playlist.variants.insert(0, fv);
}
self
}
}
impl Media {
pub fn filter_dvr(&mut self, seconds: Option<u64>) -> &mut Self {
let mut acc = 0;
let total_segments = self.playlist.segments.len();
if let Some(s) = seconds {
self.playlist.segments = self
.playlist
.segments
.iter()
.rev()
.take_while(|segment| {
acc += segment.duration as u64;
acc <= s
})
.cloned()
.collect();
self.playlist.media_sequence += (total_segments - self.playlist.segments.len()) as u64;
}
self
}
pub fn trim(&mut self, start: Option<u64>, end: Option<u64>) -> &mut Self {
let start = start.unwrap_or(0);
let end = end.unwrap_or_else(|| self.playlist.segments.len().try_into().unwrap());
let segments = &self.playlist.segments[start as usize..end as usize];
let total_segments = self.playlist.segments.len();
self.playlist.segments = segments.to_vec();
self.playlist.media_sequence += (total_segments - self.playlist.segments.len()) as u64;
self
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::io::Read;
fn build_master() -> Master {
let mut file = std::fs::File::open("manifests/master.m3u8").unwrap();
let mut content: Vec<u8> = Vec::new();
file.read_to_end(&mut content).unwrap();
let (_, master_playlist) = m3u8_rs::parse_master_playlist(&content).unwrap();
Master {
playlist: master_playlist,
}
}
fn build_media() -> Media {
let mut file = std::fs::File::open("manifests/media.m3u8").unwrap();
let mut content: Vec<u8> = Vec::new();
file.read_to_end(&mut content).unwrap();
let (_, media_playlist) = m3u8_rs::parse_media_playlist(&content).unwrap();
Media {
playlist: media_playlist,
}
}
#[test]
fn filter_60_fps() {
let mut master = build_master();
master.filter_fps(Some(60.0));
assert_eq!(master.playlist.variants.len(), 2);
}
#[test]
fn filter_min_bandwidth() {
let mut master = build_master();
master.filter_bandwidth(Some(800000), None);
assert_eq!(master.playlist.variants.len(), 3);
}
#[test]
fn filter_max_bandwidth() {
let mut master = build_master();
master.filter_bandwidth(None, Some(800000));
assert_eq!(master.playlist.variants.len(), 6);
}
#[test]
fn filter_min_and_max_bandwidth() {
let mut master = build_master();
master.filter_bandwidth(Some(800000), Some(2000000));
assert_eq!(master.playlist.variants.len(), 3);
}
#[test]
fn set_first_variant_by_index() {
let mut master = build_master();
master.first_variant_by_index(Some(1));
assert_eq!(master.playlist.variants[0].bandwidth, 800000);
assert_eq!(master.playlist.variants[1].bandwidth, 600000);
}
#[test]
fn set_first_variant_by_out_of_bounds_index() {
let mut master = build_master();
master.first_variant_by_index(Some(100));
assert_eq!(master.playlist.variants[0].bandwidth, 600000);
assert_eq!(master.playlist.variants[1].bandwidth, 800000);
}
#[test]
fn set_first_variant_by_closest_bandwidth() {
let mut master = build_master();
master.first_variant_by_closest_bandwidth(Some(1650000));
assert_eq!(master.playlist.variants[0].bandwidth, 1500000);
assert_eq!(master.playlist.variants[1].bandwidth, 600000);
}
#[test]
fn filter_dvr_with_short_duration() {
let mut media = build_media();
media.filter_dvr(Some(15));
assert_eq!(media.playlist.segments.len(), 3);
assert_eq!(media.playlist.media_sequence, 320035373);
}
#[test]
fn filter_dvr_with_long_duration() {
let mut media = build_media();
media.filter_dvr(Some(u64::MAX));
assert_eq!(media.playlist.segments.len(), 20);
assert_eq!(media.playlist.media_sequence, 320035356);
}
#[test]
fn trim_media_playlist_with_start_only() {
let mut media = build_media();
media.trim(Some(5), None);
assert_eq!(media.playlist.segments.len(), 15);
assert_eq!(media.playlist.media_sequence, 320035361);
}
#[test]
fn trim_media_playlist_with_end_only() {
let mut media = build_media();
media.trim(None, Some(5));
assert_eq!(media.playlist.segments.len(), 5);
assert_eq!(media.playlist.media_sequence, 320035371);
}
#[test]
fn trim_media_playlist_with_start_and_end() {
let mut media = build_media();
media.trim(Some(5), Some(18));
assert_eq!(media.playlist.segments.len(), 13);
assert_eq!(media.playlist.media_sequence, 320035363);
}
}