Skip to content
Toggle navigation
Toggle navigation
This project
Loading...
Sign in
brainfood
/
videojs-contrib-hls
Go to a project
Toggle navigation
Toggle navigation pinning
Projects
Groups
Snippets
Help
Project
Activity
Repository
Graphs
Network
Create a new issue
Commits
Issue Boards
Files
Commits
Network
Compare
Branches
Tags
53322e5d
authored
2016-02-26 16:34:23 -0500
by
jrivera
Browse Files
Options
Browse Files
Tag
Download
Plain Diff
Merge branch 'master' into development
2 parents
b5e60aba
8718c2e2
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
387 additions
and
37 deletions
.travis.yml
package.json
src/playlist.js
src/videojs-contrib-hls.js
test/videojs-contrib-hls.test.js
.travis.yml
View file @
53322e5
...
...
@@ -16,3 +16,10 @@ notifications:
before_script
:
-
export DISPLAY=:99.0
-
sh -e /etc/init.d/xvfb start
env
:
global
:
-
secure
:
dM7svnHPPu5IiUMeFWW5zg+iuWNpwt6SSDi3MmVvhSclNMRLesQoRB+7Qq5J/LiKhmjpv1/GlNVV0CTsHMRhZNwQ3fo38eEuTXv99aAflEITXwSEh/VntKViHbGFubn06EnVkJoH6MX3zJ6kbiwc2QdSQbywKzS6l6quUEpWpd0=
-
secure
:
AnduYGXka5ft1x7V3SuVYqvlKLvJGhUaRNFdy4UDJr3ZVuwpQjE4TMDG8REmJIJvXfHbh4qY4N1cFSGnXkZ4bH21Xk0v9DLhsxbarKz+X2BvPgXs+Af9EQ6vLEy/5S1vMLxfT5+y+Ec5bVNGOsdUZby8Y21CRzSg6ADN9kwPGlE=
addons
:
sauce_connect
:
true
firefox
:
latest
...
...
package.json
View file @
53322e5
{
"name"
:
"videojs-contrib-hls"
,
"version"
:
"1.3.
5
"
,
"version"
:
"1.3.
9
"
,
"description"
:
"Play back HLS with video.js, even where it's not natively supported"
,
"main"
:
"es5/videojs-contrib-hls.js"
,
"engines"
:
{
...
...
src/playlist.js
View file @
53322e5
...
...
@@ -3,6 +3,16 @@
*/
import
{
createTimeRange
}
from
'video.js'
;
let
Playlist
=
{
/**
* The number of segments that are unsafe to start playback at in
* a live stream. Changing this value can cause playback stalls.
* See HTTP Live Streaming, "Playing the Media Playlist File"
* https://tools.ietf.org/html/draft-pantos-http-live-streaming-18#section-6.3.3
*/
UNSAFE_LIVE_SEGMENTS
:
3
};
const
backwardDuration
=
function
(
playlist
,
endSequence
)
{
let
result
=
0
;
let
i
=
endSequence
-
playlist
.
mediaSequence
;
...
...
@@ -187,12 +197,12 @@ export const seekable = function(playlist) {
start
=
intervalDuration
(
playlist
,
playlist
.
mediaSequence
);
end
=
intervalDuration
(
playlist
,
playlist
.
mediaSequence
+
Math
.
max
(
0
,
playlist
.
segments
.
length
-
3
));
Math
.
max
(
0
,
playlist
.
segments
.
length
-
Playlist
.
UNSAFE_LIVE_SEGMENTS
));
return
createTimeRange
(
start
,
end
);
};
Playlist
.
duration
=
duration
;
Playlist
.
seekable
=
seekable
;
// exports
export
default
{
duration
,
seekable
};
export
default
Playlist
;
...
...
src/videojs-contrib-hls.js
View file @
53322e5
...
...
@@ -197,6 +197,88 @@ const keyFailed = function(key) {
return
key
.
retries
&&
key
.
retries
>=
2
;
};
/**
* Returns the CSS value for the specified property on an element
* using `getComputedStyle`. Firefox has a long-standing issue where
* getComputedStyle() may return null when running in an iframe with
* `display: none`.
* @see https://bugzilla.mozilla.org/show_bug.cgi?id=548397
*/
const
safeGetComputedStyle
=
function
(
el
,
property
)
{
let
result
;
if
(
!
el
)
{
return
''
;
}
result
=
getComputedStyle
(
el
);
if
(
!
result
)
{
return
''
;
}
return
result
[
property
];
};
/**
* Updates segment with information about its end-point in time and, optionally,
* the segment duration if we have enough information to determine a segment duration
* accurately.
* @param playlist {object} a media playlist object
* @param segmentIndex {number} the index of segment we last appended
* @param segmentEnd {number} the known of the segment referenced by segmentIndex
*/
const
updateSegmentMetadata
=
function
(
playlist
,
segmentIndex
,
segmentEnd
)
{
if
(
!
playlist
)
{
return
;
}
let
segment
=
playlist
.
segments
[
segmentIndex
];
let
previousSegment
=
playlist
.
segments
[
segmentIndex
-
1
];
if
(
segmentEnd
&&
segment
)
{
segment
.
end
=
segmentEnd
;
// fix up segment durations based on segment end data
if
(
!
previousSegment
)
{
// first segment is always has a start time of 0 making its duration
// equal to the segment end
segment
.
duration
=
segment
.
end
;
}
else
if
(
previousSegment
.
end
)
{
segment
.
duration
=
segment
.
end
-
previousSegment
.
end
;
}
}
};
/**
* Determines if we should call endOfStream on the media source based on the state
* of the buffer or if appened segment was the final segment in the playlist.
* @param playlist {object} a media playlist object
* @param mediaSource {object} the MediaSource object
* @param segmentIndex {number} the index of segment we last appended
* @param currentBuffered {object} the buffered region that currentTime resides in
* @return {boolean} whether the calling function should call endOfStream on the MediaSource
*/
const
detectEndOfStream
=
function
(
playlist
,
mediaSource
,
segmentIndex
,
currentBuffered
)
{
if
(
!
playlist
)
{
return
false
;
}
let
segments
=
playlist
.
segments
;
// determine a few boolean values to help make the branch below easier
// to read
let
appendedLastSegment
=
(
segmentIndex
===
segments
.
length
-
1
);
let
bufferedToEnd
=
(
currentBuffered
.
length
&&
segments
[
segments
.
length
-
1
].
end
<=
currentBuffered
.
end
(
0
));
// if we've buffered to the end of the video, we need to call endOfStream
// so that MediaSources can trigger the `ended` event when it runs out of
// buffered data instead of waiting for me
return
playlist
.
endList
&&
mediaSource
.
readyState
===
'open'
&&
(
appendedLastSegment
||
bufferedToEnd
);
};
const
parseCodecs
=
function
(
codecs
)
{
let
result
=
{
codecCount
:
0
,
...
...
@@ -592,10 +674,15 @@ export default class HlsHandler extends Component {
duration
()
{
let
playlists
=
this
.
playlists
;
if
(
playlists
)
{
return
Hls
.
Playlist
.
duration
(
playlists
.
media
());
if
(
!
playlists
)
{
return
0
;
}
if
(
this
.
mediaSource
)
{
return
this
.
mediaSource
.
duration
;
}
return
0
;
return
Hls
.
Playlist
.
duration
(
playlists
.
media
());
}
seekable
()
{
...
...
@@ -635,6 +722,7 @@ export default class HlsHandler extends Component {
updateDuration
(
playlist
)
{
let
oldDuration
=
this
.
mediaSource
.
duration
;
let
newDuration
=
Hls
.
Playlist
.
duration
(
playlist
);
let
buffered
=
this
.
tech_
.
buffered
();
let
setDuration
=
()
=>
{
this
.
mediaSource
.
duration
=
newDuration
;
this
.
tech_
.
trigger
(
'durationchange'
);
...
...
@@ -642,6 +730,10 @@ export default class HlsHandler extends Component {
this
.
mediaSource
.
removeEventListener
(
'sourceopen'
,
setDuration
);
};
if
(
buffered
.
length
>
0
)
{
newDuration
=
Math
.
max
(
newDuration
,
buffered
.
end
(
buffered
.
length
-
1
));
}
// if the duration has changed, invalidate the cached value
if
(
oldDuration
!==
newDuration
)
{
// update the duration
...
...
@@ -767,8 +859,8 @@ export default class HlsHandler extends Component {
// (this could be the lowest bitrate rendition as we go through all of them above)
variant
=
null
;
width
=
parseInt
(
getComputedStyle
(
this
.
tech_
.
el
()).
width
,
10
);
height
=
parseInt
(
getComputedStyle
(
this
.
tech_
.
el
()).
height
,
10
);
width
=
parseInt
(
safeGetComputedStyle
(
this
.
tech_
.
el
(),
'width'
)
,
10
);
height
=
parseInt
(
safeGetComputedStyle
(
this
.
tech_
.
el
(),
'height'
)
,
10
);
// iterate through the bandwidth-filtered playlists and find
// best rendition by player dimension
...
...
@@ -1094,6 +1186,7 @@ export default class HlsHandler extends Component {
let
segment
=
segmentInfo
.
playlist
.
segments
[
segmentInfo
.
mediaIndex
];
let
removeToTime
=
0
;
let
seekable
=
this
.
seekable
();
let
currentTime
=
this
.
tech_
.
currentTime
();
// Chrome has a hard limit of 150mb of
// buffer and a very conservative "garbage collector"
...
...
@@ -1103,10 +1196,10 @@ export default class HlsHandler extends Component {
if
(
this
.
sourceBuffer
&&
!
this
.
sourceBuffer
.
updating
)
{
// If we have a seekable range use that as the limit for what can be removed safely
// otherwise remove anything older than 1 minute before the current play head
if
(
seekable
.
length
&&
seekable
.
start
(
0
)
>
0
)
{
if
(
seekable
.
length
&&
seekable
.
start
(
0
)
>
0
&&
seekable
.
start
(
0
)
<
currentTime
)
{
removeToTime
=
seekable
.
start
(
0
);
}
else
{
removeToTime
=
this
.
tech_
.
currentTime
()
-
60
;
removeToTime
=
currentTime
-
60
;
}
if
(
removeToTime
>
0
)
{
...
...
@@ -1260,37 +1353,43 @@ export default class HlsHandler extends Component {
updateEndHandler_
()
{
let
segmentInfo
=
this
.
pendingSegment_
;
let
segment
;
let
segments
;
let
playlist
;
let
currentMediaIndex
;
let
currentBuffered
;
let
seekable
;
let
timelineUpdate
;
this
.
pendingSegment_
=
null
;
let
isEndOfStream
;
// stop here if the update errored or was aborted
if
(
!
segmentInfo
)
{
this
.
pendingSegment_
=
null
;
return
;
}
// In Firefox, the updateend event is triggered for both removing from the buffer and
// adding to the buffer. To prevent this code from executing on removals, we wait for
// segmentInfo to have a filled in buffered value before we continue processing.
if
(
!
segmentInfo
.
buffered
)
{
return
;
}
playlist
=
this
.
playlists
.
media
();
segments
=
playlist
.
segments
;
this
.
pendingSegment_
=
null
;
playlist
=
segmentInfo
.
playlist
;
currentMediaIndex
=
segmentInfo
.
mediaIndex
+
(
segmentInfo
.
mediaSequence
-
playlist
.
mediaSequence
);
currentBuffered
=
this
.
findBufferedRange_
();
isEndOfStream
=
detectEndOfStream
(
playlist
,
this
.
mediaSource
,
currentMediaIndex
,
currentBuffered
);
// if we switched renditions don't try to add segment timeline
// information to the playlist
if
(
segmentInfo
.
playlist
.
uri
!==
this
.
playlists
.
media
().
uri
)
{
if
(
isEndOfStream
)
{
return
this
.
mediaSource
.
endOfStream
();
}
return
this
.
fillBuffer
();
}
// annotate the segment with any start and end time information
// added by the media processing
segment
=
playlist
.
segments
[
currentMediaIndex
];
// when seeking to the beginning of the seekable range, it's
// possible that imprecise timing information may cause the seek to
// end up earlier than the start of the range
...
...
@@ -1313,17 +1412,13 @@ export default class HlsHandler extends Component {
timelineUpdate
=
Hls
.
findSoleUncommonTimeRangesEnd_
(
segmentInfo
.
buffered
,
this
.
tech_
.
buffered
());
if
(
timelineUpdate
&&
segment
)
{
segment
.
end
=
timelineUpdate
;
}
// Update segment meta-data (duration and end-point) based on timeline
updateSegmentMetadata
(
playlist
,
currentMediaIndex
,
timelineUpdate
);
// if we've buffered to the end of the video, let the MediaSource know
if
(
this
.
playlists
.
media
().
endList
&&
currentBuffered
.
length
&&
segments
[
segments
.
length
-
1
].
end
<=
currentBuffered
.
end
(
0
)
&&
this
.
mediaSource
.
readyState
===
'open'
)
{
this
.
mediaSource
.
endOfStream
();
return
;
// If we decide to signal the end of stream, then we can return instead
// of trying to fetch more segments
if
(
isEndOfStream
)
{
return
this
.
mediaSource
.
endOfStream
();
}
if
(
timelineUpdate
!==
null
||
...
...
test/videojs-contrib-hls.test.js
View file @
53322e5
...
...
@@ -436,7 +436,7 @@ QUnit.test('duration is set when the source opens after the playlist is loaded',
'set the duration'
);
});
QUnit
.
test
(
'calls `remove`
on sourceBuffer to
when loading a live segment'
,
function
()
{
QUnit
.
test
(
'calls `remove`
based on seekable
when loading a live segment'
,
function
()
{
let
removes
=
[];
let
seekable
=
videojs
.
createTimeRanges
([[
60
,
120
]]);
...
...
@@ -487,7 +487,59 @@ QUnit.test('calls `remove` on sourceBuffer to when loading a live segment', func
'remove called with the right range'
);
});
QUnit
.
test
(
'calls `remove` on sourceBuffer to when loading a vod segment'
,
function
()
{
QUnit
.
test
(
'calls `remove` based on currentTime when loading a live segment '
+
'if seekable start is after currentTime'
,
function
()
{
let
removes
=
[];
let
seekable
=
videojs
.
createTimeRanges
([[
0
,
80
]]);
this
.
player
.
src
({
src
:
'liveStart30sBefore.m3u8'
,
type
:
'application/vnd.apple.mpegurl'
});
openMediaSource
(
this
.
player
,
this
.
clock
);
this
.
player
.
tech_
.
hls
.
seekable
=
function
()
{
return
seekable
;
};
openMediaSource
(
this
.
player
,
this
.
clock
);
this
.
player
.
tech_
.
hls
.
mediaSource
.
addSourceBuffer
=
function
()
{
return
new
(
videojs
.
extend
(
videojs
.
EventTarget
,
{
constructor
()
{},
abort
()
{},
buffered
:
videojs
.
createTimeRange
(),
appendBuffer
()
{},
remove
(
start
,
end
)
{
removes
.
push
([
start
,
end
]);
}
}))();
};
this
.
player
.
tech_
.
hls
.
bandwidth
=
20
e10
;
this
.
player
.
tech_
.
triggerReady
();
standardXHRResponse
(
this
.
requests
[
0
]);
this
.
player
.
tech_
.
hls
.
playlists
.
trigger
(
'loadedmetadata'
);
this
.
player
.
tech_
.
trigger
(
'canplay'
);
this
.
player
.
tech_
.
paused
=
function
()
{
return
false
;
};
this
.
player
.
tech_
.
readyState
=
function
()
{
return
1
;
};
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
]]);
standardXHRResponse
(
this
.
requests
[
1
]);
QUnit
.
strictEqual
(
this
.
requests
[
0
].
url
,
'liveStart30sBefore.m3u8'
,
'master playlist requested'
);
QUnit
.
equal
(
removes
.
length
,
1
,
'remove called'
);
QUnit
.
deepEqual
(
removes
[
0
],
[
0
,
80
-
60
],
'remove called with the right range'
);
});
QUnit
.
test
(
'calls `remove` based on currentTime when loading a vod segment'
,
function
()
{
let
removes
=
[];
this
.
player
.
src
({
...
...
@@ -2268,6 +2320,81 @@ QUnit.test('tracks segment end times as they are buffered', function() {
QUnit
.
equal
(
this
.
player
.
tech_
.
hls
.
mediaSource
.
duration
,
10
+
9.5
,
'updated duration'
);
});
QUnit
.
test
(
'updates first segment duration as it is buffered'
,
function
()
{
let
bufferEnd
=
0
;
this
.
player
.
src
({
src
:
'media.m3u8'
,
type
:
'application/x-mpegURL'
});
openMediaSource
(
this
.
player
,
this
.
clock
);
// as new segments are downloaded, the buffer end is updated
this
.
player
.
tech_
.
buffered
=
function
()
{
return
videojs
.
createTimeRange
(
0
,
bufferEnd
);
};
this
.
requests
.
shift
().
respond
(
200
,
null
,
'#EXTM3U\n'
+
'#EXTINF:10,\n'
+
'0.ts\n'
+
'#EXTINF:10,\n'
+
'1.ts\n'
+
'#EXT-X-ENDLIST\n'
);
// 0.ts is shorter than advertised
standardXHRResponse
(
this
.
requests
.
shift
());
QUnit
.
equal
(
this
.
player
.
tech_
.
hls
.
mediaSource
.
duration
,
20
,
'original duration is from the m3u8'
);
QUnit
.
equal
(
this
.
player
.
tech_
.
hls
.
playlists
.
media
().
segments
[
0
].
duration
,
10
,
'segment duration initially based on playlist'
);
bufferEnd
=
9.5
;
this
.
player
.
tech_
.
hls
.
sourceBuffer
.
trigger
(
'update'
);
this
.
player
.
tech_
.
hls
.
sourceBuffer
.
trigger
(
'updateend'
);
QUnit
.
equal
(
this
.
player
.
tech_
.
hls
.
playlists
.
media
().
segments
[
0
].
duration
,
9.5
,
'updated segment duration'
);
});
QUnit
.
test
(
'updates segment durations as they are buffered'
,
function
()
{
let
bufferEnd
=
0
;
this
.
player
.
src
({
src
:
'media.m3u8'
,
type
:
'application/x-mpegURL'
});
openMediaSource
(
this
.
player
,
this
.
clock
);
// as new segments are downloaded, the buffer end is updated
this
.
player
.
tech_
.
buffered
=
function
()
{
return
videojs
.
createTimeRange
(
0
,
bufferEnd
);
};
this
.
requests
.
shift
().
respond
(
200
,
null
,
'#EXTM3U\n'
+
'#EXTINF:10,\n'
+
'0.ts\n'
+
'#EXTINF:10,\n'
+
'1.ts\n'
+
'#EXT-X-ENDLIST\n'
);
// 0.ts is shorter than advertised
standardXHRResponse
(
this
.
requests
.
shift
());
QUnit
.
equal
(
this
.
player
.
tech_
.
hls
.
mediaSource
.
duration
,
20
,
'original duration is from the m3u8'
);
QUnit
.
equal
(
this
.
player
.
tech_
.
hls
.
playlists
.
media
().
segments
[
1
].
duration
,
10
,
'segment duration initially based on playlist'
);
bufferEnd
=
9.5
;
this
.
player
.
tech_
.
hls
.
sourceBuffer
.
trigger
(
'update'
);
this
.
player
.
tech_
.
hls
.
sourceBuffer
.
trigger
(
'updateend'
);
this
.
clock
.
tick
(
1
);
standardXHRResponse
(
this
.
requests
.
shift
());
bufferEnd
=
19
;
this
.
player
.
tech_
.
hls
.
sourceBuffer
.
trigger
(
'update'
);
this
.
player
.
tech_
.
hls
.
sourceBuffer
.
trigger
(
'updateend'
);
QUnit
.
equal
(
this
.
player
.
tech_
.
hls
.
playlists
.
media
().
segments
[
1
].
duration
,
9.5
,
'updated segment duration'
);
});
QUnit
.
skip
(
'seeking does not fail when targeted between segments'
,
function
()
{
let
currentTime
;
let
segmentUrl
;
...
...
@@ -2576,7 +2703,7 @@ QUnit.test('can be disposed before finishing initialization', function() {
}
});
QUnit
.
test
(
'calls end
ed() on the media source at the end of a playlis
t'
,
function
()
{
QUnit
.
test
(
'calls end
OfStream on the media source after appending the last segmen
t'
,
function
()
{
let
endOfStreams
=
0
;
let
buffered
=
[[]];
...
...
@@ -2591,11 +2718,15 @@ QUnit.test('calls ended() on the media source at the end of a playlist', functio
this
.
player
.
tech_
.
hls
.
mediaSource
.
endOfStream
=
function
()
{
endOfStreams
++
;
};
this
.
player
.
currentTime
(
20
);
this
.
clock
.
tick
(
1
);
// playlist response
this
.
requests
.
shift
().
respond
(
200
,
null
,
'#EXTM3U\n'
+
'#EXTINF:10,\n'
+
'0.ts\n'
+
'#EXTINF:10,\n'
+
'1.ts\n'
+
'#EXT-X-ENDLIST\n'
);
// segment response
this
.
requests
[
0
].
response
=
new
ArrayBuffer
(
17
);
...
...
@@ -2604,7 +2735,52 @@ QUnit.test('calls ended() on the media source at the end of a playlist', functio
buffered
=
[[
0
,
10
]];
this
.
player
.
tech_
.
hls
.
sourceBuffer
.
trigger
(
'updateend'
);
QUnit
.
strictEqual
(
endOfStreams
,
1
,
'ended media source'
);
QUnit
.
strictEqual
(
endOfStreams
,
1
,
'called endOfStream on the media source'
);
});
QUnit
.
test
(
'calls endOfStream on the media source when the current buffer ends at duration'
,
function
()
{
let
endOfStreams
=
0
;
let
buffered
=
[[]];
this
.
player
.
src
({
src
:
'http://example.com/media.m3u8'
,
type
:
'application/vnd.apple.mpegurl'
});
openMediaSource
(
this
.
player
,
this
.
clock
);
this
.
player
.
tech_
.
buffered
=
function
()
{
return
videojs
.
createTimeRanges
(
buffered
);
};
this
.
player
.
tech_
.
hls
.
mediaSource
.
endOfStream
=
function
()
{
endOfStreams
++
;
};
this
.
player
.
currentTime
(
19
);
this
.
clock
.
tick
(
1
);
// playlist response
this
.
requests
.
shift
().
respond
(
200
,
null
,
'#EXTM3U\n'
+
'#EXTINF:10,\n'
+
'0.ts\n'
+
'#EXTINF:10,\n'
+
'1.ts\n'
+
'#EXT-X-ENDLIST\n'
);
// segment response
this
.
requests
[
0
].
response
=
new
ArrayBuffer
(
17
);
this
.
requests
.
shift
().
respond
(
200
,
null
,
''
);
QUnit
.
strictEqual
(
endOfStreams
,
0
,
'waits for the buffer update to finish'
);
buffered
=
[[
10
,
20
]];
this
.
player
.
tech_
.
hls
.
sourceBuffer
.
trigger
(
'updateend'
);
this
.
player
.
currentTime
(
5
);
this
.
clock
.
tick
(
1
);
// segment response
this
.
requests
[
0
].
response
=
new
ArrayBuffer
(
17
);
this
.
requests
.
shift
().
respond
(
200
,
null
,
''
);
buffered
=
[[
0
,
20
]];
this
.
player
.
tech_
.
hls
.
sourceBuffer
.
trigger
(
'updateend'
);
QUnit
.
strictEqual
(
endOfStreams
,
2
,
'called endOfStream on the media source twice'
);
});
QUnit
.
test
(
'calling play() at the end of a video replays'
,
function
()
{
...
...
@@ -3086,6 +3262,68 @@ QUnit.test('does not download segments if preload option set to none', function(
QUnit
.
equal
(
this
.
requests
.
length
,
0
,
'did not download any segments'
);
});
QUnit
.
test
(
'does not process update end until buffered value has been set'
,
function
()
{
let
drainBufferCallCount
=
0
;
let
origDrainBuffer
;
this
.
player
.
src
({
src
:
'master.m3u8'
,
type
:
'application/vnd.apple.mpegurl'
});
openMediaSource
(
this
.
player
,
this
.
clock
);
origDrainBuffer
=
this
.
player
.
tech_
.
hls
.
drainBuffer
;
this
.
player
.
tech_
.
hls
.
drainBuffer
=
function
()
{
drainBufferCallCount
++
;
};
// master
standardXHRResponse
(
this
.
requests
.
shift
());
// media
standardXHRResponse
(
this
.
requests
.
shift
());
QUnit
.
equal
(
drainBufferCallCount
,
0
,
'drainBuffer not called yet'
);
// segment
standardXHRResponse
(
this
.
requests
.
shift
());
QUnit
.
ok
(
this
.
player
.
tech_
.
hls
.
pendingSegment_
,
'pending segment exists'
);
QUnit
.
equal
(
drainBufferCallCount
,
1
,
'drainBuffer called'
);
this
.
player
.
tech_
.
hls
.
sourceBuffer
.
trigger
(
'updateend'
);
QUnit
.
ok
(
this
.
player
.
tech_
.
hls
.
pendingSegment_
,
'pending segment exists'
);
this
.
player
.
tech_
.
hls
.
drainBuffer
=
origDrainBuffer
;
this
.
player
.
tech_
.
hls
.
drainBuffer
();
QUnit
.
ok
(
this
.
player
.
tech_
.
hls
.
pendingSegment_
,
'pending segment exists'
);
this
.
player
.
tech_
.
hls
.
sourceBuffer
.
trigger
(
'updateend'
);
QUnit
.
ok
(
!
this
.
player
.
tech_
.
hls
.
pendingSegment_
,
'pending segment cleared out'
);
});
// workaround https://bugzilla.mozilla.org/show_bug.cgi?id=548397
QUnit
.
test
(
'selectPlaylist does not fail if getComputedStyle returns null'
,
function
()
{
let
oldGetComputedStyle
=
window
.
getComputedStyle
;
window
.
getComputedStyle
=
function
()
{
return
null
;
};
this
.
player
.
src
({
src
:
'master.m3u8'
,
type
:
'application/vnd.apple.mpegurl'
});
openMediaSource
(
this
.
player
,
this
.
clock
);
// master
standardXHRResponse
(
this
.
requests
.
shift
());
// media
standardXHRResponse
(
this
.
requests
.
shift
());
this
.
player
.
tech_
.
hls
.
selectPlaylist
();
QUnit
.
ok
(
true
,
'should not throw'
);
window
.
getComputedStyle
=
oldGetComputedStyle
;
});
QUnit
.
module
(
'Buffer Inspection'
);
QUnit
.
test
(
'detects time range end-point changed by updates'
,
function
()
{
let
edge
;
...
...
Please
register
or
sign in
to post a comment