mirror of
https://github.com/rutgersc/m3u8-rs.git
synced 2025-03-11 08:51:19 +00:00
Move alternatives to MasterPlaylist
This commit is contained in:
parent
cd9402051e
commit
57d60ba438
3 changed files with 65 additions and 25 deletions
35
sample-playlists/master-with-alternatives-2.m3u8
Normal file
35
sample-playlists/master-with-alternatives-2.m3u8
Normal file
|
@ -0,0 +1,35 @@
|
|||
#EXTM3U
|
||||
#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="bipbop_audio",LANGUAGE="eng",NAME="BipBop Audio 1",AUTOSELECT=YES,DEFAULT=YES
|
||||
#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="bipbop_audio",LANGUAGE="eng",NAME="BipBop Audio 2",AUTOSELECT=NO,DEFAULT=NO,URI="alternate_audio_aac_sinewave/prog_index.m3u8"
|
||||
#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="subs",NAME="English",DEFAULT=YES,AUTOSELECT=YES,FORCED=NO,LANGUAGE="en",CHARACTERISTICS="public.accessibility.transcribes-spoken-dialog, public.accessibility.describes-music-and-sound",URI="subtitles/eng/prog_index.m3u8"
|
||||
#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="subs",NAME="English (Forced)",DEFAULT=NO,AUTOSELECT=YES,FORCED=YES,LANGUAGE="en",URI="subtitles/eng_forced/prog_index.m3u8"
|
||||
#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="subs",NAME="Français",DEFAULT=NO,AUTOSELECT=YES,FORCED=NO,LANGUAGE="fr",CHARACTERISTICS="public.accessibility.transcribes-spoken-dialog, public.accessibility.describes-music-and-sound",URI="subtitles/fra/prog_index.m3u8"
|
||||
#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="subs",NAME="Français (Forced)",DEFAULT=NO,AUTOSELECT=YES,FORCED=YES,LANGUAGE="fr",URI="subtitles/fra_forced/prog_index.m3u8"
|
||||
#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="subs",NAME="Español",DEFAULT=NO,AUTOSELECT=YES,FORCED=NO,LANGUAGE="es",CHARACTERISTICS="public.accessibility.transcribes-spoken-dialog, public.accessibility.describes-music-and-sound",URI="subtitles/spa/prog_index.m3u8"
|
||||
#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="subs",NAME="Español (Forced)",DEFAULT=NO,AUTOSELECT=YES,FORCED=YES,LANGUAGE="es",URI="subtitles/spa_forced/prog_index.m3u8"
|
||||
#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="subs",NAME="日本語",DEFAULT=NO,AUTOSELECT=YES,FORCED=NO,LANGUAGE="ja",CHARACTERISTICS="public.accessibility.transcribes-spoken-dialog, public.accessibility.describes-music-and-sound",URI="subtitles/jpn/prog_index.m3u8"
|
||||
#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="subs",NAME="日本語 (Forced)",DEFAULT=NO,AUTOSELECT=YES,FORCED=YES,LANGUAGE="ja",URI="subtitles/jpn_forced/prog_index.m3u8"
|
||||
|
||||
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=263851,CODECS="mp4a.40.2, avc1.4d400d",RESOLUTION=416x234,AUDIO="bipbop_audio",SUBTITLES="subs"
|
||||
gear1/prog_index.m3u8
|
||||
#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=28451,CODECS="avc1.4d400d",URI="gear1/iframe_index.m3u8"
|
||||
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=577610,CODECS="mp4a.40.2, avc1.4d401e",RESOLUTION=640x360,AUDIO="bipbop_audio",SUBTITLES="subs"
|
||||
gear2/prog_index.m3u8
|
||||
#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=181534,CODECS="avc1.4d401e",URI="gear2/iframe_index.m3u8"
|
||||
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=915905,CODECS="mp4a.40.2, avc1.4d401f",RESOLUTION=960x540,AUDIO="bipbop_audio",SUBTITLES="subs"
|
||||
gear3/prog_index.m3u8
|
||||
#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=297056,CODECS="avc1.4d401f",URI="gear3/iframe_index.m3u8"
|
||||
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=1030138,CODECS="mp4a.40.2, avc1.4d401f",RESOLUTION=1280x720,AUDIO="bipbop_audio",SUBTITLES="subs"
|
||||
gear4/prog_index.m3u8
|
||||
#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=339492,CODECS="avc1.4d401f",URI="gear4/iframe_index.m3u8"
|
||||
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=1924009,CODECS="mp4a.40.2, avc1.4d401f",RESOLUTION=1920x1080,AUDIO="bipbop_audio",SUBTITLES="subs"
|
||||
gear5/prog_index.m3u8
|
||||
#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=669554,CODECS="avc1.4d401f",URI="gear5/iframe_index.m3u8"
|
||||
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=41457,CODECS="mp4a.40.2",AUDIO="bipbop_audio",SUBTITLES="subs"
|
||||
gear0/prog_index.m3u8
|
|
@ -69,13 +69,13 @@ pub struct MasterPlaylist {
|
|||
pub session_key: Option<SessionKey>,
|
||||
pub start: Option<Start>,
|
||||
pub independent_segments: bool,
|
||||
pub alternatives: Vec<AlternativeMedia> // EXT-X-MEDIA tags
|
||||
}
|
||||
|
||||
impl MasterPlaylist {
|
||||
|
||||
pub fn from_tags(mut tags: Vec<MasterPlaylistTag>) -> MasterPlaylist {
|
||||
let mut master_playlist = MasterPlaylist::default();
|
||||
let mut alternatives = vec![];
|
||||
|
||||
while let Some(tag) = tags.pop() {
|
||||
match tag {
|
||||
|
@ -83,11 +83,9 @@ impl MasterPlaylist {
|
|||
master_playlist.version = v;
|
||||
}
|
||||
MasterPlaylistTag::AlternativeMedia(v) => {
|
||||
alternatives.push(v);
|
||||
master_playlist.alternatives.push(v);
|
||||
}
|
||||
MasterPlaylistTag::VariantStream(mut stream) => {
|
||||
stream.alternatives = alternatives;
|
||||
alternatives = vec![];
|
||||
MasterPlaylistTag::VariantStream(stream) => {
|
||||
master_playlist.variants.push(stream);
|
||||
}
|
||||
MasterPlaylistTag::Uri(uri) => {
|
||||
|
@ -125,6 +123,10 @@ impl MasterPlaylist {
|
|||
writeln!(w, "{}" ,"#EXTM3U")?;
|
||||
writeln!(w, "#EXT-X-VERSION:{}", self.version)?;
|
||||
|
||||
for alternative in &self.alternatives {
|
||||
alternative.write_to(w)?;
|
||||
}
|
||||
|
||||
for variant in &self.variants {
|
||||
variant.write_to(w)?;
|
||||
}
|
||||
|
@ -178,7 +180,6 @@ pub struct VariantStream {
|
|||
pub subtitles: Option<String>,
|
||||
pub closed_captions: Option<String>,
|
||||
// PROGRAM-ID tag was removed in protocol version 6
|
||||
pub alternatives: Vec<AlternativeMedia>, // EXT-X-MEDIA tags
|
||||
}
|
||||
|
||||
impl VariantStream {
|
||||
|
@ -196,15 +197,11 @@ impl VariantStream {
|
|||
video: attrs.remove("VIDEO"),
|
||||
subtitles: attrs.remove("SUBTITLES"),
|
||||
closed_captions: attrs.remove("CLOSED-CAPTIONS"),
|
||||
alternatives: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write_to<T: Write>(&self, w: &mut T) -> std::io::Result<()> {
|
||||
|
||||
for alternative in &self.alternatives {
|
||||
alternative.write_to(w)?;
|
||||
}
|
||||
|
||||
if self.is_i_frame {
|
||||
write!(w, "#EXT-X-I-FRAME-STREAM-INF:")?;
|
||||
|
|
38
tests/lib.rs
38
tests/lib.rs
|
@ -56,6 +56,13 @@ fn playlist_master_with_alternatives() {
|
|||
assert!(print_parse_playlist_test("master-with-alternatives.m3u8"));
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn playlist_master_with_alternatives_2_3() {
|
||||
assert!(print_parse_playlist_test("master-with-alternatives-2.m3u8"));
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn playlist_master_with_i_frame_stream_inf() {
|
||||
assert!(print_parse_playlist_test("master-with-i-frame-stream-inf.m3u8"));
|
||||
|
@ -290,6 +297,21 @@ fn create_and_parse_master_playlist_full() {
|
|||
|
||||
let mut playlist_original = Playlist::MasterPlaylist(MasterPlaylist {
|
||||
version: 6,
|
||||
alternatives: vec! [
|
||||
AlternativeMedia {
|
||||
media_type: AlternativeMediaType::Audio,
|
||||
uri: Some("alt-media-uri".into()),
|
||||
group_id: "group-id".into(),
|
||||
language: Some("language".into()),
|
||||
assoc_language: Some("assoc-language".into()),
|
||||
name: "Xmedia".into(),
|
||||
default: true, // Its absence indicates an implicit value of NO
|
||||
autoselect: true, // Its absence indicates an implicit value of NO
|
||||
forced: true, // Its absence indicates an implicit value of NO
|
||||
instream_id: Some("instream_id".into()),
|
||||
characteristics: Some("characteristics".into()),
|
||||
}
|
||||
],
|
||||
variants: vec![
|
||||
VariantStream {
|
||||
is_i_frame: false,
|
||||
|
@ -303,21 +325,7 @@ fn create_and_parse_master_playlist_full() {
|
|||
video: Some("video".into()),
|
||||
subtitles: Some("subtitles".into()),
|
||||
closed_captions: Some("closed_captions".into()),
|
||||
alternatives: vec! [
|
||||
AlternativeMedia {
|
||||
media_type: AlternativeMediaType::Audio,
|
||||
uri: Some("alt-media-uri".into()),
|
||||
group_id: "group-id".into(),
|
||||
language: Some("language".into()),
|
||||
assoc_language: Some("assoc-language".into()),
|
||||
name: "Xmedia".into(),
|
||||
default: true, // Its absence indicates an implicit value of NO
|
||||
autoselect: true, // Its absence indicates an implicit value of NO
|
||||
forced: true, // Its absence indicates an implicit value of NO
|
||||
instream_id: Some("instream_id".into()),
|
||||
characteristics: Some("characteristics".into()),
|
||||
}
|
||||
]
|
||||
|
||||
}
|
||||
],
|
||||
session_data: Some(SessionData {
|
||||
|
|
Loading…
Reference in a new issue