Skip to content

Commit 0c3553c

Browse files
committed
feat(iori): wip support for discontinued parts
1 parent 8ea58c5 commit 0c3553c

File tree

14 files changed

+265
-113
lines changed

14 files changed

+265
-113
lines changed

crates/iori-hls/src/m3u8_rs.rs

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,23 +76,37 @@ impl From<m3u8_rs::Resolution> for Resolution {
7676

7777
impl From<m3u8_rs::MediaPlaylist> for MediaPlaylist {
7878
fn from(value: m3u8_rs::MediaPlaylist) -> Self {
79+
let mut current_part_index = value.discontinuity_sequence;
80+
let mut segments = Vec::with_capacity(value.segments.len());
81+
82+
for segment in value.segments {
83+
if segment.discontinuity {
84+
current_part_index += 1;
85+
}
86+
87+
let segment: crate::models::MediaSegment = (segment, current_part_index).into();
88+
segments.push(segment);
89+
}
90+
7991
MediaPlaylist {
8092
media_sequence: value.media_sequence,
81-
segments: value.segments.into_iter().map(MediaSegment::from).collect(),
93+
segments,
8294
end_list: value.end_list,
95+
discontinuity_sequence: value.discontinuity_sequence,
8396
}
8497
}
8598
}
8699

87-
impl From<m3u8_rs::MediaSegment> for MediaSegment {
88-
fn from(value: m3u8_rs::MediaSegment) -> Self {
100+
impl From<(m3u8_rs::MediaSegment, u64)> for MediaSegment {
101+
fn from((value, part_index): (m3u8_rs::MediaSegment, u64)) -> Self {
89102
MediaSegment {
90103
uri: value.uri,
91104
duration: (value.duration as f64).into(),
92105
title: value.title,
93106
byte_range: value.byte_range.map(ByteRange::from),
94107
key: value.key.map(Key::from),
95108
map: value.map.map(Map::from),
109+
part_index,
96110
}
97111
}
98112
}

crates/iori-hls/src/models.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ impl From<hls::MediaType> for AlternativeMediaType {
104104
#[derive(Debug, Clone, PartialEq, Comparable)]
105105
pub struct MediaPlaylist {
106106
pub media_sequence: u64,
107+
pub discontinuity_sequence: u64,
107108
pub segments: Vec<MediaSegment>,
108109
pub end_list: bool,
109110
}
@@ -116,6 +117,7 @@ pub struct MediaSegment {
116117
pub byte_range: Option<ByteRange>,
117118
pub key: Option<Key>,
118119
pub map: Option<Map>,
120+
pub part_index: u64,
119121
}
120122

121123
impl<'a>
@@ -125,15 +127,17 @@ impl<'a>
125127
Option<ByteRange>,
126128
Option<Key>,
127129
Option<Map>,
130+
u64,
128131
)> for MediaSegment
129132
{
130133
fn from(
131-
(inf, uri, byte_range, key, map): (
134+
(inf, uri, byte_range, key, map, part_index): (
132135
hls::Inf<'a>,
133136
Cow<'a, str>,
134137
Option<ByteRange>,
135138
Option<Key>,
136139
Option<Map>,
140+
u64,
137141
),
138142
) -> Self {
139143
Self {
@@ -146,6 +150,7 @@ impl<'a>
146150
byte_range,
147151
key,
148152
map,
153+
part_index,
149154
}
150155
}
151156
}

crates/iori-hls/src/parse.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,14 @@ pub fn parse_playlist_res(input: &[u8]) -> Result<Playlist, M3u8ParseError> {
1919

2020
// media playlist
2121
let mut media_sequence = 0;
22+
let mut discontinuity_sequence = 0;
2223
let mut segments: Vec<MediaSegment> = Vec::new();
2324
let mut end_list = false;
2425

2526
// Maps(initial segment information) and keys(encryption information)
2627
let mut current_key: Option<Key> = None;
2728
let mut current_map: Option<Map> = None;
29+
let mut current_part_index = 0;
2830

2931
// Pending tags, which should be cleared after the URI line is processed
3032
let mut pending_inf: Option<hls::Inf> = None;
@@ -35,6 +37,13 @@ pub fn parse_playlist_res(input: &[u8]) -> Result<Playlist, M3u8ParseError> {
3537
match line {
3638
HlsLine::KnownTag(KnownTag::Hls(tag)) => match tag {
3739
hls::Tag::MediaSequence(seq) => media_sequence = seq.media_sequence(),
40+
hls::Tag::DiscontinuitySequence(seq) => {
41+
discontinuity_sequence = seq.discontinuity_sequence();
42+
current_part_index = discontinuity_sequence;
43+
}
44+
hls::Tag::Discontinuity(_) => {
45+
current_part_index += 1;
46+
}
3847
hls::Tag::Inf(inf) => pending_inf = Some(inf),
3948
hls::Tag::Byterange(range) => pending_byterange = Some(range.into()),
4049
hls::Tag::Key(key) => current_key = Some(key.into()),
@@ -63,6 +72,7 @@ pub fn parse_playlist_res(input: &[u8]) -> Result<Playlist, M3u8ParseError> {
6372
pending_byterange.take(),
6473
current_key.clone(),
6574
current_map.clone(),
75+
current_part_index,
6676
)
6777
.into(),
6878
);
@@ -83,6 +93,7 @@ pub fn parse_playlist_res(input: &[u8]) -> Result<Playlist, M3u8ParseError> {
8393
} else {
8494
Playlist::MediaPlaylist(MediaPlaylist {
8595
media_sequence,
96+
discontinuity_sequence,
8697
segments,
8798
end_list,
8899
})

crates/iori-hls/tests/cases.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ fn test_accuracy_radiko_01() {
1212
}
1313

1414
#[test]
15-
fn test_accuracy_archive_02() {
15+
fn test_accuracy_archive_01() {
1616
let data = include_bytes!("./fixtures/archive_01.m3u8");
1717
let old_result = iori_hls::m3u8_rs::parse_playlist_res(data);
1818
let new_result = iori_hls::parse::parse_playlist_res(data);
@@ -21,3 +21,14 @@ fn test_accuracy_archive_02() {
2121
let new_result = new_result.expect("New parse engine should not error");
2222
assert_changes!(old_result, new_result, Changed::Unchanged);
2323
}
24+
25+
#[test]
26+
fn test_accuracy_archive_02() {
27+
let data = include_bytes!("./fixtures/archive_02.m3u8");
28+
let old_result = iori_hls::m3u8_rs::parse_playlist_res(data);
29+
let new_result = iori_hls::parse::parse_playlist_res(data);
30+
31+
let old_result = old_result.expect("Old parse engine should not error");
32+
let new_result = new_result.expect("New parse engine should not error");
33+
assert_changes!(old_result, new_result, Changed::Unchanged);
34+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#EXTM3U
2+
#EXT-X-VERSION:3
3+
#EXT-X-TARGETDURATION:10
4+
#EXT-X-MEDIA-SEQUENCE:0
5+
#EXT-X-DISCONTINUITY-SEQUENCE:0
6+
7+
#EXTINF:10.0,
8+
segment_0.ts
9+
#EXTINF:10.0,
10+
segment_1.ts
11+
12+
#EXT-X-DISCONTINUITY
13+
#EXTINF:10.0,
14+
segment_2.ts
15+
#EXTINF:10.0,
16+
segment_3.ts
17+
18+
#EXT-X-ENDLIST
19+

crates/iori/src/hls/segment.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ pub struct M3u8Segment {
2323
/// Media sequence id from the m3u8 file
2424
pub media_sequence: u64,
2525

26+
pub part_index: u64,
27+
2628
pub duration: f64,
2729
pub format: SegmentFormat,
2830
}
@@ -59,6 +61,10 @@ impl StreamingSegment for M3u8Segment {
5961
fn format(&self) -> SegmentFormat {
6062
self.format.clone()
6163
}
64+
65+
fn part_index(&self) -> u64 {
66+
self.part_index
67+
}
6268
}
6369

6470
impl RemoteStreamingSegment for M3u8Segment {

crates/iori/src/hls/source.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ impl HlsMediaPlaylistSource {
166166
initial_segment: initial_segment.clone(),
167167
sequence: self.sequence.fetch_add(1, Ordering::Relaxed),
168168
media_sequence,
169+
part_index: segment.part_index,
169170
byte_range: segment.byte_range.as_ref().map(|r| crate::ByteRange {
170171
offset: r.offset.unwrap_or(next_range_start),
171172
length: Some(r.length),

crates/iori/src/lib.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ pub trait StreamingSource {
5858
) -> impl Future<Output = IoriResult<impl Stream<Item = IoriResult<Vec<Self::Segment>>>>>;
5959
}
6060

61+
/// A segment of a streaming source
6162
pub trait StreamingSegment {
6263
/// Stream id
6364
fn stream_id(&self) -> u64;
@@ -68,6 +69,11 @@ pub trait StreamingSegment {
6869
/// Sequence ID of the segment, starts from 0
6970
fn sequence(&self) -> u64;
7071

72+
/// Optional part index of the segment
73+
fn part_index(&self) -> u64 {
74+
0
75+
}
76+
7177
/// File name of the segment
7278
fn file_name(&self) -> &str;
7379

0 commit comments

Comments
 (0)