Move alternatives to MasterPlaylist

This commit is contained in:
Rutger Schoorstra 2021-04-16 21:04:03 +02:00
parent cd9402051e
commit 57d60ba438
3 changed files with 65 additions and 25 deletions

View 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

View file

@ -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:")?;

View file

@ -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 {