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
958f2b8c
authored
2015-11-17 18:29:28 -0500
by
David LaPalomento
Browse Files
Options
Browse Files
Tag
Download
Plain Diff
Merge pull request #438 from videojs/blacklist-incompatible-codecs
Blacklist incompatible codecs
2 parents
6dcb32ce
6c574219
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
291 additions
and
2 deletions
example.html
src/videojs-hls.js
test/videojs-hls_test.js
example.html
View file @
958f2b8
...
...
@@ -39,6 +39,11 @@
padding
:
0
5px
;
margin
:
20px
0
;
}
input
{
margin-top
:
15px
;
min-width
:
450px
;
padding
:
5px
;
}
</style>
</head>
...
...
@@ -56,10 +61,31 @@
src=
"http://solutions.brightcove.com/jwhisenant/hls/apple/bipbop/bipbopall.m3u8"
type=
"application/x-mpegURL"
>
</video>
<form
id=
load-url
>
<label>
Video URL:
<input
id=
url
type=
url
value=
"http://solutions.brightcove.com/jwhisenant/hls/apple/bipbop/bipbopall.m3u8"
>
</label>
<button
type=
submit
>
Load
</button>
</form>
<script>
videojs
.
options
.
flash
.
swf
=
'node_modules/videojs-swf/dist/video-js.swf'
;
// initialize the player
var
player
=
videojs
(
'video'
);
// hook up the video switcher
var
loadUrl
=
document
.
getElementById
(
'load-url'
);
var
url
=
document
.
getElementById
(
'url'
);
loadUrl
.
addEventListener
(
'submit'
,
function
(
event
)
{
event
.
preventDefault
();
player
.
src
({
src
:
url
.
value
,
type
:
'application/x-mpegURL'
});
return
false
;
});
</script>
</body>
</html>
...
...
src/videojs-hls.js
View file @
958f2b8
...
...
@@ -294,6 +294,85 @@ videojs.Hls.bufferedAdditions_ = function(original, update) {
return
result
;
};
var
parseCodecs
=
function
(
codecs
)
{
var
result
=
{
codecCount
:
0
,
videoCodec
:
null
,
audioProfile
:
null
};
result
.
codecCount
=
codecs
.
split
(
','
).
length
;
result
.
codecCount
=
result
.
codecCount
||
2
;
// parse the video codec but ignore the version
result
.
videoCodec
=
/
(
^|
\s
|,
)
+
(
avc1
)[^
,
]
*/i
.
exec
(
codecs
);
result
.
videoCodec
=
result
.
videoCodec
&&
result
.
videoCodec
[
2
];
// parse the last field of the audio codec
result
.
audioProfile
=
/
(
^|
\s
|,
)
+mp4a.
\d
+
\.(\d
+
)
/i
.
exec
(
codecs
);
result
.
audioProfile
=
result
.
audioProfile
&&
result
.
audioProfile
[
2
];
return
result
;
};
/**
* Blacklist playlists that are known to be codec or
* stream-incompatible with the SourceBuffer configuration. For
* instance, Media Source Extensions would cause the video element to
* stall waiting for video data if you switched from a variant with
* video and audio to an audio-only one.
*
* @param media {object} a media playlist compatible with the current
* set of SourceBuffers. Variants in the current master playlist that
* do not appear to have compatible codec or stream configurations
* will be excluded from the default playlist selection algorithm
* indefinitely.
*/
videojs
.
HlsHandler
.
prototype
.
excludeIncompatibleVariants_
=
function
(
media
)
{
var
master
=
this
.
playlists
.
master
,
codecCount
=
2
,
videoCodec
=
null
,
audioProfile
=
null
,
codecs
;
if
(
media
.
attributes
&&
media
.
attributes
.
CODECS
)
{
codecs
=
parseCodecs
(
media
.
attributes
.
CODECS
);
videoCodec
=
codecs
.
videoCodec
;
audioProfile
=
codecs
.
audioProfile
;
codecCount
=
codecs
.
codecCount
;
}
master
.
playlists
.
forEach
(
function
(
variant
)
{
var
variantCodecs
=
{
codecCount
:
2
,
videoCodec
:
null
,
audioProfile
:
null
};
if
(
variant
.
attributes
&&
variant
.
attributes
.
CODECS
)
{
variantCodecs
=
parseCodecs
(
variant
.
attributes
.
CODECS
);
}
// if the streams differ in the presence or absence of audio or
// video, they are incompatible
if
(
variantCodecs
.
codecCount
!==
codecCount
)
{
variant
.
excludeUntil
=
Infinity
;
}
// if h.264 is specified on the current playlist, some flavor of
// it must be specified on all compatible variants
if
(
variantCodecs
.
videoCodec
!==
videoCodec
)
{
variant
.
excludeUntil
=
Infinity
;
}
// HE-AAC ("mp4a.40.5") is incompatible with all other versions of
// AAC audio in Chrome 46. Don't mix the two.
if
((
variantCodecs
.
audioProfile
===
'5'
&&
audioProfile
!==
'5'
)
||
(
audioProfile
===
'5'
&&
variantCodecs
.
audioProfile
!==
'5'
))
{
variant
.
excludeUntil
=
Infinity
;
}
});
};
videojs
.
HlsHandler
.
prototype
.
setupSourceBuffer_
=
function
()
{
var
media
=
this
.
playlists
.
media
(),
mimeType
;
...
...
@@ -311,6 +390,10 @@ videojs.HlsHandler.prototype.setupSourceBuffer_ = function() {
}
this
.
sourceBuffer
=
this
.
mediaSource
.
addSourceBuffer
(
mimeType
);
// exclude any incompatible variant streams from future playlist
// selection
this
.
excludeIncompatibleVariants_
(
media
);
// transition the sourcebuffer to the ended state if we've hit the end of
// the playlist
this
.
sourceBuffer
.
addEventListener
(
'updateend'
,
function
()
{
...
...
@@ -389,6 +472,7 @@ videojs.HlsHandler.prototype.setupFirstPlay = function() {
var
seekable
,
media
;
media
=
this
.
playlists
.
media
();
// check that everything is ready to begin buffering
// 1) the video is a live stream of unknown duration
...
...
@@ -585,7 +669,8 @@ videojs.HlsHandler.prototype.selectPlaylist = function () {
effectiveBitrate
,
sortedPlaylists
=
this
.
playlists
.
master
.
playlists
.
slice
(),
bandwidthPlaylists
=
[],
i
=
sortedPlaylists
.
length
,
now
=
+
new
Date
(),
i
,
variant
,
oldvariant
,
bandwidthBestVariant
,
...
...
@@ -596,8 +681,18 @@ videojs.HlsHandler.prototype.selectPlaylist = function () {
sortedPlaylists
.
sort
(
videojs
.
Hls
.
comparePlaylistBandwidth
);
// filter out any playlists that have been excluded due to
// incompatible configurations or playback errors
sortedPlaylists
=
sortedPlaylists
.
filter
(
function
(
variant
)
{
if
(
variant
.
excludeUntil
!==
undefined
)
{
return
now
>=
variant
.
excludeUntil
;
}
return
true
;
});
// filter out any variant that has greater effective bitrate
// than the current estimated bandwidth
i
=
sortedPlaylists
.
length
;
while
(
i
--
)
{
variant
=
sortedPlaylists
[
i
];
...
...
test/videojs-hls_test.js
View file @
958f2b8
...
...
@@ -999,7 +999,7 @@ test('uses the lowest bitrate if no other is suitable', function() {
'the lowest bitrate stream is selected'
);
});
test
(
'selects the correct rendition by player dimensions'
,
function
()
{
test
(
'selects the correct rendition by player dimensions'
,
function
()
{
var
playlist
;
player
.
src
({
...
...
@@ -1071,6 +1071,174 @@ test('selects the highest bitrate playlist when the player dimensions are ' +
'selected the highest bandwidth variant'
);
});
test
(
'filters playlists that are currently excluded'
,
function
()
{
var
playlist
;
player
.
src
({
src
:
'manifest/master.m3u8'
,
type
:
'application/vnd.apple.mpegurl'
});
openMediaSource
(
player
);
player
.
tech_
.
hls
.
bandwidth
=
1
e10
;
requests
.
shift
().
respond
(
200
,
null
,
'#EXTM3U\n'
+
'#EXT-X-STREAM-INF:BANDWIDTH=1000\n'
+
'media.m3u8\n'
+
'#EXT-X-STREAM-INF:BANDWIDTH=1\n'
+
'media1.m3u8\n'
);
// master
standardXHRResponse
(
requests
.
shift
());
// media
// exclude the current playlist
player
.
tech_
.
hls
.
playlists
.
master
.
playlists
[
0
].
excludeUntil
=
+
new
Date
()
+
1000
;
playlist
=
player
.
tech_
.
hls
.
selectPlaylist
();
equal
(
playlist
,
player
.
tech_
.
hls
.
playlists
.
master
.
playlists
[
1
],
'respected exclusions'
);
// timeout the exclusion
clock
.
tick
(
1000
);
playlist
=
player
.
tech_
.
hls
.
selectPlaylist
();
equal
(
playlist
,
player
.
tech_
.
hls
.
playlists
.
master
.
playlists
[
0
],
'expired the exclusion'
);
});
test
(
'blacklists switching from video+audio playlists to audio only'
,
function
()
{
var
audioPlaylist
;
player
.
src
({
src
:
'manifest/master.m3u8'
,
type
:
'application/vnd.apple.mpegurl'
});
openMediaSource
(
player
);
player
.
tech_
.
hls
.
bandwidth
=
1
e10
;
requests
.
shift
().
respond
(
200
,
null
,
'#EXTM3U\n'
+
'#EXT-X-STREAM-INF:BANDWIDTH=1,CODECS="mp4a.40.2"\n'
+
'media.m3u8\n'
+
'#EXT-X-STREAM-INF:BANDWIDTH=10,RESOLUTION=1x1\n'
+
'media1.m3u8\n'
);
// master
standardXHRResponse
(
requests
.
shift
());
// media1
equal
(
player
.
tech_
.
hls
.
playlists
.
media
(),
player
.
tech_
.
hls
.
playlists
.
master
.
playlists
[
1
],
'selected video+audio'
);
audioPlaylist
=
player
.
tech_
.
hls
.
playlists
.
master
.
playlists
[
0
];
equal
(
audioPlaylist
.
excludeUntil
,
Infinity
,
'excluded incompatible playlist'
);
});
test
(
'blacklists switching from audio-only playlists to video+audio'
,
function
()
{
var
videoAudioPlaylist
;
player
.
src
({
src
:
'manifest/master.m3u8'
,
type
:
'application/vnd.apple.mpegurl'
});
openMediaSource
(
player
);
player
.
tech_
.
hls
.
bandwidth
=
1
;
requests
.
shift
().
respond
(
200
,
null
,
'#EXTM3U\n'
+
'#EXT-X-STREAM-INF:BANDWIDTH=1,CODECS="mp4a.40.2"\n'
+
'media.m3u8\n'
+
'#EXT-X-STREAM-INF:BANDWIDTH=10,RESOLUTION=1x1\n'
+
'media1.m3u8\n'
);
// master
standardXHRResponse
(
requests
.
shift
());
// media1
equal
(
player
.
tech_
.
hls
.
playlists
.
media
(),
player
.
tech_
.
hls
.
playlists
.
master
.
playlists
[
0
],
'selected audio only'
);
videoAudioPlaylist
=
player
.
tech_
.
hls
.
playlists
.
master
.
playlists
[
1
];
equal
(
videoAudioPlaylist
.
excludeUntil
,
Infinity
,
'excluded incompatible playlist'
);
});
test
(
'blacklists switching from video-only playlists to video+audio'
,
function
()
{
var
videoAudioPlaylist
;
player
.
src
({
src
:
'manifest/master.m3u8'
,
type
:
'application/vnd.apple.mpegurl'
});
openMediaSource
(
player
);
player
.
tech_
.
hls
.
bandwidth
=
1
;
requests
.
shift
().
respond
(
200
,
null
,
'#EXTM3U\n'
+
'#EXT-X-STREAM-INF:BANDWIDTH=1,CODECS="avc1.4d400d"\n'
+
'media.m3u8\n'
+
'#EXT-X-STREAM-INF:BANDWIDTH=10,CODECS="avc1.4d400d,mp4a.40.2"\n'
+
'media1.m3u8\n'
);
// master
standardXHRResponse
(
requests
.
shift
());
// media
equal
(
player
.
tech_
.
hls
.
playlists
.
media
(),
player
.
tech_
.
hls
.
playlists
.
master
.
playlists
[
0
],
'selected video only'
);
videoAudioPlaylist
=
player
.
tech_
.
hls
.
playlists
.
master
.
playlists
[
1
];
equal
(
videoAudioPlaylist
.
excludeUntil
,
Infinity
,
'excluded incompatible playlist'
);
});
test
(
'does not blacklist compatible H.264 codec strings'
,
function
()
{
var
master
;
player
.
src
({
src
:
'manifest/master.m3u8'
,
type
:
'application/vnd.apple.mpegurl'
});
openMediaSource
(
player
);
player
.
tech_
.
hls
.
bandwidth
=
1
;
requests
.
shift
().
respond
(
200
,
null
,
'#EXTM3U\n'
+
'#EXT-X-STREAM-INF:BANDWIDTH=1,CODECS="avc1.4d400d,mp4a.40.5"\n'
+
'media.m3u8\n'
+
'#EXT-X-STREAM-INF:BANDWIDTH=10,CODECS="avc1.4d400f,mp4a.40.5"\n'
+
'media1.m3u8\n'
);
// master
standardXHRResponse
(
requests
.
shift
());
// media
master
=
player
.
tech_
.
hls
.
playlists
.
master
;
strictEqual
(
master
.
playlists
[
0
].
excludeUntil
,
undefined
,
'did not blacklist'
);
strictEqual
(
master
.
playlists
[
1
].
excludeUntil
,
undefined
,
'did not blacklist'
);
});
test
(
'does not blacklist compatible AAC codec strings'
,
function
()
{
var
master
;
player
.
src
({
src
:
'manifest/master.m3u8'
,
type
:
'application/vnd.apple.mpegurl'
});
openMediaSource
(
player
);
player
.
tech_
.
hls
.
bandwidth
=
1
;
requests
.
shift
().
respond
(
200
,
null
,
'#EXTM3U\n'
+
'#EXT-X-STREAM-INF:BANDWIDTH=1,CODECS="avc1.4d400d,mp4a.40.2"\n'
+
'media.m3u8\n'
+
'#EXT-X-STREAM-INF:BANDWIDTH=10,CODECS="avc1.4d400d,mp4a.40.3"\n'
+
'media1.m3u8\n'
);
// master
standardXHRResponse
(
requests
.
shift
());
// media
master
=
player
.
tech_
.
hls
.
playlists
.
master
;
strictEqual
(
master
.
playlists
[
0
].
excludeUntil
,
undefined
,
'did not blacklist'
);
strictEqual
(
master
.
playlists
[
1
].
excludeUntil
,
undefined
,
'did not blacklist'
);
});
test
(
'blacklists switching between playlists with incompatible audio codecs'
,
function
()
{
var
alternatePlaylist
;
player
.
src
({
src
:
'manifest/master.m3u8'
,
type
:
'application/vnd.apple.mpegurl'
});
openMediaSource
(
player
);
player
.
tech_
.
hls
.
bandwidth
=
1
;
requests
.
shift
().
respond
(
200
,
null
,
'#EXTM3U\n'
+
'#EXT-X-STREAM-INF:BANDWIDTH=1,CODECS="avc1.4d400d,mp4a.40.5"\n'
+
'media.m3u8\n'
+
'#EXT-X-STREAM-INF:BANDWIDTH=10,CODECS="avc1.4d400d,mp4a.40.2"\n'
+
'media1.m3u8\n'
);
// master
standardXHRResponse
(
requests
.
shift
());
// media
equal
(
player
.
tech_
.
hls
.
playlists
.
media
(),
player
.
tech_
.
hls
.
playlists
.
master
.
playlists
[
0
],
'selected HE-AAC stream'
);
alternatePlaylist
=
player
.
tech_
.
hls
.
playlists
.
master
.
playlists
[
1
];
equal
(
alternatePlaylist
.
excludeUntil
,
Infinity
,
'excluded incompatible playlist'
);
});
test
(
'does not download the next segment if the buffer is full'
,
function
()
{
var
currentTime
=
15
;
player
.
src
({
...
...
Please
register
or
sign in
to post a comment