mirror of
https://github.com/sile/hls_m3u8.git
synced 2024-12-22 20:16:27 +00:00
Add stable-vec
#52
This commit is contained in:
parent
7e6da6224d
commit
096957b167
4 changed files with 62 additions and 54 deletions
|
@ -28,6 +28,7 @@ thiserror = "1.0"
|
|||
derive_more = "0.99"
|
||||
shorthand = "0.1"
|
||||
strum = { version = "0.17", features = ["derive"] }
|
||||
stable-vec = { version = "0.4" }
|
||||
|
||||
[dev-dependencies]
|
||||
pretty_assertions = "0.6"
|
||||
|
|
|
@ -136,4 +136,5 @@ mod media_segment;
|
|||
mod traits;
|
||||
|
||||
pub use error::Result;
|
||||
pub use stable_vec;
|
||||
pub use traits::*;
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
use std::collections::{BTreeMap, HashSet};
|
||||
use std::collections::HashSet;
|
||||
use std::fmt;
|
||||
use std::str::FromStr;
|
||||
use std::time::Duration;
|
||||
|
||||
use derive_builder::Builder;
|
||||
use stable_vec::StableVec;
|
||||
|
||||
use crate::line::{Line, Lines, Tag};
|
||||
use crate::media_segment::MediaSegment;
|
||||
|
@ -19,7 +20,7 @@ use crate::utils::{tag, BoolExt};
|
|||
use crate::{Error, RequiredVersion};
|
||||
|
||||
/// Media playlist.
|
||||
#[derive(Builder, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
#[derive(Builder, Debug, Clone, PartialEq, Eq)]
|
||||
#[builder(build_fn(skip), setter(strip_option))]
|
||||
#[non_exhaustive]
|
||||
pub struct MediaPlaylist {
|
||||
|
@ -105,7 +106,7 @@ pub struct MediaPlaylist {
|
|||
///
|
||||
/// This field is required.
|
||||
#[builder(setter(custom))]
|
||||
pub segments: BTreeMap<usize, MediaSegment>,
|
||||
pub segments: StableVec<MediaSegment>,
|
||||
/// The allowable excess duration of each media segment in the
|
||||
/// associated playlist.
|
||||
///
|
||||
|
@ -225,17 +226,15 @@ impl MediaPlaylistBuilder {
|
|||
/// Adds a media segment to the resulting playlist and assigns the next free
|
||||
/// [`MediaSegment::number`] to the segment.
|
||||
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 {
|
||||
segment.number
|
||||
} else {
|
||||
segments.keys().last().copied().unwrap_or(0) + 1
|
||||
}
|
||||
};
|
||||
if segment.explicit_number {
|
||||
segments.reserve_for(segment.number);
|
||||
segments.insert(segment.number, segment);
|
||||
} else {
|
||||
segments.push(segment);
|
||||
}
|
||||
|
||||
segments.insert(number, segment);
|
||||
self
|
||||
}
|
||||
|
||||
|
@ -255,11 +254,22 @@ impl MediaPlaylistBuilder {
|
|||
/// will be present in the final media playlist and the following is only
|
||||
/// possible if the segment is marked with `ExtXDiscontinuity`.
|
||||
pub fn segments(&mut self, segments: Vec<MediaSegment>) -> &mut Self {
|
||||
// media segments are numbered starting at either 0 or the discontinuity
|
||||
// sequence, but it might not be available at the moment.
|
||||
//
|
||||
// -> final numbering will be applied in the build function
|
||||
self.segments = Some(segments.into_iter().enumerate().collect());
|
||||
let mut vec = StableVec::<MediaSegment>::with_capacity(segments.len());
|
||||
let mut remaining = Vec::with_capacity(segments.len());
|
||||
|
||||
for segment in segments {
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -274,20 +284,14 @@ impl MediaPlaylistBuilder {
|
|||
|
||||
let sequence_number = self.media_sequence.unwrap_or(0);
|
||||
|
||||
let segments = self
|
||||
let mut segments = self
|
||||
.segments
|
||||
.as_ref()
|
||||
.clone()
|
||||
.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
|
||||
if let Some(first_segment) = result_segments.keys().min() {
|
||||
if sequence_number > *first_segment {
|
||||
if let Some(first_segment) = segments.find_first() {
|
||||
if sequence_number > first_segment.number && first_segment.explicit_number {
|
||||
return Err(format!(
|
||||
"there should be no segment ({}) before the sequence_number ({})",
|
||||
first_segment, sequence_number,
|
||||
|
@ -295,20 +299,14 @@ impl MediaPlaylistBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
let mut position = sequence_number;
|
||||
let mut previous_range: Option<ExtXByteRange> = None;
|
||||
|
||||
for segment in segments
|
||||
.iter()
|
||||
.filter_map(|(_, s)| if s.explicit_number { None } else { Some(s) })
|
||||
{
|
||||
while result_segments.contains_key(&position) {
|
||||
position += 1;
|
||||
for (i, segment) in segments.iter_mut() {
|
||||
// assign the correct number to all implcitly numbered segments:
|
||||
if !segment.explicit_number {
|
||||
segment.number = i + sequence_number;
|
||||
}
|
||||
|
||||
let mut segment = segment.clone();
|
||||
segment.number = position;
|
||||
|
||||
// add the segment number as iv, if the iv is missing:
|
||||
for key in &mut segment.keys {
|
||||
if let ExtXKey(Some(DecryptionKey {
|
||||
|
@ -340,21 +338,17 @@ impl MediaPlaylistBuilder {
|
|||
|
||||
previous_range = segment.byte_range;
|
||||
}
|
||||
|
||||
result_segments.insert(segment.number, segment);
|
||||
position += 1;
|
||||
}
|
||||
|
||||
let mut previous_n = None;
|
||||
|
||||
for n in result_segments.keys() {
|
||||
if let Some(previous_n) = previous_n {
|
||||
if previous_n + 1 != *n {
|
||||
return Err(format!("missing segment ({})", previous_n + 1));
|
||||
}
|
||||
}
|
||||
|
||||
previous_n = Some(n);
|
||||
// TODO: can segments be missing?
|
||||
if !segments.is_compact() {
|
||||
// find the missing segment by iterating through all segments:
|
||||
// let missing = segments
|
||||
// .iter()
|
||||
// .enumerate()
|
||||
// .find_map(|(i, e)| e.is_none().athen(i))
|
||||
// .unwrap();
|
||||
return Err(format!("a segment is missing"));
|
||||
}
|
||||
|
||||
Ok(MediaPlaylist {
|
||||
|
@ -368,7 +362,7 @@ impl MediaPlaylistBuilder {
|
|||
has_independent_segments: self.has_independent_segments.unwrap_or(false),
|
||||
start: self.start.unwrap_or(None),
|
||||
has_end_list: self.has_end_list.unwrap_or(false),
|
||||
segments: result_segments,
|
||||
segments,
|
||||
allowable_excess_duration: self
|
||||
.allowable_excess_duration
|
||||
.unwrap_or_else(|| Duration::from_secs(0)),
|
||||
|
@ -816,9 +810,9 @@ mod tests {
|
|||
.build()
|
||||
.unwrap();
|
||||
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((2681, 2681)));
|
||||
assert_eq!(segments.next(), Some((2682, 2682)));
|
||||
assert_eq!(segments.next(), Some((0, 2680)));
|
||||
assert_eq!(segments.next(), Some((1, 2681)));
|
||||
assert_eq!(segments.next(), Some((2, 2682)));
|
||||
assert_eq!(segments.next(), None);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
use std::collections::{BTreeMap, HashMap};
|
||||
|
||||
use stable_vec::StableVec;
|
||||
|
||||
use crate::types::{DecryptionKey, ProtocolVersion};
|
||||
|
||||
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)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
|
Loading…
Reference in a new issue