mirror of
https://github.com/v0l/m3u8-rs.git
synced 2025-06-16 23:08:50 +00:00
feat: create enum to represent partial/full segments
This commit is contained in:
12
src/lib.rs
12
src/lib.rs
@ -38,7 +38,7 @@
|
||||
//! Creating a playlist and writing it back to a vec/file
|
||||
//!
|
||||
//! ```
|
||||
//! use m3u8_rs::{MediaPlaylist, MediaPlaylistType, MediaSegment};
|
||||
//! use m3u8_rs::{MediaPlaylist, MediaPlaylistType, MediaSegment, MediaSegmentType};
|
||||
//!
|
||||
//! let playlist = MediaPlaylist {
|
||||
//! version: Some(6),
|
||||
@ -48,12 +48,12 @@
|
||||
//! end_list: true,
|
||||
//! playlist_type: Some(MediaPlaylistType::Vod),
|
||||
//! segments: vec![
|
||||
//! MediaSegment {
|
||||
//! MediaSegmentType::Full(MediaSegment {
|
||||
//! uri: "20140311T113819-01-338559live.ts".into(),
|
||||
//! duration: 2.002,
|
||||
//! title: Some("title".into()),
|
||||
//! ..Default::default()
|
||||
//! },
|
||||
//! }),
|
||||
//! ],
|
||||
//! ..Default::default()
|
||||
//! };
|
||||
@ -69,18 +69,18 @@
|
||||
//!
|
||||
//! ```
|
||||
//! use std::sync::atomic::Ordering;
|
||||
//! use m3u8_rs::{WRITE_OPT_FLOAT_PRECISION, MediaPlaylist, MediaSegment};
|
||||
//! use m3u8_rs::{WRITE_OPT_FLOAT_PRECISION, MediaPlaylist, MediaSegment, MediaSegmentType};
|
||||
//!
|
||||
//! WRITE_OPT_FLOAT_PRECISION.store(5, Ordering::Relaxed);
|
||||
//!
|
||||
//! let playlist = MediaPlaylist {
|
||||
//! target_duration: 3,
|
||||
//! segments: vec![
|
||||
//! MediaSegment {
|
||||
//! MediaSegmentType::Full(MediaSegment {
|
||||
//! duration: 2.9,
|
||||
//! title: Some("title".into()),
|
||||
//! ..Default::default()
|
||||
//! },
|
||||
//! }),
|
||||
//! ],
|
||||
//! ..Default::default()
|
||||
//! };
|
||||
|
@ -476,13 +476,15 @@ fn media_playlist_from_tags(mut tags: Vec<MediaPlaylistTag>) -> MediaPlaylist {
|
||||
next_segment.key = encryption_key.clone();
|
||||
next_segment.map = map.clone();
|
||||
next_segment.uri = u;
|
||||
media_playlist.segments.push(next_segment);
|
||||
media_playlist
|
||||
.segments
|
||||
.push(MediaSegmentType::Full(next_segment));
|
||||
next_segment = MediaSegment::empty();
|
||||
encryption_key = None;
|
||||
map = None;
|
||||
}
|
||||
SegmentTag::Part(p) => {
|
||||
next_segment.parts.push(p);
|
||||
media_playlist.segments.push(MediaSegmentType::Partial(p));
|
||||
}
|
||||
SegmentTag::Unknown(t) => {
|
||||
next_segment.unknown_tags.push(t);
|
||||
|
@ -738,7 +738,7 @@ pub struct MediaPlaylist {
|
||||
pub target_duration: u64,
|
||||
/// `#EXT-X-MEDIA-SEQUENCE:<number>`
|
||||
pub media_sequence: u64,
|
||||
pub segments: Vec<MediaSegment>,
|
||||
pub segments: Vec<MediaSegmentType>,
|
||||
/// `#EXT-X-DISCONTINUITY-SEQUENCE:<number>`
|
||||
pub discontinuity_sequence: u64,
|
||||
/// `#EXT-X-ENDLIST`
|
||||
@ -868,6 +868,23 @@ impl Default for MediaPlaylistType {
|
||||
// Media Segment
|
||||
// -----------------------------------------------------------------------------------------------
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum MediaSegmentType {
|
||||
/// Regular HLS segment (#EXTINF)
|
||||
Full(MediaSegment),
|
||||
/// HLS-LL: Partial segment (#EXT-X-PART)
|
||||
Partial(Part),
|
||||
}
|
||||
|
||||
impl MediaSegmentType {
|
||||
pub fn write_to<T: Write>(&self, w: &mut T) -> std::io::Result<()> {
|
||||
match self {
|
||||
MediaSegmentType::Full(s) => s.write_to(w),
|
||||
MediaSegmentType::Partial(s) => s.write_to(w),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A [Media Segment](https://tools.ietf.org/html/draft-pantos-http-live-streaming-19#section-3)
|
||||
/// is specified by a URI and optionally a byte range.
|
||||
#[derive(Debug, Default, PartialEq, Clone)]
|
||||
@ -891,9 +908,6 @@ pub struct MediaSegment {
|
||||
pub daterange: Option<DateRange>,
|
||||
/// `#EXT-`
|
||||
pub unknown_tags: Vec<ExtTag>,
|
||||
|
||||
// LL-HLS specific fields
|
||||
pub parts: Vec<Part>,
|
||||
}
|
||||
|
||||
impl MediaSegment {
|
||||
@ -932,9 +946,6 @@ impl MediaSegment {
|
||||
v.write_attributes_to(w)?;
|
||||
writeln!(w)?;
|
||||
}
|
||||
for part in &self.parts {
|
||||
part.write_to(w)?;
|
||||
}
|
||||
for unknown_tag in &self.unknown_tags {
|
||||
writeln!(w, "{}", unknown_tag)?;
|
||||
}
|
||||
|
140
tests/lib.rs
140
tests/lib.rs
@ -208,12 +208,12 @@ fn create_segment_float_inf() {
|
||||
discontinuity_sequence: 1234,
|
||||
end_list: true,
|
||||
playlist_type: Some(MediaPlaylistType::Vod),
|
||||
segments: vec![MediaSegment {
|
||||
segments: vec![MediaSegmentType::Full(MediaSegment {
|
||||
uri: "20140311T113819-01-338559live.ts".into(),
|
||||
duration: 2.000f32,
|
||||
title: Some("title".into()),
|
||||
..Default::default()
|
||||
}],
|
||||
})],
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
@ -336,12 +336,12 @@ fn create_and_parse_media_playlist_empty() {
|
||||
fn create_and_parse_media_playlist_single_segment() {
|
||||
let mut playlist_original = Playlist::MediaPlaylist(MediaPlaylist {
|
||||
target_duration: 2,
|
||||
segments: vec![MediaSegment {
|
||||
segments: vec![MediaSegmentType::Full(MediaSegment {
|
||||
uri: "20140311T113819-01-338559live.ts".into(),
|
||||
duration: 2.002,
|
||||
title: Some("hey".into()),
|
||||
..Default::default()
|
||||
}],
|
||||
})],
|
||||
..Default::default()
|
||||
});
|
||||
let playlist_parsed = print_create_and_parse_playlist(&mut playlist_original);
|
||||
@ -364,7 +364,7 @@ fn create_and_parse_media_playlist_full() {
|
||||
other_attributes: Default::default(),
|
||||
}),
|
||||
independent_segments: true,
|
||||
segments: vec![MediaSegment {
|
||||
segments: vec![MediaSegmentType::Full(MediaSegment {
|
||||
uri: "20140311T113819-01-338559live.ts".into(),
|
||||
duration: 2.002,
|
||||
title: Some("338559".into()),
|
||||
@ -414,7 +414,7 @@ fn create_and_parse_media_playlist_full() {
|
||||
rest: Some("DURATION=2.002".into()),
|
||||
}],
|
||||
..Default::default()
|
||||
}],
|
||||
})],
|
||||
unknown_tags: vec![],
|
||||
|
||||
server_control: Default::default(),
|
||||
@ -492,76 +492,76 @@ fn create_and_parse_media_playlist_llhls() {
|
||||
other_attributes: Default::default(),
|
||||
}),
|
||||
independent_segments: true,
|
||||
segments: vec![MediaSegment {
|
||||
uri: "20140311T113819-01-338559live.ts".into(),
|
||||
duration: 2.002,
|
||||
title: Some("338559".into()),
|
||||
byte_range: Some(ByteRange {
|
||||
length: 137116,
|
||||
offset: Some(4559),
|
||||
}),
|
||||
discontinuity: true,
|
||||
key: Some(Key {
|
||||
method: KeyMethod::None,
|
||||
uri: Some("https://secure.domain.com".into()),
|
||||
iv: Some("0xb059217aa2649ce170b734".into()),
|
||||
keyformat: Some("xXkeyformatXx".into()),
|
||||
keyformatversions: Some("xXFormatVers".into()),
|
||||
}),
|
||||
map: Some(Map {
|
||||
uri: "www.map-uri.com".into(),
|
||||
segments: vec![
|
||||
MediaSegmentType::Full(MediaSegment {
|
||||
uri: "20140311T113819-01-338559live.ts".into(),
|
||||
duration: 2.002,
|
||||
title: Some("338559".into()),
|
||||
byte_range: Some(ByteRange {
|
||||
length: 137116,
|
||||
offset: Some(4559),
|
||||
}),
|
||||
other_attributes: Default::default(),
|
||||
}),
|
||||
program_date_time: Some(
|
||||
chrono::FixedOffset::east(8 * 3600)
|
||||
.ymd(2010, 2, 19)
|
||||
.and_hms_milli(14, 54, 23, 31),
|
||||
),
|
||||
daterange: Some(DateRange {
|
||||
id: "9999".into(),
|
||||
class: Some("class".into()),
|
||||
start_date: chrono::FixedOffset::east(8 * 3600)
|
||||
.ymd(2010, 2, 19)
|
||||
.and_hms_milli(14, 54, 23, 31),
|
||||
end_date: None,
|
||||
duration: None,
|
||||
planned_duration: Some("40.000".parse().unwrap()),
|
||||
x_prefixed: Some(HashMap::from([(
|
||||
"X-client-attribute".into(),
|
||||
"whatever".into(),
|
||||
)])),
|
||||
end_on_next: false,
|
||||
other_attributes: Default::default(),
|
||||
}),
|
||||
unknown_tags: vec![],
|
||||
parts: vec![
|
||||
Part {
|
||||
uri: "part0.ts".into(),
|
||||
duration: 0.5,
|
||||
independent: true,
|
||||
gap: false,
|
||||
discontinuity: true,
|
||||
key: Some(Key {
|
||||
method: KeyMethod::None,
|
||||
uri: Some("https://secure.domain.com".into()),
|
||||
iv: Some("0xb059217aa2649ce170b734".into()),
|
||||
keyformat: Some("xXkeyformatXx".into()),
|
||||
keyformatversions: Some("xXFormatVers".into()),
|
||||
}),
|
||||
map: Some(Map {
|
||||
uri: "www.map-uri.com".into(),
|
||||
byte_range: Some(ByteRange {
|
||||
length: 50000,
|
||||
offset: Some(0),
|
||||
length: 137116,
|
||||
offset: Some(4559),
|
||||
}),
|
||||
},
|
||||
Part {
|
||||
uri: "part1.ts".into(),
|
||||
duration: 0.5,
|
||||
independent: false,
|
||||
gap: false,
|
||||
byte_range: Some(ByteRange {
|
||||
length: 50000,
|
||||
offset: Some(50000),
|
||||
}),
|
||||
},
|
||||
],
|
||||
..Default::default()
|
||||
}],
|
||||
other_attributes: Default::default(),
|
||||
}),
|
||||
program_date_time: Some(
|
||||
chrono::FixedOffset::east(8 * 3600)
|
||||
.ymd(2010, 2, 19)
|
||||
.and_hms_milli(14, 54, 23, 31),
|
||||
),
|
||||
daterange: Some(DateRange {
|
||||
id: "9999".into(),
|
||||
class: Some("class".into()),
|
||||
start_date: chrono::FixedOffset::east(8 * 3600)
|
||||
.ymd(2010, 2, 19)
|
||||
.and_hms_milli(14, 54, 23, 31),
|
||||
end_date: None,
|
||||
duration: None,
|
||||
planned_duration: Some("40.000".parse().unwrap()),
|
||||
x_prefixed: Some(HashMap::from([(
|
||||
"X-client-attribute".into(),
|
||||
"whatever".into(),
|
||||
)])),
|
||||
end_on_next: false,
|
||||
other_attributes: Default::default(),
|
||||
}),
|
||||
unknown_tags: vec![],
|
||||
..Default::default()
|
||||
}),
|
||||
MediaSegmentType::Partial(Part {
|
||||
uri: "part0.ts".into(),
|
||||
duration: 0.5,
|
||||
independent: true,
|
||||
gap: false,
|
||||
byte_range: Some(ByteRange {
|
||||
length: 50000,
|
||||
offset: Some(0),
|
||||
}),
|
||||
}),
|
||||
MediaSegmentType::Partial(Part {
|
||||
uri: "part1.ts".into(),
|
||||
duration: 0.5,
|
||||
independent: false,
|
||||
gap: false,
|
||||
byte_range: Some(ByteRange {
|
||||
length: 50000,
|
||||
offset: Some(50000),
|
||||
}),
|
||||
}),
|
||||
],
|
||||
unknown_tags: vec![],
|
||||
server_control: Some(ServerControl {
|
||||
can_skip_until: Some(12.0),
|
||||
|
Reference in New Issue
Block a user