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
97988fc1
authored
2015-11-20 13:12:31 -0500
by
David LaPalomento
Browse Files
Options
Browse Files
Tag
Download
Plain Diff
Merge pull request #440 from videojs/blacklist
Blacklist
2 parents
56919c5f
3b1a5651
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
74 additions
and
67 deletions
src/m3u8/m3u8-parser.js
src/videojs-hls.js
test/videojs-hls_test.js
src/m3u8/m3u8-parser.js
View file @
97988fc
...
...
@@ -110,7 +110,7 @@
var
match
,
event
;
//strip whitespace
line
=
line
.
replace
(
/^
\s
+|
\s
+$/g
,
''
);
line
=
line
.
replace
(
/^
[\u
0000
\s]
+|
[\u
0000
\s]
+$/g
,
''
);
if
(
line
.
length
===
0
)
{
// ignore empty lines
return
;
...
...
src/videojs-hls.js
View file @
97988fc
...
...
@@ -7,17 +7,18 @@
'use strict'
;
var
//
a
fudge factor to apply to advertised playlist bitrates to account for
//
A
fudge factor to apply to advertised playlist bitrates to account for
// temporary flucations in client bandwidth
bandwidthVariance
=
1.2
,
blacklistDuration
=
5
*
60
*
1000
,
// 2 minute blacklist
TIME_FUDGE_FACTOR
=
1
/
60
,
// Fudge factor to account for TimeRanges rounding
Component
=
videojs
.
getComponent
(
'Component'
),
//
t
he amount of time to wait between checking the state of the buffer
//
T
he amount of time to wait between checking the state of the buffer
bufferCheckInterval
=
500
,
keyFailed
,
resolveUrl
,
TIME_FUDGE_FACTOR
=
1
/
60
;
resolveUrl
;
// returns true if a key has failed to download within a certain amount of retries
keyFailed
=
function
(
key
)
{
...
...
@@ -179,15 +180,7 @@ videojs.HlsHandler.prototype.src = function(src) {
}.
bind
(
this
));
this
.
playlists
.
on
(
'error'
,
function
()
{
// close the media source with the appropriate error type
if
(
this
.
playlists
.
error
.
code
===
2
)
{
this
.
mediaSource
.
endOfStream
(
'network'
);
}
else
if
(
this
.
playlists
.
error
.
code
===
4
)
{
this
.
mediaSource
.
endOfStream
(
'decode'
);
}
// if this error is unrecognized, pass it along to the tech
this
.
tech_
.
error
(
this
.
playlists
.
error
);
this
.
blacklistCurrentPlaylist_
(
this
.
playlists
.
error
);
}.
bind
(
this
));
this
.
playlists
.
on
(
'loadedplaylist'
,
function
()
{
...
...
@@ -916,6 +909,36 @@ videojs.HlsHandler.prototype.setBandwidth = function(xhr) {
this
.
tech_
.
trigger
(
'bandwidthupdate'
);
};
videojs
.
HlsHandler
.
prototype
.
blacklistCurrentPlaylist_
=
function
(
error
)
{
var
currentPlaylist
,
nextPlaylist
;
currentPlaylist
=
this
.
playlists
.
media
();
// If there is no current playlist, then an error occurred while we were
// trying to load the master OR while we were disposing of the tech
if
(
!
currentPlaylist
)
{
this
.
error
=
error
;
return
this
.
mediaSource
.
endOfStream
(
'network'
);
}
// Select a new playlist
nextPlaylist
=
this
.
selectPlaylist
();
if
(
nextPlaylist
)
{
videojs
.
log
.
warn
(
'Problem encountered with the current HLS playlist. Switching to another playlist.'
);
// Blacklist this playlist
currentPlaylist
.
excludeUntil
=
Date
.
now
()
+
blacklistDuration
;
return
this
.
playlists
.
media
(
nextPlaylist
);
}
else
{
videojs
.
log
.
warn
(
'Problem encountered with the current HLS playlist. No suitable alternatives found.'
);
// We have no more playlists we can select so we must fail
this
.
error
=
error
;
return
this
.
mediaSource
.
endOfStream
(
'network'
);
}
};
videojs
.
HlsHandler
.
prototype
.
loadSegment
=
function
(
segmentInfo
)
{
var
self
=
this
,
...
...
@@ -930,7 +953,11 @@ videojs.HlsHandler.prototype.loadSegment = function(segmentInfo) {
this
.
segmentXhr_
=
videojs
.
Hls
.
xhr
({
uri
:
segmentInfo
.
uri
,
responseType
:
'arraybuffer'
,
withCredentials
:
this
.
source_
.
withCredentials
withCredentials
:
this
.
source_
.
withCredentials
,
// Set xhr timeout to 150% of the segment duration to allow us
// some time to switch renditions in the event of a catastrophic
// decrease in network performance or a server issue.
timeout
:
(
segment
.
duration
*
1.5
)
*
1000
},
function
(
error
,
request
)
{
// the segment request is no longer outstanding
self
.
segmentXhr_
=
null
;
...
...
@@ -943,13 +970,12 @@ videojs.HlsHandler.prototype.loadSegment = function(segmentInfo) {
// otherwise, trigger a network error
if
(
!
request
.
aborted
&&
error
)
{
self
.
error
=
{
self
.
pendingSegment_
=
null
;
return
self
.
blacklistCurrentPlaylist_
({
status
:
request
.
status
,
message
:
'HLS segment request error at URL: '
+
segmentInfo
.
uri
,
code
:
(
request
.
status
>=
500
)
?
4
:
2
};
return
self
.
mediaSource
.
endOfStream
(
'network'
);
});
}
// stop processing if the request was aborted
...
...
@@ -1021,9 +1047,10 @@ videojs.HlsHandler.prototype.drainBuffer = function(event) {
// if the key download failed, we want to skip this segment
// but if the key hasn't downloaded yet, we want to try again later
if
(
keyFailed
(
segment
.
key
))
{
videojs
.
log
.
warn
(
'Network error retrieving key from "'
+
segment
.
key
.
uri
+
'"'
);
return
this
.
mediaSource
.
endOfStream
(
'network'
);
return
this
.
blacklistCurrentPlaylist_
({
message
:
'HLS segment key request error.'
,
code
:
4
});
}
else
if
(
!
segment
.
key
.
bytes
)
{
// waiting for the key bytes, try again later
...
...
test/videojs-hls_test.js
View file @
97988fc
...
...
@@ -831,21 +831,6 @@ test('selects a playlist after segment downloads', function() {
strictEqual
(
calls
,
3
,
'selects after additional segments'
);
});
test
(
'reports an error if a segment is unreachable'
,
function
()
{
player
.
src
({
src
:
'manifest/master.m3u8'
,
type
:
'application/vnd.apple.mpegurl'
});
openMediaSource
(
player
);
player
.
tech_
.
hls
.
bandwidth
=
20000
;
standardXHRResponse
(
requests
[
0
]);
// master
standardXHRResponse
(
requests
[
1
]);
// media
requests
[
2
].
respond
(
400
);
// segment
strictEqual
(
player
.
tech_
.
hls
.
mediaSource
.
error_
,
'network'
,
'network error is triggered'
);
});
test
(
'updates the duration after switching playlists'
,
function
()
{
var
selectedPlaylist
=
false
;
player
.
src
({
...
...
@@ -1510,32 +1495,23 @@ test('playlist 404 should end stream with a network error', function() {
equal
(
player
.
tech_
.
hls
.
mediaSource
.
error_
,
'network'
,
'set a network error'
);
});
test
(
'segment 404 should trigger MEDIA_ERR_NETWORK'
,
function
()
{
test
(
'segment 404 should trigger blacklisting of media'
,
function
()
{
var
media
;
player
.
src
({
src
:
'manifest/m
edia
.m3u8'
,
src
:
'manifest/m
aster
.m3u8'
,
type
:
'application/vnd.apple.mpegurl'
});
openMediaSource
(
player
);
standardXHRResponse
(
requests
[
0
]);
requests
[
1
].
respond
(
404
);
ok
(
player
.
tech_
.
hls
.
error
.
message
,
'an error message is available'
);
equal
(
2
,
player
.
tech_
.
hls
.
error
.
code
,
'Player error code should be set to MediaError.MEDIA_ERR_NETWORK'
);
});
test
(
'segment 500 should trigger MEDIA_ERR_ABORTED'
,
function
()
{
player
.
src
({
src
:
'manifest/media.m3u8'
,
type
:
'application/vnd.apple.mpegurl'
});
player
.
tech_
.
hls
.
bandwidth
=
20000
;
standardXHRResponse
(
requests
[
0
]);
// master
standardXHRResponse
(
requests
[
1
]);
// media
openMediaSource
(
player
)
;
media
=
player
.
tech_
.
hls
.
playlists
.
media_
;
standardXHRResponse
(
requests
[
0
]);
requests
[
1
].
respond
(
500
);
ok
(
player
.
tech_
.
hls
.
error
.
message
,
'an error message is available'
);
equal
(
4
,
player
.
tech_
.
hls
.
error
.
code
,
'Player error code should be set to MediaError.MEDIA_ERR_ABORTED'
);
requests
[
2
].
respond
(
400
);
// segment
ok
(
media
.
excludeUntil
>
0
,
'original media blacklisted for some time'
);
});
test
(
'seeking in an empty playlist is a non-erroring noop'
,
function
()
{
...
...
@@ -2458,8 +2434,8 @@ test('retries key requests once upon failure', function() {
equal
(
requests
.
length
,
2
,
'gives up after one retry'
);
});
test
(
'
errors
if key requests fail more than once'
,
function
()
{
var
bytes
=
[];
test
(
'
blacklists playlist
if key requests fail more than once'
,
function
()
{
var
bytes
=
[]
,
media
;
player
.
src
({
src
:
'https://example.com/encrypted-media.m3u8'
,
...
...
@@ -2479,14 +2455,16 @@ test('errors if key requests fail more than once', function() {
player
.
tech_
.
hls
.
sourceBuffer
.
appendBuffer
=
function
(
chunk
)
{
bytes
.
push
(
chunk
);
};
media
=
player
.
tech_
.
hls
.
playlists
.
media_
;
standardXHRResponse
(
requests
.
pop
());
// segment 1
requests
.
shift
().
respond
(
404
);
// fail key
requests
.
shift
().
respond
(
404
);
// fail key, again
player
.
tech_
.
hls
.
checkBuffer_
();
equal
(
player
.
tech_
.
hls
.
mediaSource
.
error_
,
'network'
,
'triggered a network error'
);
ok
(
media
.
excludeUntil
>
0
,
'playlist blacklisted'
);
});
test
(
'the key is supplied to the decrypter in the correct format'
,
function
()
{
...
...
@@ -2624,8 +2602,9 @@ test('resolves relative key URLs against the playlist', function() {
equal
(
requests
[
0
].
url
,
'https://example.com/key.php?r=52'
,
'resolves the key URL'
);
});
test
(
'treats invalid keys as a key request failure'
,
function
()
{
var
bytes
=
[];
test
(
'treats invalid keys as a key request failure and blacklists playlist'
,
function
()
{
var
bytes
=
[],
media
;
player
.
src
({
src
:
'https://example.com/media.m3u8'
,
type
:
'application/vnd.apple.mpegurl'
...
...
@@ -2644,6 +2623,8 @@ test('treats invalid keys as a key request failure', function() {
player
.
tech_
.
hls
.
sourceBuffer
.
appendBuffer
=
function
(
chunk
)
{
bytes
.
push
(
chunk
);
};
media
=
player
.
tech_
.
hls
.
playlists
.
media_
;
// segment request
standardXHRResponse
(
requests
.
pop
());
// keys should be 16 bytes long
...
...
@@ -2657,10 +2638,9 @@ test('treats invalid keys as a key request failure', function() {
requests
.
shift
().
respond
(
200
,
null
,
''
);
player
.
tech_
.
hls
.
checkBuffer_
();
// two failed attempts is a network error
equal
(
player
.
tech_
.
hls
.
mediaSource
.
error_
,
'network'
,
'triggered a network error'
);
// two failed attempts is an error - blacklist this playlist
ok
(
media
.
excludeUntil
>
0
,
'blacklisted playlist'
);
});
test
(
'live stream should not call endOfStream'
,
function
(){
...
...
Please
register
or
sign in
to post a comment