diff --git a/src/master_playlist.rs b/src/master_playlist.rs index 9d9906f..c0ecd43 100644 --- a/src/master_playlist.rs +++ b/src/master_playlist.rs @@ -481,24 +481,6 @@ mod tests { #[test] fn test_display() { assert_eq!( - concat!( - "#EXTM3U\n", - "#EXT-X-STREAM-INF:", - "BANDWIDTH=150000,CODECS=\"avc1.42e00a,mp4a.40.2\",RESOLUTION=416x234\n", - "http://example.com/low/index.m3u8\n", - "#EXT-X-STREAM-INF:", - "BANDWIDTH=240000,CODECS=\"avc1.42e00a,mp4a.40.2\",RESOLUTION=416x234\n", - "http://example.com/lo_mid/index.m3u8\n", - "#EXT-X-STREAM-INF:", - "BANDWIDTH=440000,CODECS=\"avc1.42e00a,mp4a.40.2\",RESOLUTION=416x234\n", - "http://example.com/hi_mid/index.m3u8\n", - "#EXT-X-STREAM-INF:", - "BANDWIDTH=640000,CODECS=\"avc1.42e00a,mp4a.40.2\",RESOLUTION=640x360\n", - "http://example.com/high/index.m3u8\n", - "#EXT-X-STREAM-INF:BANDWIDTH=64000,CODECS=\"mp4a.40.5\"\n", - "http://example.com/audio/index.m3u8\n" - ) - .to_string(), MasterPlaylist::builder() .variants(vec![ VariantStream::ExtXStreamInf { @@ -568,7 +550,30 @@ mod tests { ]) .build() .unwrap() - .to_string() + .to_string(), + concat!( + "#EXTM3U\n", + // + "#EXT-X-STREAM-INF:", + "BANDWIDTH=150000,CODECS=\"avc1.42e00a,mp4a.40.2\",RESOLUTION=416x234\n", + "http://example.com/low/index.m3u8\n", + // + "#EXT-X-STREAM-INF:", + "BANDWIDTH=240000,CODECS=\"avc1.42e00a,mp4a.40.2\",RESOLUTION=416x234\n", + "http://example.com/lo_mid/index.m3u8\n", + // + "#EXT-X-STREAM-INF:", + "BANDWIDTH=440000,CODECS=\"avc1.42e00a,mp4a.40.2\",RESOLUTION=416x234\n", + "http://example.com/hi_mid/index.m3u8\n", + // + "#EXT-X-STREAM-INF:", + "BANDWIDTH=640000,CODECS=\"avc1.42e00a,mp4a.40.2\",RESOLUTION=640x360\n", + "http://example.com/high/index.m3u8\n", + // + "#EXT-X-STREAM-INF:BANDWIDTH=64000,CODECS=\"mp4a.40.5\"\n", + "http://example.com/audio/index.m3u8\n" + ) + .to_string() ); } } diff --git a/tests/playlist.rs b/tests/playlist.rs deleted file mode 100644 index 925f4bf..0000000 --- a/tests/playlist.rs +++ /dev/null @@ -1,41 +0,0 @@ -//! Credits go to -//! - https://github.com/globocom/m3u8/blob/master/tests/playlists.py -use std::time::Duration; - -use hls_m3u8::tags::{ExtInf, ExtXEndList}; -use hls_m3u8::{MediaPlaylist, MediaSegment}; -use pretty_assertions::assert_eq; - -#[test] -fn test_simple_playlist() { - let playlist = concat!( - "#EXTM3U\n", - "#EXT-X-TARGETDURATION:5220\n", - "#EXTINF:0,\n", - "http://media.example.com/entire1.ts\n", - "#EXTINF:5220,\n", - "http://media.example.com/entire2.ts\n", - "#EXT-X-ENDLIST\n" - ); - - assert_eq!( - MediaPlaylist::builder() - .target_duration(Duration::from_secs(5220)) - .segments(vec![ - MediaSegment::builder() - .inf(ExtInf::new(Duration::from_secs(0))) - .uri("http://media.example.com/entire1.ts") - .build() - .unwrap(), - MediaSegment::builder() - .inf(ExtInf::new(Duration::from_secs(5220))) - .uri("http://media.example.com/entire2.ts") - .build() - .unwrap(), - ]) - .end_list(ExtXEndList) - .build() - .unwrap(), - playlist.parse::().unwrap(), - ); -} diff --git a/tests/master_playlist.rs b/tests/rfc8216.rs similarity index 51% rename from tests/master_playlist.rs rename to tests/rfc8216.rs index 2d35d35..5390f55 100644 --- a/tests/master_playlist.rs +++ b/tests/rfc8216.rs @@ -1,27 +1,173 @@ -use hls_m3u8::tags::{ExtXMedia, VariantStream}; -use hls_m3u8::types::{MediaType, StreamData}; -use hls_m3u8::MasterPlaylist; +// https://tools.ietf.org/html/rfc8216#section-8 +use std::time::Duration; +use hls_m3u8::tags::{ + ExtInf, ExtXEndList, ExtXKey, ExtXMedia, ExtXMediaSequence, ExtXTargetDuration, VariantStream, +}; +use hls_m3u8::types::{EncryptionMethod, MediaType, StreamData}; +use hls_m3u8::{MasterPlaylist, MediaPlaylist, MediaSegment}; use pretty_assertions::assert_eq; -#[test] -fn test_master_playlist() { - // https://tools.ietf.org/html/rfc8216#section-8.4 - let master_playlist = concat!( - "#EXTM3U\n", - "#EXT-X-STREAM-INF:BANDWIDTH=1280000,AVERAGE-BANDWIDTH=1000000\n", - "http://example.com/low.m3u8\n", - "#EXT-X-STREAM-INF:BANDWIDTH=2560000,AVERAGE-BANDWIDTH=2000000\n", - "http://example.com/mid.m3u8\n", - "#EXT-X-STREAM-INF:BANDWIDTH=7680000,AVERAGE-BANDWIDTH=6000000\n", - "http://example.com/hi.m3u8\n", - "#EXT-X-STREAM-INF:BANDWIDTH=65000,CODECS=\"mp4a.40.5\"\n", - "http://example.com/audio-only.m3u8", - ) - .parse::() - .unwrap(); +macro_rules! generate_tests { + ( $( $fnname:ident => { $struct:expr, $str:expr }),+ $(,)* ) => { + $( + #[test] + fn $fnname() { + assert_eq!($struct, $str.parse().unwrap()); - assert_eq!( + assert_eq!($struct.to_string(), $str.to_string()); + } + )+ + } +} + +generate_tests! [ + test_simple_playlist => { + MediaPlaylist::builder() + .target_duration(ExtXTargetDuration::new(Duration::from_secs(10))) + .segments(vec![ + MediaSegment::builder() + .inf(ExtInf::new(Duration::from_secs_f64(9.009))) + .uri("http://media.example.com/first.ts") + .build() + .unwrap(), + MediaSegment::builder() + .inf(ExtInf::new(Duration::from_secs_f64(9.009))) + .uri("http://media.example.com/second.ts") + .build() + .unwrap(), + MediaSegment::builder() + .inf(ExtInf::new(Duration::from_secs_f64(3.003))) + .uri("http://media.example.com/third.ts") + .build() + .unwrap(), + ]) + .end_list(ExtXEndList) + .build() + .unwrap(), + concat!( + "#EXTM3U\n", + "#EXT-X-VERSION:3\n", + "#EXT-X-TARGETDURATION:10\n", + "#EXTINF:9.009,\n", + "http://media.example.com/first.ts\n", + "#EXTINF:9.009,\n", + "http://media.example.com/second.ts\n", + "#EXTINF:3.003,\n", + "http://media.example.com/third.ts\n", + "#EXT-X-ENDLIST\n" + ) + }, + test_live_media_playlist_using_https => { + MediaPlaylist::builder() + .target_duration(ExtXTargetDuration::new(Duration::from_secs(8))) + .media_sequence(ExtXMediaSequence::new(2680)) + .segments(vec![ + MediaSegment::builder() + .inf(ExtInf::new(Duration::from_secs_f64(7.975))) + .uri("https://priv.example.com/fileSequence2680.ts") + .build() + .unwrap(), + MediaSegment::builder() + .inf(ExtInf::new(Duration::from_secs_f64(7.941))) + .uri("https://priv.example.com/fileSequence2681.ts") + .build() + .unwrap(), + MediaSegment::builder() + .inf(ExtInf::new(Duration::from_secs_f64(7.975))) + .uri("https://priv.example.com/fileSequence2682.ts") + .build() + .unwrap(), + ]) + .build() + .unwrap(), + concat!( + "#EXTM3U\n", + "#EXT-X-VERSION:3\n", + "#EXT-X-TARGETDURATION:8\n", + "#EXT-X-MEDIA-SEQUENCE:2680\n", + "#EXTINF:7.975,\n", + "https://priv.example.com/fileSequence2680.ts\n", + "#EXTINF:7.941,\n", + "https://priv.example.com/fileSequence2681.ts\n", + "#EXTINF:7.975,\n", + "https://priv.example.com/fileSequence2682.ts\n", + ) + }, + test_media_playlist_with_encrypted_segments => { + MediaPlaylist::builder() + .target_duration(ExtXTargetDuration::new(Duration::from_secs(15))) + .media_sequence(ExtXMediaSequence::new(7794)) + .segments(vec![ + MediaSegment::builder() + .inf(ExtInf::new(Duration::from_secs_f64(2.833))) + .keys(vec![ + ExtXKey::new( + EncryptionMethod::Aes128, + "https://priv.example.com/key.php?r=52" + ) + ]) + .uri("http://media.example.com/fileSequence52-A.ts") + .build() + .unwrap(), + MediaSegment::builder() + .inf(ExtInf::new(Duration::from_secs_f64(15.0))) + .keys(vec![ + ExtXKey::new( + EncryptionMethod::Aes128, + "https://priv.example.com/key.php?r=52" + ) + ]) + .uri("http://media.example.com/fileSequence52-B.ts") + .build() + .unwrap(), + MediaSegment::builder() + .inf(ExtInf::new(Duration::from_secs_f64(13.333))) + .keys(vec![ + ExtXKey::new( + EncryptionMethod::Aes128, + "https://priv.example.com/key.php?r=52" + ) + ]) + .uri("http://media.example.com/fileSequence52-C.ts") + .build() + .unwrap(), + MediaSegment::builder() + .inf(ExtInf::new(Duration::from_secs_f64(15.0))) + .keys(vec![ + ExtXKey::new( + EncryptionMethod::Aes128, + "https://priv.example.com/key.php?r=53" + ) + ]) + .uri("http://media.example.com/fileSequence53-A.ts") + .build() + .unwrap(), + ]) + .build() + .unwrap(), + concat!( + "#EXTM3U\n", + "#EXT-X-VERSION:3\n", + "#EXT-X-TARGETDURATION:15\n", + "#EXT-X-MEDIA-SEQUENCE:7794\n", + + "#EXT-X-KEY:METHOD=AES-128,URI=\"https://priv.example.com/key.php?r=52\"\n", + + "#EXTINF:2.833,\n", + "http://media.example.com/fileSequence52-A.ts\n", + "#EXTINF:15,\n", + "http://media.example.com/fileSequence52-B.ts\n", + "#EXTINF:13.333,\n", + "http://media.example.com/fileSequence52-C.ts\n", + + "#EXT-X-KEY:METHOD=AES-128,URI=\"https://priv.example.com/key.php?r=53\"\n", + + "#EXTINF:15,\n", + "http://media.example.com/fileSequence53-A.ts\n" + ) + }, + test_master_playlist => { MasterPlaylist::builder() .variants(vec![ VariantStream::ExtXStreamInf { @@ -75,32 +221,19 @@ fn test_master_playlist() { ]) .build() .unwrap(), - master_playlist - ); -} - -#[test] -fn test_master_playlist_with_i_frames() { - // https://tools.ietf.org/html/rfc8216#section-8.5 - let master_playlist = concat!( - "#EXTM3U\n", - "#EXT-X-STREAM-INF:BANDWIDTH=1280000\n", - "low/audio-video.m3u8\n", - "#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=86000,URI=\"low/iframe.m3u8\"\n", - "#EXT-X-STREAM-INF:BANDWIDTH=2560000\n", - "mid/audio-video.m3u8\n", - "#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=150000,URI=\"mid/iframe.m3u8\"\n", - "#EXT-X-STREAM-INF:BANDWIDTH=7680000\n", - "hi/audio-video.m3u8\n", - // this one: - "#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=550000,URI=\"hi/iframe.m3u8\"\n", - "#EXT-X-STREAM-INF:BANDWIDTH=65000,CODECS=\"mp4a.40.5\"\n", - "audio-only.m3u8" - ) - .parse::() - .unwrap(); - - assert_eq!( + concat!( + "#EXTM3U\n", + "#EXT-X-STREAM-INF:BANDWIDTH=1280000,AVERAGE-BANDWIDTH=1000000\n", + "http://example.com/low.m3u8\n", + "#EXT-X-STREAM-INF:BANDWIDTH=2560000,AVERAGE-BANDWIDTH=2000000\n", + "http://example.com/mid.m3u8\n", + "#EXT-X-STREAM-INF:BANDWIDTH=7680000,AVERAGE-BANDWIDTH=6000000\n", + "http://example.com/hi.m3u8\n", + "#EXT-X-STREAM-INF:BANDWIDTH=65000,CODECS=\"mp4a.40.5\"\n", + "http://example.com/audio-only.m3u8\n" + ) + }, + test_master_playlist_with_i_frames => { MasterPlaylist::builder() .variants(vec![ VariantStream::ExtXStreamInf { @@ -154,38 +287,22 @@ fn test_master_playlist_with_i_frames() { ]) .build() .unwrap(), - master_playlist - ); -} - -#[test] -fn test_master_playlist_with_alternative_audio() { - // https://tools.ietf.org/html/rfc8216#section-8.6 - // TODO: I think the CODECS=\"..." have to be replaced. - let master_playlist = concat!( - "#EXTM3U\n", - "#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID=\"aac\",NAME=\"English\", ", - "DEFAULT=YES,AUTOSELECT=YES,LANGUAGE=\"en\", ", - "URI=\"main/english-audio.m3u8\"\n", - "#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID=\"aac\",NAME=\"Deutsch\", ", - "DEFAULT=NO,AUTOSELECT=YES,LANGUAGE=\"de\", ", - "URI=\"main/german-audio.m3u8\"\n", - "#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID=\"aac\",NAME=\"Commentary\", ", - "DEFAULT=NO,AUTOSELECT=NO,LANGUAGE=\"en\", ", - "URI=\"commentary/audio-only.m3u8\"\n", - "#EXT-X-STREAM-INF:BANDWIDTH=1280000,CODECS=\"...\",AUDIO=\"aac\"\n", - "low/video-only.m3u8\n", - "#EXT-X-STREAM-INF:BANDWIDTH=2560000,CODECS=\"...\",AUDIO=\"aac\"\n", - "mid/video-only.m3u8\n", - "#EXT-X-STREAM-INF:BANDWIDTH=7680000,CODECS=\"...\",AUDIO=\"aac\"\n", - "hi/video-only.m3u8\n", - "#EXT-X-STREAM-INF:BANDWIDTH=65000,CODECS=\"mp4a.40.5\",AUDIO=\"aac\"\n", - "main/english-audio.m3u8" - ) - .parse::() - .unwrap(); - - assert_eq!( + concat!( + "#EXTM3U\n", + "#EXT-X-STREAM-INF:BANDWIDTH=1280000\n", + "low/audio-video.m3u8\n", + "#EXT-X-I-FRAME-STREAM-INF:URI=\"low/iframe.m3u8\",BANDWIDTH=86000\n", + "#EXT-X-STREAM-INF:BANDWIDTH=2560000\n", + "mid/audio-video.m3u8\n", + "#EXT-X-I-FRAME-STREAM-INF:URI=\"mid/iframe.m3u8\",BANDWIDTH=150000\n", + "#EXT-X-STREAM-INF:BANDWIDTH=7680000\n", + "hi/audio-video.m3u8\n", + "#EXT-X-I-FRAME-STREAM-INF:URI=\"hi/iframe.m3u8\",BANDWIDTH=550000\n", + "#EXT-X-STREAM-INF:BANDWIDTH=65000,CODECS=\"mp4a.40.5\"\n", + "audio-only.m3u8\n" + ) + }, + test_master_playlist_with_alternative_audio => { MasterPlaylist::builder() .media(vec![ ExtXMedia::builder() @@ -271,44 +388,46 @@ fn test_master_playlist_with_alternative_audio() { ]) .build() .unwrap(), - master_playlist - ); -} + concat!( + "#EXTM3U\n", + "#EXT-X-MEDIA:", + "TYPE=AUDIO,", + "URI=\"main/english-audio.m3u8\",", + "GROUP-ID=\"aac\",", + "LANGUAGE=\"en\",", + "NAME=\"English\",", + "DEFAULT=YES,", + "AUTOSELECT=YES\n", -#[test] -fn test_master_playlist_with_alternative_video() { - // https://tools.ietf.org/html/rfc8216#section-8.7 - let master_playlist = concat!( - "#EXTM3U\n", - "#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID=\"low\",NAME=\"Main\", ", - "AUTOSELECT=YES,DEFAULT=YES,URI=\"low/main/audio-video.m3u8\"\n", - "#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID=\"low\",NAME=\"Centerfield\", ", - "DEFAULT=NO,URI=\"low/centerfield/audio-video.m3u8\"\n", - "#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID=\"low\",NAME=\"Dugout\", ", - "DEFAULT=NO,URI=\"low/dugout/audio-video.m3u8\"\n", - "#EXT-X-STREAM-INF:BANDWIDTH=1280000,CODECS=\"...\",VIDEO=\"low\"\n", - "low/main/audio-video.m3u8\n", - "#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID=\"mid\",NAME=\"Main\", ", - "AUTOSELECT=YES,DEFAULT=YES,URI=\"mid/main/audio-video.m3u8\"\n", - "#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID=\"mid\",NAME=\"Centerfield\", ", - "DEFAULT=NO,URI=\"mid/centerfield/audio-video.m3u8\"\n", - "#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID=\"mid\",NAME=\"Dugout\", ", - "DEFAULT=NO,URI=\"mid/dugout/audio-video.m3u8\"\n", - "#EXT-X-STREAM-INF:BANDWIDTH=2560000,CODECS=\"...\",VIDEO=\"mid\"\n", - "mid/main/audio-video.m3u8\n", - "#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID=\"hi\",NAME=\"Main\",", - "AUTOSELECT=YES,DEFAULT=YES,URI=\"hi/main/audio-video.m3u8\"\n", - "#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID=\"hi\",NAME=\"Centerfield\", ", - "DEFAULT=NO,URI=\"hi/centerfield/audio-video.m3u8\"\n", - "#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID=\"hi\",NAME=\"Dugout\", ", - "DEFAULT=NO,URI=\"hi/dugout/audio-video.m3u8\"\n", - "#EXT-X-STREAM-INF:BANDWIDTH=7680000,CODECS=\"...\",VIDEO=\"hi\"\n", - "hi/main/audio-video.m3u8" - ) - .parse::() - .unwrap(); + "#EXT-X-MEDIA:", + "TYPE=AUDIO,", + "URI=\"main/german-audio.m3u8\",", + "GROUP-ID=\"aac\",", + "LANGUAGE=\"de\",", + "NAME=\"Deutsch\",", + "AUTOSELECT=YES\n", - assert_eq!( + "#EXT-X-MEDIA:", + "TYPE=AUDIO,", + "URI=\"commentary/audio-only.m3u8\",", + "GROUP-ID=\"aac\",", + "LANGUAGE=\"en\",", + "NAME=\"Commentary\"\n", + + "#EXT-X-STREAM-INF:BANDWIDTH=1280000,CODECS=\"...\",AUDIO=\"aac\"\n", + "low/video-only.m3u8\n", + + "#EXT-X-STREAM-INF:BANDWIDTH=2560000,CODECS=\"...\",AUDIO=\"aac\"\n", + "mid/video-only.m3u8\n", + + "#EXT-X-STREAM-INF:BANDWIDTH=7680000,CODECS=\"...\",AUDIO=\"aac\"\n", + "hi/video-only.m3u8\n", + + "#EXT-X-STREAM-INF:BANDWIDTH=65000,CODECS=\"mp4a.40.5\",AUDIO=\"aac\"\n", + "main/english-audio.m3u8\n" + ) + }, + test_master_playlist_with_alternative_video => { MasterPlaylist::builder() .media(vec![ // low @@ -433,6 +552,80 @@ fn test_master_playlist_with_alternative_video() { ]) .build() .unwrap(), - master_playlist - ); -} + concat!( + "#EXTM3U\n", + "#EXT-X-MEDIA:", + "TYPE=VIDEO,", + "URI=\"low/main/audio-video.m3u8\",", + "GROUP-ID=\"low\",", + "NAME=\"Main\",", + "DEFAULT=YES,", + "AUTOSELECT=YES", + "\n", + + "#EXT-X-MEDIA:", + "TYPE=VIDEO,", + "URI=\"low/centerfield/audio-video.m3u8\",", + "GROUP-ID=\"low\",", + "NAME=\"Centerfield\"", + "\n", + + "#EXT-X-MEDIA:", + "TYPE=VIDEO,", + "URI=\"low/dugout/audio-video.m3u8\",", + "GROUP-ID=\"low\",", + "NAME=\"Dugout\"", + "\n", + + + "#EXT-X-MEDIA:", + "TYPE=VIDEO,", + "URI=\"mid/main/audio-video.m3u8\",", + "GROUP-ID=\"mid\",", + "NAME=\"Main\",", + "DEFAULT=YES,", + "AUTOSELECT=YES\n", + + "#EXT-X-MEDIA:", + "TYPE=VIDEO,", + "URI=\"mid/centerfield/audio-video.m3u8\",", + "GROUP-ID=\"mid\",", + "NAME=\"Centerfield\"\n", + + "#EXT-X-MEDIA:", + "TYPE=VIDEO,", + "URI=\"mid/dugout/audio-video.m3u8\",", + "GROUP-ID=\"mid\",", + "NAME=\"Dugout\"\n", + + "#EXT-X-MEDIA:", + "TYPE=VIDEO,", + "URI=\"hi/main/audio-video.m3u8\",", + "GROUP-ID=\"hi\",", + "NAME=\"Main\",", + "DEFAULT=YES,", + "AUTOSELECT=YES\n", + + "#EXT-X-MEDIA:", + "TYPE=VIDEO,", + "URI=\"hi/centerfield/audio-video.m3u8\",", + "GROUP-ID=\"hi\",", + "NAME=\"Centerfield\"\n", + + "#EXT-X-MEDIA:", + "TYPE=VIDEO,", + "URI=\"hi/dugout/audio-video.m3u8\",", + "GROUP-ID=\"hi\",", + "NAME=\"Dugout\"\n", + + "#EXT-X-STREAM-INF:BANDWIDTH=1280000,CODECS=\"...\",VIDEO=\"low\"\n", + "low/main/audio-video.m3u8\n", + + "#EXT-X-STREAM-INF:BANDWIDTH=2560000,CODECS=\"...\",VIDEO=\"mid\"\n", + "mid/main/audio-video.m3u8\n", + + "#EXT-X-STREAM-INF:BANDWIDTH=7680000,CODECS=\"...\",VIDEO=\"hi\"\n", + "hi/main/audio-video.m3u8\n", + ) + } +];