mirror of
https://github.com/sile/hls_m3u8.git
synced 2024-11-21 14:50:59 +00:00
remove _tag
suffix from MediaSegment fields
This commit is contained in:
parent
8cced1ac53
commit
b2c997d04d
5 changed files with 92 additions and 93 deletions
|
@ -124,7 +124,7 @@ impl MediaPlaylistBuilder {
|
|||
if let Some(segments) = &self.segments {
|
||||
for s in segments {
|
||||
// CHECK: `#EXT-X-TARGETDURATION`
|
||||
let segment_duration = s.inf_tag().duration();
|
||||
let segment_duration = s.inf().duration();
|
||||
let rounded_segment_duration = {
|
||||
if segment_duration.subsec_nanos() < 500_000_000 {
|
||||
Duration::from_secs(segment_duration.as_secs())
|
||||
|
@ -152,7 +152,7 @@ impl MediaPlaylistBuilder {
|
|||
}
|
||||
|
||||
// CHECK: `#EXT-X-BYTE-RANGE`
|
||||
if let Some(tag) = s.byte_range_tag() {
|
||||
if let Some(tag) = s.byte_range() {
|
||||
if tag.to_range().start().is_none() {
|
||||
let last_uri = last_range_uri.ok_or_else(Error::invalid_input)?;
|
||||
if last_uri != s.uri() {
|
||||
|
@ -286,7 +286,7 @@ fn parse_media_playlist(
|
|||
let mut has_discontinuity_tag = false;
|
||||
let mut unknown_tags = vec![];
|
||||
|
||||
let mut available_key_tags: Vec<crate::tags::ExtXKey> = vec![];
|
||||
let mut available_keys: Vec<crate::tags::ExtXKey> = vec![];
|
||||
|
||||
for line in Lines::from(input) {
|
||||
match line? {
|
||||
|
@ -294,25 +294,25 @@ fn parse_media_playlist(
|
|||
match tag {
|
||||
Tag::ExtInf(t) => {
|
||||
has_partial_segment = true;
|
||||
segment.inf_tag(t);
|
||||
segment.inf(t);
|
||||
}
|
||||
Tag::ExtXByteRange(t) => {
|
||||
has_partial_segment = true;
|
||||
segment.byte_range_tag(t);
|
||||
segment.byte_range(t);
|
||||
}
|
||||
Tag::ExtXDiscontinuity(t) => {
|
||||
has_discontinuity_tag = true;
|
||||
has_partial_segment = true;
|
||||
segment.discontinuity_tag(t);
|
||||
segment.discontinuity(t);
|
||||
}
|
||||
Tag::ExtXKey(t) => {
|
||||
has_partial_segment = true;
|
||||
if available_key_tags.is_empty() {
|
||||
if available_keys.is_empty() {
|
||||
// An ExtXKey applies to every MediaSegment and to every Media
|
||||
// Initialization Section declared by an EXT-X-MAP tag, that appears
|
||||
// between it and the next EXT-X-KEY tag in the Playlist file with the
|
||||
// same KEYFORMAT attribute (or the end of the Playlist file).
|
||||
available_key_tags = available_key_tags
|
||||
available_keys = available_keys
|
||||
.into_iter()
|
||||
.map(|k| {
|
||||
if t.key_format() == k.key_format() {
|
||||
|
@ -323,22 +323,22 @@ fn parse_media_playlist(
|
|||
})
|
||||
.collect();
|
||||
} else {
|
||||
available_key_tags.push(t);
|
||||
available_keys.push(t);
|
||||
}
|
||||
}
|
||||
Tag::ExtXMap(mut t) => {
|
||||
has_partial_segment = true;
|
||||
|
||||
t.set_keys(available_key_tags.clone());
|
||||
segment.map_tag(t);
|
||||
t.set_keys(available_keys.clone());
|
||||
segment.map(t);
|
||||
}
|
||||
Tag::ExtXProgramDateTime(t) => {
|
||||
has_partial_segment = true;
|
||||
segment.program_date_time_tag(t);
|
||||
segment.program_date_time(t);
|
||||
}
|
||||
Tag::ExtXDateRange(t) => {
|
||||
has_partial_segment = true;
|
||||
segment.date_range_tag(t);
|
||||
segment.date_range(t);
|
||||
}
|
||||
Tag::ExtXTargetDuration(t) => {
|
||||
builder.target_duration(t);
|
||||
|
@ -350,9 +350,11 @@ fn parse_media_playlist(
|
|||
if segments.is_empty() {
|
||||
return Err(Error::invalid_input());
|
||||
}
|
||||
|
||||
if has_discontinuity_tag {
|
||||
return Err(Error::invalid_input());
|
||||
}
|
||||
|
||||
builder.discontinuity_sequence(t);
|
||||
}
|
||||
Tag::ExtXEndList(t) => {
|
||||
|
@ -386,7 +388,7 @@ fn parse_media_playlist(
|
|||
}
|
||||
Line::Uri(uri) => {
|
||||
segment.uri(uri);
|
||||
segment.keys(available_key_tags.clone());
|
||||
segment.keys(available_keys.clone());
|
||||
segments.push(segment.build().map_err(Error::builder)?);
|
||||
segment = MediaSegment::builder();
|
||||
has_partial_segment = false;
|
||||
|
|
|
@ -19,22 +19,22 @@ pub struct MediaSegment {
|
|||
keys: Vec<ExtXKey>,
|
||||
/// The [`ExtXMap`] tag associated with the media segment.
|
||||
#[builder(default)]
|
||||
map_tag: Option<ExtXMap>,
|
||||
map: Option<ExtXMap>,
|
||||
/// The [`ExtXByteRange`] tag associated with the [`MediaSegment`].
|
||||
#[builder(default)]
|
||||
byte_range_tag: Option<ExtXByteRange>,
|
||||
byte_range: Option<ExtXByteRange>,
|
||||
/// The [`ExtXDateRange`] tag associated with the media segment.
|
||||
#[builder(default)]
|
||||
date_range_tag: Option<ExtXDateRange>,
|
||||
date_range: Option<ExtXDateRange>,
|
||||
/// The [`ExtXDiscontinuity`] tag associated with the media segment.
|
||||
#[builder(default)]
|
||||
discontinuity_tag: Option<ExtXDiscontinuity>,
|
||||
discontinuity: Option<ExtXDiscontinuity>,
|
||||
/// The [`ExtXProgramDateTime`] tag associated with the media
|
||||
/// segment.
|
||||
#[builder(default)]
|
||||
program_date_time_tag: Option<ExtXProgramDateTime>,
|
||||
program_date_time: Option<ExtXProgramDateTime>,
|
||||
/// The [`ExtInf`] tag associated with the [`MediaSegment`].
|
||||
inf_tag: ExtInf,
|
||||
inf: ExtInf,
|
||||
/// The `URI` of the [`MediaSegment`].
|
||||
#[shorthand(enable(into))]
|
||||
uri: String,
|
||||
|
@ -47,12 +47,13 @@ impl MediaSegment {
|
|||
|
||||
impl MediaSegmentBuilder {
|
||||
/// Pushes an [`ExtXKey`] tag.
|
||||
pub fn push_key_tag<VALUE: Into<ExtXKey>>(&mut self, value: VALUE) -> &mut Self {
|
||||
if let Some(key_tags) = &mut self.keys {
|
||||
key_tags.push(value.into());
|
||||
pub fn push_key<VALUE: Into<ExtXKey>>(&mut self, value: VALUE) -> &mut Self {
|
||||
if let Some(keys) = &mut self.keys {
|
||||
keys.push(value.into());
|
||||
} else {
|
||||
self.keys = Some(vec![value.into()]);
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
}
|
||||
|
@ -63,27 +64,27 @@ impl fmt::Display for MediaSegment {
|
|||
writeln!(f, "{}", value)?;
|
||||
}
|
||||
|
||||
if let Some(value) = &self.map_tag {
|
||||
if let Some(value) = &self.map {
|
||||
writeln!(f, "{}", value)?;
|
||||
}
|
||||
|
||||
if let Some(value) = &self.byte_range_tag {
|
||||
if let Some(value) = &self.byte_range {
|
||||
writeln!(f, "{}", value)?;
|
||||
}
|
||||
|
||||
if let Some(value) = &self.date_range_tag {
|
||||
if let Some(value) = &self.date_range {
|
||||
writeln!(f, "{}", value)?;
|
||||
}
|
||||
|
||||
if let Some(value) = &self.discontinuity_tag {
|
||||
if let Some(value) = &self.discontinuity {
|
||||
writeln!(f, "{}", value)?;
|
||||
}
|
||||
|
||||
if let Some(value) = &self.program_date_time_tag {
|
||||
if let Some(value) = &self.program_date_time {
|
||||
writeln!(f, "{}", value)?;
|
||||
}
|
||||
|
||||
writeln!(f, "{}", self.inf_tag)?; // TODO: there might be a `,` missing
|
||||
writeln!(f, "{}", self.inf)?; // TODO: there might be a `,` missing
|
||||
writeln!(f, "{}", self.uri)?;
|
||||
Ok(())
|
||||
}
|
||||
|
@ -93,12 +94,12 @@ impl RequiredVersion for MediaSegment {
|
|||
fn required_version(&self) -> ProtocolVersion {
|
||||
required_version![
|
||||
self.keys,
|
||||
self.map_tag,
|
||||
self.byte_range_tag,
|
||||
self.date_range_tag,
|
||||
self.discontinuity_tag,
|
||||
self.program_date_time_tag,
|
||||
self.inf_tag
|
||||
self.map,
|
||||
self.byte_range,
|
||||
self.date_range,
|
||||
self.discontinuity,
|
||||
self.program_date_time,
|
||||
self.inf
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -120,11 +121,11 @@ mod tests {
|
|||
assert_eq!(
|
||||
MediaSegment::builder()
|
||||
.keys(vec![ExtXKey::empty()])
|
||||
.map_tag(ExtXMap::new("https://www.example.com/"))
|
||||
.byte_range_tag(ExtXByteRange::new(20, Some(5)))
|
||||
//.date_range_tag() // TODO!
|
||||
.discontinuity_tag(ExtXDiscontinuity)
|
||||
.inf_tag(ExtInf::new(Duration::from_secs(4)))
|
||||
.map(ExtXMap::new("https://www.example.com/"))
|
||||
.byte_range(ExtXByteRange::new(20, Some(5)))
|
||||
//.date_range() // TODO!
|
||||
.discontinuity(ExtXDiscontinuity)
|
||||
.inf(ExtInf::new(Duration::from_secs(4)))
|
||||
.uri("http://www.uri.com/")
|
||||
.build()
|
||||
.unwrap()
|
||||
|
|
|
@ -75,6 +75,10 @@ impl FromStr for ExtXTargetDuration {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<Duration> for ExtXTargetDuration {
|
||||
fn from(value: Duration) -> Self { Self::new(value) }
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
|
|
@ -6,50 +6,48 @@ use pretty_assertions::assert_eq;
|
|||
|
||||
#[test]
|
||||
fn test_media_playlist_with_byterange() {
|
||||
let media_playlist = concat!(
|
||||
"#EXTM3U\n",
|
||||
"#EXT-X-TARGETDURATION:10\n",
|
||||
"#EXT-X-VERSION:4\n",
|
||||
"#EXT-X-MEDIA-SEQUENCE:0\n",
|
||||
"#EXTINF:10.0,\n",
|
||||
"#EXT-X-BYTERANGE:75232@0\n",
|
||||
"video.ts\n",
|
||||
"#EXT-X-BYTERANGE:82112@752321\n",
|
||||
"#EXTINF:10.0,\n",
|
||||
"video.ts\n",
|
||||
"#EXTINF:10.0,\n",
|
||||
"#EXT-X-BYTERANGE:69864\n",
|
||||
"video.ts\n"
|
||||
)
|
||||
.parse::<MediaPlaylist>()
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
MediaPlaylist::builder()
|
||||
.target_duration(ExtXTargetDuration::new(Duration::from_secs(10)))
|
||||
.media_sequence(ExtXMediaSequence::new(0))
|
||||
.segments(vec![
|
||||
MediaSegment::builder()
|
||||
.inf_tag(ExtInf::new(Duration::from_secs_f64(10.0)))
|
||||
.byte_range_tag(ExtXByteRange::new(75232, Some(0)))
|
||||
.inf(ExtInf::new(Duration::from_secs_f64(10.0)))
|
||||
.byte_range(ExtXByteRange::new(75232, Some(0)))
|
||||
.uri("video.ts")
|
||||
.build()
|
||||
.unwrap(),
|
||||
MediaSegment::builder()
|
||||
.inf_tag(ExtInf::new(Duration::from_secs_f64(10.0)))
|
||||
.byte_range_tag(ExtXByteRange::new(82112, Some(752321)))
|
||||
.inf(ExtInf::new(Duration::from_secs_f64(10.0)))
|
||||
.byte_range(ExtXByteRange::new(82112, Some(752321)))
|
||||
.uri("video.ts")
|
||||
.build()
|
||||
.unwrap(),
|
||||
MediaSegment::builder()
|
||||
.inf_tag(ExtInf::new(Duration::from_secs_f64(10.0)))
|
||||
.byte_range_tag(ExtXByteRange::new(69864, None))
|
||||
.inf(ExtInf::new(Duration::from_secs_f64(10.0)))
|
||||
.byte_range(ExtXByteRange::new(69864, None))
|
||||
.uri("video.ts")
|
||||
.build()
|
||||
.unwrap(),
|
||||
])
|
||||
.build()
|
||||
.unwrap(),
|
||||
media_playlist
|
||||
concat!(
|
||||
"#EXTM3U\n",
|
||||
"#EXT-X-TARGETDURATION:10\n",
|
||||
"#EXT-X-VERSION:4\n",
|
||||
"#EXT-X-MEDIA-SEQUENCE:0\n",
|
||||
"#EXTINF:10.0,\n",
|
||||
"#EXT-X-BYTERANGE:75232@0\n",
|
||||
"video.ts\n",
|
||||
"#EXT-X-BYTERANGE:82112@752321\n",
|
||||
"#EXTINF:10.0,\n",
|
||||
"video.ts\n",
|
||||
"#EXTINF:10.0,\n",
|
||||
"#EXT-X-BYTERANGE:69864\n",
|
||||
"video.ts\n"
|
||||
)
|
||||
.parse::<MediaPlaylist>()
|
||||
.unwrap()
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
//! Credits go to
|
||||
//! - https://github.com/globocom/m3u8/blob/master/tests/playlists.py
|
||||
use hls_m3u8::tags::*;
|
||||
use hls_m3u8::MediaPlaylist;
|
||||
|
||||
use std::time::Duration;
|
||||
|
||||
use hls_m3u8::tags::{ExtInf, ExtXEndList};
|
||||
use hls_m3u8::{MediaPlaylist, MediaSegment};
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
#[test]
|
||||
fn test_simple_playlist() {
|
||||
let playlist = concat!(
|
||||
|
@ -17,31 +18,24 @@ fn test_simple_playlist() {
|
|||
"#EXT-X-ENDLIST\n"
|
||||
);
|
||||
|
||||
let media_playlist = playlist.parse::<MediaPlaylist>().unwrap();
|
||||
assert_eq!(
|
||||
media_playlist.target_duration(),
|
||||
ExtXTargetDuration::new(Duration::from_secs(5220))
|
||||
);
|
||||
|
||||
assert_eq!(media_playlist.segments().len(), 2);
|
||||
|
||||
assert_eq!(
|
||||
media_playlist.segments()[0].inf_tag(),
|
||||
&ExtInf::new(Duration::from_secs(0))
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
media_playlist.segments()[1].inf_tag(),
|
||||
&ExtInf::new(Duration::from_secs(5220))
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
media_playlist.segments()[0].uri(),
|
||||
&"http://media.example.com/entire1.ts".to_string()
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
media_playlist.segments()[1].uri(),
|
||||
&"http://media.example.com/entire2.ts".to_string()
|
||||
MediaPlaylist::builder()
|
||||
.target_duration(Duration::from_secs(5220))
|
||||
.segments(vec![
|
||||
MediaSegment::builder()
|
||||
.inf(ExtInf::new(Duration::from_secs(0)))
|
||||
.uri("http://media.example.com/entire1.ts")
|
||||
.build()
|
||||
.unwrap(),
|
||||
MediaSegment::builder()
|
||||
.inf(ExtInf::new(Duration::from_secs(5220)))
|
||||
.uri("http://media.example.com/entire2.ts")
|
||||
.build()
|
||||
.unwrap(),
|
||||
])
|
||||
.end_list(ExtXEndList)
|
||||
.build()
|
||||
.unwrap(),
|
||||
playlist.parse::<MediaPlaylist>().unwrap(),
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue