@ -96,6 +96,15 @@ QUnit.module('HLS', {
// save and restore browser detection for the Firefox-specific tests
this . old . IS_FIREFOX = videojs . browser . IS_FIREFOX ;
this . standardXHRResponse = ( request , data ) => {
standardXHRResponse ( request , data ) ;
// Because SegmentLoader#fillBuffer_ is now scheduled asynchronously
// we have to use clock.tick to get the expected side effects of
// SegmentLoader#handleUpdateEnd_
this . clock . tick ( 1 ) ;
} ;
// setup a player
this . player = createPlayer ( ) ;
} ,
@ -145,7 +154,7 @@ QUnit.test('starts playing if autoplay is specified', function(assert) {
// make sure play() is called *after* the media source opens
openMediaSource ( this . player , this . clock ) ;
standardXHRResponse ( this . requests [ 0 ] ) ;
this . standardXHRResponse ( this . requests [ 0 ] ) ;
assert . ok ( ! this . player . paused ( ) , 'not paused' ) ;
} ) ;
@ -157,8 +166,8 @@ QUnit.test('stats are reset on each new source', function(assert) {
// make sure play() is called *after* the media source opens
openMediaSource ( this . player , this . clock ) ;
standardXHRResponse ( this . requests . shift ( ) ) ;
standardXHRResponse ( this . requests . shift ( ) ) ;
this . standardXHRResponse ( this . requests . shift ( ) ) ;
this . standardXHRResponse ( this . requests . shift ( ) ) ;
assert . equal ( this . player . tech_ . hls . stats . mediaBytesTransferred , 1024 , 'stat is set' ) ;
this . player . src ( {
@ -177,7 +186,7 @@ QUnit.test('XHR requests first byte range on play', function(assert) {
this . clock . tick ( 1 ) ;
this . player . tech_ . trigger ( 'play' ) ;
openMediaSource ( this . player , this . clock ) ;
standardXHRResponse ( this . requests [ 0 ] ) ;
this . standardXHRResponse ( this . requests [ 0 ] ) ;
assert . equal ( this . requests [ 1 ] . headers . Range , 'bytes=0-522827' ) ;
} ) ;
@ -188,10 +197,10 @@ QUnit.test('Seeking requests correct byte range', function(assert) {
} ) ;
this . player . tech_ . trigger ( 'play' ) ;
openMediaSource ( this . player , this . clock ) ;
standardXHRResponse ( this . requests [ 0 ] ) ;
this . standardXHRResponse ( this . requests [ 0 ] ) ;
this . clock . tick ( 1 ) ;
this . player . currentTime ( 41 ) ;
this . clock . tick ( 1 ) ;
this . clock . tick ( 2 ) ;
assert . equal ( this . requests [ 2 ] . headers . Range , 'bytes=2299992-2835603' ) ;
} ) ;
@ -208,7 +217,7 @@ QUnit.test('autoplay seeks to the live point after playlist load', function(asse
} ) ;
openMediaSource ( this . player , this . clock ) ;
this . player . tech_ . trigger ( 'play' ) ;
standardXHRResponse ( this . requests . shift ( ) ) ;
this . standardXHRResponse ( this . requests . shift ( ) ) ;
this . clock . tick ( 1 ) ;
assert . notEqual ( currentTime , 0 , 'seeked on autoplay' ) ;
@ -227,7 +236,7 @@ QUnit.test('autoplay seeks to the live point after media source open', function(
} ) ;
this . player . tech_ . triggerReady ( ) ;
this . clock . tick ( 1 ) ;
standardXHRResponse ( this . requests . shift ( ) ) ;
this . standardXHRResponse ( this . requests . shift ( ) ) ;
openMediaSource ( this . player , this . clock ) ;
this . player . tech_ . trigger ( 'play' ) ;
this . clock . tick ( 1 ) ;
@ -242,7 +251,7 @@ QUnit.test('duration is set when the source opens after the playlist is loaded',
} ) ;
this . player . tech_ . triggerReady ( ) ;
this . clock . tick ( 1 ) ;
standardXHRResponse ( this . requests . shift ( ) ) ;
this . standardXHRResponse ( this . requests . shift ( ) ) ;
openMediaSource ( this . player , this . clock ) ;
assert . equal ( this . player . tech_ . hls . mediaSource . duration ,
@ -269,7 +278,7 @@ QUnit.test('codecs are passed to the source buffer', function(assert) {
'#EXTM3U\n' +
'#EXT-X-STREAM-INF:CODECS="avc1.dd00dd, mp4a.40.f"\n' +
'media.m3u8\n' ) ;
standardXHRResponse ( this . requests . shift ( ) ) ;
this . standardXHRResponse ( this . requests . shift ( ) ) ;
assert . equal ( codecs . length , 1 , 'created a source buffer' ) ;
assert . equal ( codecs [ 0 ] , 'video/mp2t; codecs="avc1.dd00dd, mp4a.40.f"' , 'specified the codecs' ) ;
} ) ;
@ -296,7 +305,7 @@ QUnit.test('creates a PlaylistLoader on init', function(assert) {
openMediaSource ( this . player , this . clock ) ;
assert . equal ( this . requests [ 0 ] . aborted , true , 'aborted previous src' ) ;
standardXHRResponse ( this . requests [ 1 ] ) ;
this . standardXHRResponse ( this . requests [ 1 ] ) ;
assert . ok ( this . player . tech_ . hls . playlists . master ,
'set the master playlist' ) ;
assert . ok ( this . player . tech_ . hls . playlists . media ( ) ,
@ -320,7 +329,7 @@ QUnit.test('sets the duration if one is available on the playlist', function(ass
events ++ ;
} ) ;
standardXHRResponse ( this . requests [ 0 ] ) ;
this . standardXHRResponse ( this . requests [ 0 ] ) ;
assert . equal ( this . player . tech_ . hls . mediaSource . duration ,
40 ,
'set the duration' ) ;
@ -340,7 +349,7 @@ QUnit.test('estimates individual segment durations if needed', function(assert)
changes ++ ;
} ) ;
standardXHRResponse ( this . requests [ 0 ] ) ;
this . standardXHRResponse ( this . requests [ 0 ] ) ;
assert . strictEqual ( this . player . tech_ . hls . mediaSource . duration ,
this . player . tech_ . hls . playlists . media ( ) . segments . length * 10 ,
'duration is updated' ) ;
@ -383,8 +392,8 @@ QUnit.test('starts downloading a segment on loadedmetadata', function(assert) {
} ;
openMediaSource ( this . player , this . clock ) ;
standardXHRResponse ( this . requests [ 0 ] ) ;
standardXHRResponse ( this . requests [ 1 ] ) ;
this . standardXHRResponse ( this . requests [ 0 ] ) ;
this . standardXHRResponse ( this . requests [ 1 ] ) ;
assert . strictEqual ( this . requests [ 1 ] . url ,
absoluteUrl ( 'manifest/media-00001.ts' ) ,
'the first segment is requested' ) ;
@ -409,8 +418,8 @@ QUnit.test('re-initializes the handler for each source', function(assert) {
openMediaSource ( this . player , this . clock ) ;
firstPlaylists = this . player . tech_ . hls . playlists ;
firstMSE = this . player . tech_ . hls . mediaSource ;
standardXHRResponse ( this . requests . shift ( ) ) ;
standardXHRResponse ( this . requests . shift ( ) ) ;
this . standardXHRResponse ( this . requests . shift ( ) ) ;
this . standardXHRResponse ( this . requests . shift ( ) ) ;
masterPlaylistController = this . player . tech_ . hls . masterPlaylistController_ ;
masterPlaylistController . mainSegmentLoader_ . sourceUpdater_ . sourceBuffer_ . abort = ( ) => {
aborts ++ ;
@ -453,9 +462,9 @@ QUnit.test('downloads media playlists after loading the master', function(assert
openMediaSource ( this . player , this . clock ) ;
this . player . tech_ . hls . bandwidth = 20 e10 ;
standardXHRResponse ( this . requests [ 0 ] ) ;
standardXHRResponse ( this . requests [ 1 ] ) ;
standardXHRResponse ( this . requests [ 2 ] ) ;
this . standardXHRResponse ( this . requests [ 0 ] ) ;
this . standardXHRResponse ( this . requests [ 1 ] ) ;
this . standardXHRResponse ( this . requests [ 2 ] ) ;
assert . strictEqual ( this . requests [ 0 ] . url ,
'manifest/master.m3u8' ,
@ -522,9 +531,9 @@ QUnit.test('upshifts if the initial bandwidth hint is high', function(assert) {
openMediaSource ( this . player , this . clock ) ;
this . player . tech_ . hls . bandwidth = 10 e20 ;
standardXHRResponse ( this . requests [ 0 ] ) ;
standardXHRResponse ( this . requests [ 1 ] ) ;
standardXHRResponse ( this . requests [ 2 ] ) ;
this . standardXHRResponse ( this . requests [ 0 ] ) ;
this . standardXHRResponse ( this . requests [ 1 ] ) ;
this . standardXHRResponse ( this . requests [ 2 ] ) ;
assert . strictEqual (
this . requests [ 0 ] . url ,
@ -555,9 +564,9 @@ QUnit.test('downshifts if the initial bandwidth hint is low', function(assert) {
openMediaSource ( this . player , this . clock ) ;
this . player . tech_ . hls . bandwidth = 100 ;
standardXHRResponse ( this . requests [ 0 ] ) ;
standardXHRResponse ( this . requests [ 1 ] ) ;
standardXHRResponse ( this . requests [ 2 ] ) ;
this . standardXHRResponse ( this . requests [ 0 ] ) ;
this . standardXHRResponse ( this . requests [ 1 ] ) ;
this . standardXHRResponse ( this . requests [ 2 ] ) ;
assert . strictEqual ( this . requests [ 0 ] . url ,
'manifest/master.m3u8' ,
@ -596,9 +605,9 @@ QUnit.test('buffer checks are noops when only the master is ready', function(ass
} ) ;
openMediaSource ( this . player , this . clock ) ;
// master
standardXHRResponse ( this . requests . shift ( ) ) ;
this . standardXHRResponse ( this . requests . shift ( ) ) ;
// media
standardXHRResponse ( this . requests . shift ( ) ) ;
this . standardXHRResponse ( this . requests . shift ( ) ) ;
// ignore any outstanding segment requests
this . requests . length = 0 ;
@ -614,7 +623,7 @@ QUnit.test('buffer checks are noops when only the master is ready', function(ass
// force media1 to be requested
this . player . tech_ . hls . bandwidth = 1 ;
// master
standardXHRResponse ( this . requests . shift ( ) ) ;
this . standardXHRResponse ( this . requests . shift ( ) ) ;
this . clock . tick ( 10 * 1000 ) ;
assert . strictEqual ( 1 , this . requests . length , 'one request was made' ) ;
@ -634,7 +643,7 @@ QUnit.test('selects a playlist below the current bandwidth', function(assert) {
type : 'application/vnd.apple.mpegurl'
} ) ;
openMediaSource ( this . player , this . clock ) ;
standardXHRResponse ( this . requests [ 0 ] ) ;
this . standardXHRResponse ( this . requests [ 0 ] ) ;
// the default playlist has a really high bitrate
this . player . tech_ . hls . playlists . master . playlists [ 0 ] . attributes . BANDWIDTH = 9 e10 ;
@ -682,7 +691,7 @@ QUnit.test('raises the minimum bitrate for a stream proportionially', function(a
} ) ;
openMediaSource ( this . player , this . clock ) ;
standardXHRResponse ( this . requests [ 0 ] ) ;
this . standardXHRResponse ( this . requests [ 0 ] ) ;
// the default playlist's bandwidth + 10% is assert.equal to the current bandwidth
this . player . tech_ . hls . playlists . master . playlists [ 0 ] . attributes . BANDWIDTH = 10 ;
@ -709,7 +718,7 @@ QUnit.test('uses the lowest bitrate if no other is suitable', function(assert) {
} ) ;
openMediaSource ( this . player , this . clock ) ;
standardXHRResponse ( this . requests [ 0 ] ) ;
this . standardXHRResponse ( this . requests [ 0 ] ) ;
// the lowest bitrate playlist is much greater than 1b/s
this . player . tech_ . hls . bandwidth = 1 ;
@ -733,7 +742,7 @@ QUnit.test('selects the correct rendition by tech dimensions', function(assert)
type : 'application/vnd.apple.mpegurl'
} ) ;
openMediaSource ( this . player , this . clock ) ;
standardXHRResponse ( this . requests [ 0 ] ) ;
this . standardXHRResponse ( this . requests [ 0 ] ) ;
hls = this . player . tech_ . hls ;
@ -808,7 +817,7 @@ QUnit.test('selects the highest bitrate playlist when the player dimensions are
'#EXT-X-STREAM-INF:BANDWIDTH=1,RESOLUTION=1x1\n' +
'media1.m3u8\n' ) ;
// media
standardXHRResponse ( this . requests . shift ( ) ) ;
this . standardXHRResponse ( this . requests . shift ( ) ) ;
this . player . tech_ . hls . bandwidth = 1 e10 ;
this . player . width ( 1024 ) ;
@ -842,7 +851,7 @@ QUnit.test('filters playlists that are currently excluded', function(assert) {
'#EXT-X-STREAM-INF:BANDWIDTH=1\n' +
'media1.m3u8\n' ) ;
// media
standardXHRResponse ( this . requests . shift ( ) ) ;
this . standardXHRResponse ( this . requests . shift ( ) ) ;
// exclude the current playlist
this . player . tech_ . hls . playlists . master . playlists [ 0 ] . excludeUntil = + new Date ( ) + 1000 ;
@ -882,7 +891,7 @@ QUnit.test('does not blacklist compatible H.264 codec strings', function(assert)
'media1.m3u8\n' ) ;
// media
standardXHRResponse ( this . requests . shift ( ) ) ;
this . standardXHRResponse ( this . requests . shift ( ) ) ;
master = this . player . tech_ . hls . playlists . master ;
assert . strictEqual ( typeof master . playlists [ 0 ] . excludeUntil ,
'undefined' ,
@ -915,7 +924,7 @@ QUnit.test('does not blacklist compatible AAC codec strings', function(assert) {
'media1.m3u8\n' ) ;
// media
standardXHRResponse ( this . requests . shift ( ) ) ;
this . standardXHRResponse ( this . requests . shift ( ) ) ;
master = this . player . tech_ . hls . playlists . master ;
assert . strictEqual ( typeof master . playlists [ 0 ] . excludeUntil ,
'undefined' ,
@ -934,7 +943,7 @@ QUnit.test('cancels outstanding XHRs when seeking', function(assert) {
type : 'application/vnd.apple.mpegurl'
} ) ;
openMediaSource ( this . player , this . clock ) ;
standardXHRResponse ( this . requests [ 0 ] ) ;
this . standardXHRResponse ( this . requests [ 0 ] ) ;
this . player . tech_ . hls . media = {
segments : [ {
uri : '0.ts' ,
@ -947,7 +956,7 @@ QUnit.test('cancels outstanding XHRs when seeking', function(assert) {
// attempt to seek while the download is in progress
this . player . currentTime ( 7 ) ;
this . clock . tick ( 1 ) ;
this . clock . tick ( 2 ) ;
assert . ok ( this . requests [ 1 ] . aborted , 'XHR aborted' ) ;
assert . strictEqual ( this . requests . length , 3 , 'opened new XHR' ) ;
@ -959,7 +968,7 @@ QUnit.test('does not abort segment loading for in-buffer seeking', function(asse
type : 'application/vnd.apple.mpegurl'
} ) ;
openMediaSource ( this . player , this . clock ) ;
standardXHRResponse ( this . requests . shift ( ) ) ;
this . standardXHRResponse ( this . requests . shift ( ) ) ;
this . player . tech_ . buffered = function ( ) {
return videojs . createTimeRange ( 0 , 20 ) ;
} ;
@ -991,9 +1000,9 @@ QUnit.test('segment 404 should trigger blacklisting of media', function(assert)
this . player . tech_ . hls . bandwidth = 20000 ;
// master
standardXHRResponse ( this . requests [ 0 ] ) ;
this . standardXHRResponse ( this . requests [ 0 ] ) ;
// media
standardXHRResponse ( this . requests [ 1 ] ) ;
this . standardXHRResponse ( this . requests [ 1 ] ) ;
media = this . player . tech_ . hls . playlists . media_ ;
@ -1075,7 +1084,7 @@ QUnit.test('fire loadedmetadata once we successfully load a playlist', function(
count += 1 ;
} ) ;
// master
standardXHRResponse ( this . requests . shift ( ) ) ;
this . standardXHRResponse ( this . requests . shift ( ) ) ;
assert . equal ( count , 0 ,
'loadedMedia not triggered before requesting playlist' ) ;
// media
@ -1085,7 +1094,7 @@ QUnit.test('fire loadedmetadata once we successfully load a playlist', function(
assert . equal ( this . env . log . warn . calls , 1 , 'warning logged for blacklist' ) ;
// media
standardXHRResponse ( this . requests . shift ( ) ) ;
this . standardXHRResponse ( this . requests . shift ( ) ) ;
assert . equal ( count , 1 ,
'loadedMedia triggered after successful recovery from 404' ) ;
@ -1100,7 +1109,7 @@ QUnit.test('sets seekable and duration for live playlists', function(assert) {
} ) ;
openMediaSource ( this . player , this . clock ) ;
standardXHRResponse ( this . requests [ 0 ] ) ;
this . standardXHRResponse ( this . requests [ 0 ] ) ;
assert . equal ( this . player . tech_ . hls . mediaSource . seekable . length ,
1 ,
@ -1124,7 +1133,7 @@ QUnit.test('live playlist starts with correct currentTime value', function(asser
} ) ;
openMediaSource ( this . player , this . clock ) ;
standardXHRResponse ( this . requests [ 0 ] ) ;
this . standardXHRResponse ( this . requests [ 0 ] ) ;
this . player . tech_ . hls . playlists . trigger ( 'loadedmetadata' ) ;
@ -1148,7 +1157,7 @@ QUnit.test('estimates seekable ranges for live streams that have been paused for
} ) ;
openMediaSource ( this . player , this . clock ) ;
standardXHRResponse ( this . requests . shift ( ) ) ;
this . standardXHRResponse ( this . requests . shift ( ) ) ;
this . player . tech_ . hls . playlists . media ( ) . mediaSequence = 172 ;
this . player . tech_ . hls . playlists . media ( ) . syncInfo = {
mediaSequence : 130 ,
@ -1347,7 +1356,7 @@ QUnit.test('can seek before the source buffer opens', function(assert) {
} ) ;
this . player . tech_ . triggerReady ( ) ;
this . clock . tick ( 1 ) ;
standardXHRResponse ( this . requests . shift ( ) ) ;
this . standardXHRResponse ( this . requests . shift ( ) ) ;
this . player . triggerReady ( ) ;
this . player . currentTime ( 1 ) ;
@ -1363,15 +1372,15 @@ QUnit.test('resets the switching algorithm if a request times out', function(ass
this . player . tech_ . hls . bandwidth = 1 e20 ;
// master
standardXHRResponse ( this . requests . shift ( ) ) ;
this . standardXHRResponse ( this . requests . shift ( ) ) ;
// media.m3u8
standardXHRResponse ( this . requests . shift ( ) ) ;
this . standardXHRResponse ( this . requests . shift ( ) ) ;
// simulate a segment timeout
this . requests [ 0 ] . timedout = true ;
// segment
this . requests . shift ( ) . abort ( ) ;
standardXHRResponse ( this . requests . shift ( ) ) ;
this . standardXHRResponse ( this . requests . shift ( ) ) ;
assert . strictEqual ( this . player . tech_ . hls . playlists . media ( ) ,
this . player . tech_ . hls . playlists . master . playlists [ 1 ] ,
@ -1423,8 +1432,8 @@ QUnit.test('remove event handlers on dispose', function(assert) {
} ) ;
openMediaSource ( player , this . clock ) ;
standardXHRResponse ( this . requests [ 0 ] ) ;
standardXHRResponse ( this . requests [ 1 ] ) ;
this . standardXHRResponse ( this . requests [ 0 ] ) ;
this . standardXHRResponse ( this . requests [ 1 ] ) ;
player . dispose ( ) ;
@ -1517,7 +1526,7 @@ QUnit.test('re-emits mediachange events', function(assert) {
type : 'application/vnd.apple.mpegurl'
} ) ;
openMediaSource ( this . player , this . clock ) ;
standardXHRResponse ( this . requests . shift ( ) ) ;
this . standardXHRResponse ( this . requests . shift ( ) ) ;
this . player . tech_ . hls . playlists . trigger ( 'mediachange' ) ;
assert . strictEqual ( mediaChanges , 1 , 'fired mediachange' ) ;
@ -1568,12 +1577,15 @@ QUnit.test('calling play() at the end of a video replays', function(assert) {
'#EXTINF:10,\n' +
'0.ts\n' +
'#EXT-X-ENDLIST\n' ) ;
standardXHRResponse ( this . requests . shift ( ) ) ;
this . clock . tick ( 1 ) ;
this . standardXHRResponse ( this . requests . shift ( ) ) ;
this . player . tech_ . ended = function ( ) {
return true ;
} ;
this . player . tech_ . trigger ( 'play' ) ;
this . clock . tick ( 1 ) ;
assert . equal ( seekTime , 0 , 'seeked to the beginning' ) ;
// verify stats
@ -1592,6 +1604,8 @@ QUnit.test('keys are resolved relative to the master playlist', function(assert)
'#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=17\n' +
'playlist/playlist.m3u8\n' +
'#EXT-X-ENDLIST\n' ) ;
this . clock . tick ( 1 ) ;
this . requests . shift ( ) . respond ( 200 , null ,
'#EXTM3U\n' +
'#EXT-X-TARGETDURATION:15\n' +
@ -1599,6 +1613,8 @@ QUnit.test('keys are resolved relative to the master playlist', function(assert)
'#EXTINF:2.833,\n' +
'http://media.example.com/fileSequence1.ts\n' +
'#EXT-X-ENDLIST\n' ) ;
this . clock . tick ( 1 ) ;
assert . equal ( this . requests . length , 2 , 'requested the key' ) ;
assert . equal ( this . requests [ 0 ] . url ,
absoluteUrl ( 'video/playlist/keys/key.php' ) ,
@ -1621,6 +1637,8 @@ QUnit.test('keys are resolved relative to their containing playlist', function(a
'#EXTINF:2.833,\n' +
'http://media.example.com/fileSequence1.ts\n' +
'#EXT-X-ENDLIST\n' ) ;
this . clock . tick ( 1 ) ;
assert . equal ( this . requests . length , 2 , 'requested a key' ) ;
assert . equal ( this . requests [ 0 ] . url ,
absoluteUrl ( 'video/keys/key.php' ) ,
@ -1644,11 +1662,13 @@ QUnit.test('seeking should abort an outstanding key request and create a new one
'#EXTINF:9,\n' +
'http://media.example.com/fileSequence2.ts\n' +
'#EXT-X-ENDLIST\n' ) ;
this . clock . tick ( 1 ) ;
// segment 1
standardXHRResponse ( this . requests . pop ( ) ) ;
this . standardXHRResponse ( this . requests . pop ( ) ) ;
this . player . currentTime ( 11 ) ;
this . clock . tick ( 1 ) ;
this . clock . tick ( 2 ) ;
assert . ok ( this . requests [ 0 ] . aborted , 'the key XHR should be aborted' ) ;
// aborted key 1
this . requests . shift ( ) ;
@ -1682,13 +1702,16 @@ QUnit.test('switching playlists with an outstanding key request aborts request a
} ) ;
openMediaSource ( this . player , this . clock ) ;
this . player . tech_ . trigger ( 'play' ) ;
this . clock . tick ( 1 ) ;
// master playlist
standardXHRResponse ( this . requests . shift ( ) ) ;
this . standardXHRResponse ( this . requests . shift ( ) ) ;
// media playlist
this . requests . shift ( ) . respond ( 200 , null , media ) ;
this . clock . tick ( 1 ) ;
// first segment of the original media playlist
standardXHRResponse ( this . requests . pop ( ) ) ;
this . standardXHRResponse ( this . requests . pop ( ) ) ;
assert . equal ( this . requests . length , 1 , 'key request only one outstanding' ) ;
keyXhr = this . requests . shift ( ) ;
@ -1696,6 +1719,7 @@ QUnit.test('switching playlists with an outstanding key request aborts request a
this . player . tech_ . hls . playlists . trigger ( 'mediachanging' ) ;
this . player . tech_ . hls . playlists . trigger ( 'mediachange' ) ;
this . clock . tick ( 1 ) ;
assert . ok ( keyXhr . aborted , 'key request aborted' ) ;
assert . equal ( this . requests . length , 2 , 'loaded key and segment' ) ;
@ -1719,9 +1743,9 @@ QUnit.test('does not download segments if preload option set to none', function(
openMediaSource ( this . player , this . clock ) ;
// master
standardXHRResponse ( this . requests . shift ( ) ) ;
this . standardXHRResponse ( this . requests . shift ( ) ) ;
// media
standardXHRResponse ( this . requests . shift ( ) ) ;
this . standardXHRResponse ( this . requests . shift ( ) ) ;
this . clock . tick ( 10 * 1000 ) ;
this . requests = this . requests . filter ( function ( request ) {
@ -1747,9 +1771,9 @@ QUnit.test('selectPlaylist does not fail if getComputedStyle returns null', func
} ) ;
openMediaSource ( this . player , this . clock ) ;
// master
standardXHRResponse ( this . requests . shift ( ) ) ;
this . standardXHRResponse ( this . requests . shift ( ) ) ;
// media
standardXHRResponse ( this . requests . shift ( ) ) ;
this . standardXHRResponse ( this . requests . shift ( ) ) ;
this . player . tech_ . hls . selectPlaylist ( ) ;
assert . ok ( true , 'should not throw' ) ;
@ -1773,6 +1797,8 @@ QUnit.test('resolves relative key URLs against the playlist', function(assert) {
'#EXTINF:2.833,\n' +
'http://media.example.com/fileSequence52-A.ts\n' +
'#EXT-X-ENDLIST\n' ) ;
this . clock . tick ( 1 ) ;
assert . equal ( this . requests [ 0 ] . url ,
'https://example.com/key.php?r=52' ,
'resolves the key URL' ) ;
@ -1789,9 +1815,9 @@ QUnit.test('adds 1 default audio track if we have not parsed any and the playlis
openMediaSource ( this . player , this . clock ) ;
// master
standardXHRResponse ( this . requests . shift ( ) ) ;
this . standardXHRResponse ( this . requests . shift ( ) ) ;
// media
standardXHRResponse ( this . requests . shift ( ) ) ;
this . standardXHRResponse ( this . requests . shift ( ) ) ;
assert . equal ( this . player . audioTracks ( ) . length , 1 , 'one audio track after load' ) ;
assert . equal ( this . player . audioTracks ( ) [ 0 ] . label , 'default' , 'set the label' ) ;
@ -1817,9 +1843,9 @@ QUnit.test('adds 1 default audio track if in flash mode', function(assert) {
openMediaSource ( this . player , this . clock ) ;
// master
standardXHRResponse ( this . requests . shift ( ) ) ;
this . standardXHRResponse ( this . requests . shift ( ) ) ;
// media
standardXHRResponse ( this . requests . shift ( ) ) ;
this . standardXHRResponse ( this . requests . shift ( ) ) ;
assert . equal ( this . player . audioTracks ( ) . length , 1 , 'one audio track after load' ) ;
assert . equal ( this . player . audioTracks ( ) [ 0 ] . label , 'default' , 'set the label' ) ;
@ -1838,9 +1864,9 @@ QUnit.test('adds audio tracks if we have parsed some from a playlist', function(
openMediaSource ( this . player , this . clock ) ;
// master
standardXHRResponse ( this . requests . shift ( ) ) ;
this . standardXHRResponse ( this . requests . shift ( ) ) ;
// media
standardXHRResponse ( this . requests . shift ( ) ) ;
this . standardXHRResponse ( this . requests . shift ( ) ) ;
let vjsAudioTracks = this . player . audioTracks ( ) ;
assert . equal ( vjsAudioTracks . length , 3 , '3 active vjs tracks' ) ;
@ -1871,9 +1897,9 @@ QUnit.test('when audioinfo changes on an independent audio track in Firefox 48 &
openMediaSource ( this . player , this . clock ) ;
// master
standardXHRResponse ( this . requests . shift ( ) ) ;
this . standardXHRResponse ( this . requests . shift ( ) ) ;
// media
standardXHRResponse ( this . requests . shift ( ) ) ;
this . standardXHRResponse ( this . requests . shift ( ) ) ;
assert . equal ( audioTracks . length , 3 , 'three audio track after load' ) ;
let defaultTrack = mpc . activeAudioGroup ( ) . filter ( ( track ) => {
@ -1910,8 +1936,8 @@ QUnit.test('audioinfo changes with one track, blacklist playlist on Firefox 48 &
assert . equal ( audioTracks . length , 0 , 'zero audio tracks at load time' ) ;
openMediaSource ( this . player , this . clock ) ;
standardXHRResponse ( this . requests . shift ( ) ) ;
standardXHRResponse ( this . requests . shift ( ) ) ;
this . standardXHRResponse ( this . requests . shift ( ) ) ;
this . standardXHRResponse ( this . requests . shift ( ) ) ;
assert . equal ( audioTracks . length , 1 , 'one audio track after load' ) ;
let mpc = this . player . tech_ . hls . masterPlaylistController_ ;
@ -1948,14 +1974,14 @@ QUnit.test('changing audioinfo for muxed audio blacklists the current playlist i
let mpc = hls . masterPlaylistController_ ;
// master
standardXHRResponse ( this . requests . shift ( ) ) ;
this . standardXHRResponse ( this . requests . shift ( ) ) ;
// video media
standardXHRResponse ( this . requests . shift ( ) ) ;
this . standardXHRResponse ( this . requests . shift ( ) ) ;
// video segments
standardXHRResponse ( this . requests . shift ( ) ) ;
standardXHRResponse ( this . requests . shift ( ) ) ;
this . standardXHRResponse ( this . requests . shift ( ) ) ;
this . standardXHRResponse ( this . requests . shift ( ) ) ;
// audio media
standardXHRResponse ( this . requests . shift ( ) ) ;
this . standardXHRResponse ( this . requests . shift ( ) ) ;
// ignore audio requests
this . requests . length = 0 ;
assert . equal ( audioTracks . length , 3 , 'three audio track after load' ) ;
@ -2019,7 +2045,7 @@ QUnit.test('cleans up the buffer when loading live segments', function(assert) {
} ;
this . player . tech_ . hls . bandwidth = 20 e10 ;
this . player . tech_ . triggerReady ( ) ;
standardXHRResponse ( this . requests [ 0 ] ) ;
this . standardXHRResponse ( this . requests [ 0 ] ) ;
this . player . tech_ . hls . playlists . trigger ( 'loadedmetadata' ) ;
this . player . tech_ . trigger ( 'canplay' ) ;
@ -2027,9 +2053,10 @@ QUnit.test('cleans up the buffer when loading live segments', function(assert) {
return false ;
} ;
this . player . tech_ . trigger ( 'play' ) ;
this . clock . tick ( 1 ) ;
// request first playable segment
standardXHRResponse ( this . requests [ 1 ] ) ;
this . standardXHRResponse ( this . requests [ 1 ] ) ;
assert . strictEqual ( this . requests [ 0 ] . url , 'liveStart30sBefore.m3u8' ,
'master playlist requested' ) ;
@ -2075,7 +2102,7 @@ QUnit.test('cleans up the buffer based on currentTime when loading a live segmen
} ;
this . player . tech_ . hls . bandwidth = 20 e10 ;
this . player . tech_ . triggerReady ( ) ;
standardXHRResponse ( this . requests [ 0 ] ) ;
this . standardXHRResponse ( this . requests [ 0 ] ) ;
this . player . tech_ . hls . playlists . trigger ( 'loadedmetadata' ) ;
this . player . tech_ . trigger ( 'canplay' ) ;
@ -2084,13 +2111,14 @@ QUnit.test('cleans up the buffer based on currentTime when loading a live segmen
} ;
this . player . tech_ . trigger ( 'play' ) ;
this . clock . tick ( 1 ) ;
// Change seekable so that it starts *after* the currentTime which was set
// based on the previous seekable range (the end of 80)
seekable = videojs . createTimeRanges ( [ [ 100 , 120 ] ] ) ;
// request first playable segment
standardXHRResponse ( this . requests [ 1 ] ) ;
this . standardXHRResponse ( this . requests [ 1 ] ) ;
assert . strictEqual ( this . requests [ 0 ] . url , 'liveStart30sBefore.m3u8' , 'master playlist requested' ) ;
assert . equal ( removes . length , 1 , 'remove called' ) ;
@ -2129,10 +2157,10 @@ QUnit.test('cleans up the buffer when loading VOD segments', function(assert) {
this . player . width ( 640 ) ;
this . player . height ( 360 ) ;
this . player . tech_ . hls . bandwidth = 20 e10 ;
standardXHRResponse ( this . requests [ 0 ] ) ;
this . standardXHRResponse ( this . requests [ 0 ] ) ;
this . player . currentTime ( 120 ) ;
standardXHRResponse ( this . requests [ 1 ] ) ;
standardXHRResponse ( this . requests [ 2 ] ) ;
this . standardXHRResponse ( this . requests [ 1 ] ) ;
this . standardXHRResponse ( this . requests [ 2 ] ) ;
assert . strictEqual ( this . requests [ 0 ] . url , 'manifest/master.m3u8' ,
'master playlist requested' ) ;
@ -2154,9 +2182,9 @@ QUnit.test('when mediaGroup changes enabled track should not change', function(a
openMediaSource ( this . player , this . clock ) ;
// master
standardXHRResponse ( this . requests . shift ( ) ) ;
this . standardXHRResponse ( this . requests . shift ( ) ) ;
// video media
standardXHRResponse ( this . requests . shift ( ) ) ;
this . standardXHRResponse ( this . requests . shift ( ) ) ;
let hls = this . player . tech_ . hls ;
let mpc = hls . masterPlaylistController_ ;
let audioTracks = this . player . audioTracks ( ) ;
@ -2170,13 +2198,10 @@ QUnit.test('when mediaGroup changes enabled track should not change', function(a
this . requests . length = 0 ;
// force mpc to select a playlist from a new media group
mpc . masterPlaylistLoader_ . media ( mpc . master ( ) . playlists [ 0 ] ) ;
// TODO extra segment requests!!!
this . requests . shift ( ) ;
this . requests . shift ( ) ;
this . clock . tick ( 1 ) ;
// video media
standardXHRResponse ( this . requests . shift ( ) ) ;
this . standardXHRResponse ( this . requests . shift ( ) ) ;
assert . notEqual ( oldMediaGroup , hls . playlists . media ( ) . attributes . AUDIO , 'selected a new playlist' ) ;
audioTracks = this . player . audioTracks ( ) ;
@ -2198,6 +2223,7 @@ QUnit.test('when mediaGroup changes enabled track should not change', function(a
// swap back to the old media group
// this playlist is already loaded so no new requests are made
mpc . masterPlaylistLoader_ . media ( mpc . master ( ) . playlists [ 3 ] ) ;
this . clock . tick ( 1 ) ;
assert . notEqual ( oldMediaGroup , hls . playlists . media ( ) . attributes . AUDIO , 'selected a new playlist' ) ;
audioTracks = this . player . audioTracks ( ) ;
@ -2222,9 +2248,9 @@ QUnit.test('Allows specifying the beforeRequest function on the player', functio
beforeRequestCalled = true ;
} ;
// master
standardXHRResponse ( this . requests . shift ( ) ) ;
this . standardXHRResponse ( this . requests . shift ( ) ) ;
// media
standardXHRResponse ( this . requests . shift ( ) ) ;
this . standardXHRResponse ( this . requests . shift ( ) ) ;
assert . ok ( beforeRequestCalled , 'beforeRequest was called' ) ;
@ -2245,7 +2271,7 @@ QUnit.test('Allows specifying the beforeRequest function globally', function(ass
} ) ;
openMediaSource ( this . player , this . clock ) ;
// master
standardXHRResponse ( this . requests . shift ( ) ) ;
this . standardXHRResponse ( this . requests . shift ( ) ) ;
assert . ok ( beforeRequestCalled , 'beforeRequest was called' ) ;
@ -2273,11 +2299,11 @@ QUnit.test('Allows overriding the global beforeRequest function', function(asser
beforeLocalRequestCalled ++ ;
} ;
// master
standardXHRResponse ( this . requests . shift ( ) ) ;
this . standardXHRResponse ( this . requests . shift ( ) ) ;
// media
standardXHRResponse ( this . requests . shift ( ) ) ;
this . standardXHRResponse ( this . requests . shift ( ) ) ;
// ts
standardXHRResponse ( this . requests . shift ( ) ) ;
this . standardXHRResponse ( this . requests . shift ( ) ) ;
assert . equal ( beforeLocalRequestCalled , 2 , 'local beforeRequest was called twice ' +
'for the media playlist and media' ) ;
@ -2341,9 +2367,9 @@ QUnit.test('populates quality levels list when available', function(assert) {
openMediaSource ( this . player , this . clock ) ;
// master
standardXHRResponse ( this . requests . shift ( ) ) ;
this . standardXHRResponse ( this . requests . shift ( ) ) ;
// media
standardXHRResponse ( this . requests . shift ( ) ) ;
this . standardXHRResponse ( this . requests . shift ( ) ) ;
assert . equal ( addCount , 4 , 'four levels added from master' ) ;
assert . equal ( changeCount , 1 , 'selected initial quality level' ) ;
@ -2356,6 +2382,16 @@ QUnit.module('HLS Integration', {
this . mse = useFakeMediaSource ( ) ;
this . tech = new ( videojs . getTech ( 'Html5' ) ) ( { } ) ;
this . clock = this . env . clock ;
this . standardXHRResponse = ( request , data ) => {
standardXHRResponse ( request , data ) ;
// Because SegmentLoader#fillBuffer_ is now scheduled asynchronously
// we have to use clock.tick to get the expected side effects of
// SegmentLoader#handleUpdateEnd_
this . clock . tick ( 1 ) ;
} ;
videojs . HlsHandler . prototype . setupQualityLevels_ = ( ) => { } ;
} ,
afterEach ( ) {
@ -2375,9 +2411,9 @@ QUnit.test('does not error when MediaSource is not defined', function(assert) {
hls . mediaSource . trigger ( 'sourceopen' ) ;
// master
standardXHRResponse ( this . requests . shift ( ) ) ;
this . standardXHRResponse ( this . requests . shift ( ) ) ;
// media
standardXHRResponse ( this . requests . shift ( ) ) ;
this . standardXHRResponse ( this . requests . shift ( ) ) ;
assert . ok ( true , 'did not throw an exception' ) ;
} ) ;
@ -2390,9 +2426,9 @@ QUnit.test('aborts all in-flight work when disposed', function(assert) {
hls . mediaSource . trigger ( 'sourceopen' ) ;
// master
standardXHRResponse ( this . requests . shift ( ) ) ;
this . standardXHRResponse ( this . requests . shift ( ) ) ;
// media
standardXHRResponse ( this . requests . shift ( ) ) ;
this . standardXHRResponse ( this . requests . shift ( ) ) ;
hls . dispose ( ) ;
assert . ok ( this . requests [ 0 ] . aborted , 'aborted the old segment request' ) ;
@ -2411,12 +2447,12 @@ QUnit.test('stats are reset on dispose', function(assert) {
hls . mediaSource . trigger ( 'sourceopen' ) ;
// master
standardXHRResponse ( this . requests . shift ( ) ) ;
this . standardXHRResponse ( this . requests . shift ( ) ) ;
// media
standardXHRResponse ( this . requests . shift ( ) ) ;
this . standardXHRResponse ( this . requests . shift ( ) ) ;
// media
standardXHRResponse ( this . requests . shift ( ) ) ;
this . standardXHRResponse ( this . requests . shift ( ) ) ;
assert . equal ( hls . stats . mediaBytesTransferred , 1024 , 'stat is set' ) ;
hls . dispose ( ) ;
@ -2467,19 +2503,19 @@ QUnit.test('downloads additional playlists if required', function(assert) {
hls . mediaSource . trigger ( 'sourceopen' ) ;
hls . bandwidth = 1 ;
// master
standardXHRResponse ( this . requests [ 0 ] ) ;
this . standardXHRResponse ( this . requests [ 0 ] ) ;
// media
standardXHRResponse ( this . requests [ 1 ] ) ;
this . standardXHRResponse ( this . requests [ 1 ] ) ;
originalPlaylist = hls . playlists . media ( ) ;
// the playlist selection is revisited after a new segment is downloaded
this . requests [ 2 ] . bandwidth = 3000000 ;
// segment
standardXHRResponse ( this . requests [ 2 ] ) ;
this . standardXHRResponse ( this . requests [ 2 ] ) ;
hls . mediaSource . sourceBuffers [ 0 ] . trigger ( 'updateend' ) ;
// new media
standardXHRResponse ( this . requests [ 3 ] ) ;
this . standardXHRResponse ( this . requests [ 3 ] ) ;
assert . ok ( ( /manifest\/media\d+.m3u8$/ ) . test ( this . requests [ 3 ] . url ) ,
'made a playlist request' ) ;
@ -2506,16 +2542,16 @@ QUnit.test('waits to download new segments until the media playlist is stable',
// make sure we stay on the lowest variant
hls . bandwidth = 1 ;
// master
standardXHRResponse ( this . requests . shift ( ) ) ;
this . standardXHRResponse ( this . requests . shift ( ) ) ;
// media1
standardXHRResponse ( this . requests . shift ( ) ) ;
this . standardXHRResponse ( this . requests . shift ( ) ) ;
// source buffer created after media source is open and first media playlist is selected
sourceBuffer = hls . mediaSource . sourceBuffers [ 0 ] ;
// segment 0
standardXHRResponse ( this . requests . shift ( ) ) ;
this . standardXHRResponse ( this . requests . shift ( ) ) ;
// no time has elapsed, so bandwidth is really high and we'll switch
// playlists
sourceBuffer . trigger ( 'updateend' ) ;
@ -2525,7 +2561,7 @@ QUnit.test('waits to download new segments until the media playlist is stable',
assert . equal ( this . requests . length , 1 , 'delays segment fetching' ) ;
// another media playlist
standardXHRResponse ( this . requests . shift ( ) ) ;
this . standardXHRResponse ( this . requests . shift ( ) ) ;
this . clock . tick ( 10 * 1000 ) ;
assert . equal ( this . requests . length , 1 , 'resumes segment fetching' ) ;
@ -2622,6 +2658,17 @@ QUnit.module('HLS - Encryption', {
this . requests = this . env . requests ;
this . mse = useFakeMediaSource ( ) ;
this . tech = new ( videojs . getTech ( 'Html5' ) ) ( { } ) ;
this . clock = this . env . clock ;
this . standardXHRResponse = ( request , data ) => {
standardXHRResponse ( request , data ) ;
// Because SegmentLoader#fillBuffer_ is now scheduled asynchronously
// we have to use clock.tick to get the expected side effects of
// SegmentLoader#handleUpdateEnd_
this . clock . tick ( 1 ) ;
} ;
videojs . HlsHandler . prototype . setupQualityLevels_ = ( ) => { } ;
} ,
afterEach ( ) {
@ -2648,15 +2695,17 @@ QUnit.test('blacklists playlist if key requests fail', function(assert) {
'#EXTINF:15.0,\n' +
'http://media.example.com/fileSequence53-A.ts\n' +
'#EXT-X-ENDLIST\n' ) ;
this . clock . tick ( 1 ) ;
// segment 1
if ( /key\.php/i . test ( this . requests [ 0 ] . url ) ) {
standardXHRResponse ( this . requests . pop ( ) ) ;
this . standardXHRResponse ( this . requests . pop ( ) ) ;
} else {
standardXHRResponse ( this . requests . shift ( ) ) ;
this . standardXHRResponse ( this . requests . shift ( ) ) ;
}
// fail key
this . requests . shift ( ) . respond ( 404 ) ;
assert . ok ( hls . playlists . media ( ) . excludeUntil > 0 ,
'playlist blacklisted' ) ;
assert . equal ( this . env . log . warn . calls , 1 , 'logged warning for blacklist' ) ;
@ -2680,9 +2729,10 @@ QUnit.test('treats invalid keys as a key request failure and blacklists playlist
'#EXTINF:15.0,\n' +
'http://media.example.com/fileSequence52-B.ts\n' +
'#EXT-X-ENDLIST\n' ) ;
this . clock . tick ( 1 ) ;
// segment request
standardXHRResponse ( this . requests . pop ( ) ) ;
this . standardXHRResponse ( this . requests . pop ( ) ) ;
assert . equal ( this . requests [ 0 ] . url ,
'https://priv.example.com/key.php?r=52' ,
@ -2690,6 +2740,7 @@ QUnit.test('treats invalid keys as a key request failure and blacklists playlist
// keys *should* be 16 bytes long -- this one is too small
this . requests [ 0 ] . response = new Uint8Array ( 1 ) . buffer ;
this . requests . shift ( ) . respond ( 200 , null , '' ) ;
this . clock . tick ( 1 ) ;
// blacklist this playlist
assert . ok ( hls . playlists . media ( ) . excludeUntil > 0 ,