mirror of
https://github.com/sile/hls_m3u8.git
synced 2025-01-08 19:25:24 +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"
|
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"
|
||||||
|
|
|
@ -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::*;
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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::*;
|
||||||
|
|
Loading…
Reference in a new issue