From 57d60ba438a00756ff3e7349d5b824cf4ac27af7 Mon Sep 17 00:00:00 2001 From: Rutger Schoorstra Date: Fri, 16 Apr 2021 21:04:03 +0200 Subject: [PATCH] Move alternatives to MasterPlaylist --- .../master-with-alternatives-2.m3u8 | 35 +++++++++++++++++ src/playlist.rs | 17 ++++----- tests/lib.rs | 38 +++++++++++-------- 3 files changed, 65 insertions(+), 25 deletions(-) create mode 100644 sample-playlists/master-with-alternatives-2.m3u8 diff --git a/sample-playlists/master-with-alternatives-2.m3u8 b/sample-playlists/master-with-alternatives-2.m3u8 new file mode 100644 index 0000000..2c40867 --- /dev/null +++ b/sample-playlists/master-with-alternatives-2.m3u8 @@ -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 \ No newline at end of file diff --git a/src/playlist.rs b/src/playlist.rs index fba1615..429e835 100644 --- a/src/playlist.rs +++ b/src/playlist.rs @@ -69,13 +69,13 @@ pub struct MasterPlaylist { pub session_key: Option, pub start: Option, pub independent_segments: bool, + pub alternatives: Vec // EXT-X-MEDIA tags } impl MasterPlaylist { pub fn from_tags(mut tags: Vec) -> 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, pub closed_captions: Option, // PROGRAM-ID tag was removed in protocol version 6 - pub alternatives: Vec, // 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(&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:")?; diff --git a/tests/lib.rs b/tests/lib.rs index 4990398..29e33d1 100644 --- a/tests/lib.rs +++ b/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 {