1
0
Fork 0
mirror of https://github.com/sile/hls_m3u8.git synced 2024-05-18 16:28:20 +00:00

Add stable-vec #52

This commit is contained in:
Luro02 2020-04-18 12:50:45 +02:00 committed by Lucas
parent 7e6da6224d
commit 096957b167
4 changed files with 62 additions and 54 deletions

View file

@ -28,6 +28,7 @@ thiserror = "1.0"
derive_more = "0.99" derive_more = "0.99"
shorthand = "0.1" shorthand = "0.1"
strum = { version = "0.17", features = ["derive"] } strum = { version = "0.17", features = ["derive"] }
stable-vec = { version = "0.4" }
[dev-dependencies] [dev-dependencies]
pretty_assertions = "0.6" pretty_assertions = "0.6"

View file

@ -136,4 +136,5 @@ mod media_segment;
mod traits; mod traits;
pub use error::Result; pub use error::Result;
pub use stable_vec;
pub use traits::*; pub use traits::*;

View file

@ -1,9 +1,10 @@
use std::collections::{BTreeMap, HashSet}; use std::collections::HashSet;
use std::fmt; use std::fmt;
use std::str::FromStr; use std::str::FromStr;
use std::time::Duration; use std::time::Duration;
use derive_builder::Builder; use derive_builder::Builder;
use stable_vec::StableVec;
use crate::line::{Line, Lines, Tag}; use crate::line::{Line, Lines, Tag};
use crate::media_segment::MediaSegment; use crate::media_segment::MediaSegment;
@ -19,7 +20,7 @@ use crate::utils::{tag, BoolExt};
use crate::{Error, RequiredVersion}; use crate::{Error, RequiredVersion};
/// Media playlist. /// Media playlist.
#[derive(Builder, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Builder, Debug, Clone, PartialEq, Eq)]
#[builder(build_fn(skip), setter(strip_option))] #[builder(build_fn(skip), setter(strip_option))]
#[non_exhaustive] #[non_exhaustive]
pub struct MediaPlaylist { pub struct MediaPlaylist {
@ -105,7 +106,7 @@ pub struct MediaPlaylist {
/// ///
/// This field is required. /// This field is required.
#[builder(setter(custom))] #[builder(setter(custom))]
pub segments: BTreeMap<usize, MediaSegment>, pub segments: StableVec<MediaSegment>,
/// The allowable excess duration of each media segment in the /// The allowable excess duration of each media segment in the
/// associated playlist. /// associated playlist.
/// ///
@ -225,17 +226,15 @@ impl MediaPlaylistBuilder {
/// Adds a media segment to the resulting playlist and assigns the next free /// Adds a media segment to the resulting playlist and assigns the next free
/// [`MediaSegment::number`] to the segment. /// [`MediaSegment::number`] to the segment.
pub fn push_segment(&mut self, segment: MediaSegment) -> &mut Self { pub fn push_segment(&mut self, segment: MediaSegment) -> &mut Self {
let segments = self.segments.get_or_insert_with(BTreeMap::new); let segments = self.segments.get_or_insert_with(StableVec::new);
let number = { if segment.explicit_number {
if segment.explicit_number { segments.reserve_for(segment.number);
segment.number segments.insert(segment.number, segment);
} else { } else {
segments.keys().last().copied().unwrap_or(0) + 1 segments.push(segment);
} }
};
segments.insert(number, segment);
self self
} }
@ -255,11 +254,22 @@ impl MediaPlaylistBuilder {
/// will be present in the final media playlist and the following is only /// will be present in the final media playlist and the following is only
/// possible if the segment is marked with `ExtXDiscontinuity`. /// possible if the segment is marked with `ExtXDiscontinuity`.
pub fn segments(&mut self, segments: Vec<MediaSegment>) -> &mut Self { pub fn segments(&mut self, segments: Vec<MediaSegment>) -> &mut Self {
// media segments are numbered starting at either 0 or the discontinuity let mut vec = StableVec::<MediaSegment>::with_capacity(segments.len());
// sequence, but it might not be available at the moment. let mut remaining = Vec::with_capacity(segments.len());
//
// -> final numbering will be applied in the build function for segment in segments {
self.segments = Some(segments.into_iter().enumerate().collect()); if segment.explicit_number {
vec.insert(segment.number, segment);
} else {
remaining.push(segment);
}
}
for segment in remaining {
vec.push(segment);
}
self.segments = Some(vec);
self self
} }
@ -274,20 +284,14 @@ impl MediaPlaylistBuilder {
let sequence_number = self.media_sequence.unwrap_or(0); let sequence_number = self.media_sequence.unwrap_or(0);
let segments = self let mut segments = self
.segments .segments
.as_ref() .clone()
.ok_or_else(|| "missing field `segments`".to_string())?; .ok_or_else(|| "missing field `segments`".to_string())?;
// insert all explictly numbered segments into the result
let mut result_segments = segments
.iter()
.filter_map(|(_, s)| s.explicit_number.athen(|| (s.number, s.clone())))
.collect::<BTreeMap<_, _>>();
// no segment should exist before the sequence_number // no segment should exist before the sequence_number
if let Some(first_segment) = result_segments.keys().min() { if let Some(first_segment) = segments.find_first() {
if sequence_number > *first_segment { if sequence_number > first_segment.number && first_segment.explicit_number {
return Err(format!( return Err(format!(
"there should be no segment ({}) before the sequence_number ({})", "there should be no segment ({}) before the sequence_number ({})",
first_segment, sequence_number, first_segment, sequence_number,
@ -295,20 +299,14 @@ impl MediaPlaylistBuilder {
} }
} }
let mut position = sequence_number;
let mut previous_range: Option<ExtXByteRange> = None; let mut previous_range: Option<ExtXByteRange> = None;
for segment in segments for (i, segment) in segments.iter_mut() {
.iter() // assign the correct number to all implcitly numbered segments:
.filter_map(|(_, s)| if s.explicit_number { None } else { Some(s) }) if !segment.explicit_number {
{ segment.number = i + sequence_number;
while result_segments.contains_key(&position) {
position += 1;
} }
let mut segment = segment.clone();
segment.number = position;
// add the segment number as iv, if the iv is missing: // add the segment number as iv, if the iv is missing:
for key in &mut segment.keys { for key in &mut segment.keys {
if let ExtXKey(Some(DecryptionKey { if let ExtXKey(Some(DecryptionKey {
@ -340,21 +338,17 @@ impl MediaPlaylistBuilder {
previous_range = segment.byte_range; previous_range = segment.byte_range;
} }
result_segments.insert(segment.number, segment);
position += 1;
} }
let mut previous_n = None; // TODO: can segments be missing?
if !segments.is_compact() {
for n in result_segments.keys() { // find the missing segment by iterating through all segments:
if let Some(previous_n) = previous_n { // let missing = segments
if previous_n + 1 != *n { // .iter()
return Err(format!("missing segment ({})", previous_n + 1)); // .enumerate()
} // .find_map(|(i, e)| e.is_none().athen(i))
} // .unwrap();
return Err(format!("a segment is missing"));
previous_n = Some(n);
} }
Ok(MediaPlaylist { Ok(MediaPlaylist {
@ -368,7 +362,7 @@ impl MediaPlaylistBuilder {
has_independent_segments: self.has_independent_segments.unwrap_or(false), has_independent_segments: self.has_independent_segments.unwrap_or(false),
start: self.start.unwrap_or(None), start: self.start.unwrap_or(None),
has_end_list: self.has_end_list.unwrap_or(false), has_end_list: self.has_end_list.unwrap_or(false),
segments: result_segments, segments,
allowable_excess_duration: self allowable_excess_duration: self
.allowable_excess_duration .allowable_excess_duration
.unwrap_or_else(|| Duration::from_secs(0)), .unwrap_or_else(|| Duration::from_secs(0)),
@ -816,9 +810,9 @@ mod tests {
.build() .build()
.unwrap(); .unwrap();
let mut segments = playlist.segments.into_iter().map(|(k, v)| (k, v.number)); let mut segments = playlist.segments.into_iter().map(|(k, v)| (k, v.number));
assert_eq!(segments.next(), Some((2680, 2680))); assert_eq!(segments.next(), Some((0, 2680)));
assert_eq!(segments.next(), Some((2681, 2681))); assert_eq!(segments.next(), Some((1, 2681)));
assert_eq!(segments.next(), Some((2682, 2682))); assert_eq!(segments.next(), Some((2, 2682)));
assert_eq!(segments.next(), None); assert_eq!(segments.next(), None);
} }

View file

@ -1,5 +1,7 @@
use std::collections::{BTreeMap, HashMap}; use std::collections::{BTreeMap, HashMap};
use stable_vec::StableVec;
use crate::types::{DecryptionKey, ProtocolVersion}; use crate::types::{DecryptionKey, ProtocolVersion};
mod private { mod private {
@ -108,6 +110,16 @@ impl<K, V: RequiredVersion, S> RequiredVersion for HashMap<K, V, S> {
} }
} }
impl<T: RequiredVersion> RequiredVersion for StableVec<T> {
fn required_version(&self) -> ProtocolVersion {
self.values()
.map(RequiredVersion::required_version)
.max()
// return ProtocolVersion::V1, if the iterator is empty:
.unwrap_or_default()
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;