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
4b0e7317
authored
2014-10-30 11:55:44 -0400
by
David LaPalomento
Browse Files
Options
Browse Files
Tag
Download
Plain Diff
Merge pull request #178 from videojs/pre-segment-switch
Pre segment switch
2 parents
de317697
014577a9
Show whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
154 additions
and
32 deletions
package.json
src/playlist-loader.js
src/videojs-hls.js
src/xhr.js
test/manifest/media.json
test/manifest/media.m3u8
test/manifest/media1.m3u8
test/manifest/media3.m3u8
test/videojs-hls_test.js
package.json
View file @
4b0e731
...
...
@@ -29,12 +29,13 @@
"karma-firefox-launcher"
:
"~0.1.3"
,
"karma-ie-launcher"
:
"~0.1.1"
,
"karma-opera-launcher"
:
"~0.1.0"
,
"karma-phantomjs-launcher"
:
"
~0.1.1
"
,
"karma-phantomjs-launcher"
:
"
^0.1.4
"
,
"karma-qunit"
:
"~0.1.1"
,
"karma-safari-launcher"
:
"~0.1.1"
,
"karma-sauce-launcher"
:
"~0.1.8"
,
"qunitjs"
:
"^1.15.0"
,
"sinon"
:
"1.10.2"
,
"video.js"
:
"^4.
7.2
"
"video.js"
:
"^4.
9.0
"
},
"dependencies"
:
{
"pkcs7"
:
"^0.2.2"
,
...
...
src/playlist-loader.js
View file @
4b0e731
...
...
@@ -58,6 +58,8 @@
haveMetadata
=
function
(
error
,
xhr
,
url
)
{
var
parser
,
refreshDelay
,
update
;
loader
.
setBandwidth
(
request
||
xhr
);
// any in-flight request is now finished
request
=
null
;
...
...
@@ -200,6 +202,10 @@
});
};
loader
.
setBandwidth
=
function
(
xhr
)
{
loader
.
bandwidth
=
xhr
.
bandwidth
;
};
// live playlist staleness timeout
loader
.
on
(
'mediaupdatetimeout'
,
function
()
{
if
(
loader
.
state
!==
'HAVE_METADATA'
)
{
...
...
src/videojs-hls.js
View file @
4b0e731
...
...
@@ -101,19 +101,58 @@ videojs.Hls.prototype.handleSourceOpen = function() {
sourceBuffer
.
appendBuffer
(
this
.
segmentParser_
.
getFlvHeader
());
this
.
mediaIndex
=
0
;
if
(
this
.
playlists
)
{
this
.
playlists
.
dispose
();
}
this
.
playlists
=
new
videojs
.
Hls
.
PlaylistLoader
(
this
.
src_
,
settings
.
withCredentials
);
this
.
playlists
.
on
(
'loadedmetadata'
,
videojs
.
bind
(
this
,
function
()
{
oldMediaPlaylist
=
this
.
playlists
.
media
();
var
selectedPlaylist
,
loaderHandler
,
newBitrate
,
segmentDuration
,
segmentDlTime
,
setupEvents
,
threshold
;
setupEvents
=
function
()
{
this
.
fillBuffer
();
// periodically check if new data needs to be downloaded or
// buffered data should be appended to the source buffer
this
.
fillBuffer
();
player
.
on
(
'timeupdate'
,
videojs
.
bind
(
this
,
this
.
fillBuffer
));
player
.
on
(
'timeupdate'
,
videojs
.
bind
(
this
,
this
.
drainBuffer
));
player
.
on
(
'waiting'
,
videojs
.
bind
(
this
,
this
.
drainBuffer
));
player
.
trigger
(
'loadedmetadata'
);
};
oldMediaPlaylist
=
this
.
playlists
.
media
();
this
.
bandwidth
=
this
.
playlists
.
bandwidth
;
selectedPlaylist
=
this
.
selectPlaylist
();
newBitrate
=
selectedPlaylist
.
attributes
&&
selectedPlaylist
.
attributes
.
BANDWIDTH
;
segmentDuration
=
oldMediaPlaylist
.
segments
&&
oldMediaPlaylist
.
segments
[
this
.
mediaIndex
].
duration
||
oldMediaPlaylist
.
targetDuration
;
segmentDlTime
=
(
segmentDuration
*
newBitrate
)
/
this
.
bandwidth
;
if
(
!
segmentDlTime
)
{
segmentDlTime
=
Infinity
;
}
// this threshold is to account for having a high latency on the manifest
// request which is a somewhat small file.
threshold
=
10
;
if
(
segmentDlTime
<=
threshold
)
{
this
.
playlists
.
media
(
selectedPlaylist
);
loaderHandler
=
videojs
.
bind
(
this
,
function
()
{
setupEvents
.
call
(
this
);
this
.
playlists
.
off
(
'loadedplaylist'
,
loaderHandler
);
});
this
.
playlists
.
on
(
'loadedplaylist'
,
loaderHandler
);
}
else
{
setupEvents
.
call
(
this
);
}
}));
this
.
playlists
.
on
(
'error'
,
videojs
.
bind
(
this
,
function
()
{
...
...
@@ -409,12 +448,27 @@ videojs.Hls.prototype.fillBuffer = function(offset) {
this
.
loadSegment
(
segmentUri
,
offset
);
};
/*
* Sets `bandwidth`, `segmentXhrTime`, and appends to the `bytesReceived.
* Expects an object with:
* * `roundTripTime` - the round trip time for the request we're setting the time for
* * `bandwidth` - the bandwidth we want to set
* * `bytesReceived` - amount of bytes downloaded
* `bandwidth` is the only required property.
*/
videojs
.
Hls
.
prototype
.
setBandwidth
=
function
(
xhr
)
{
var
tech
=
this
;
// calculate the download bandwidth
tech
.
segmentXhrTime
=
xhr
.
roundTripTime
;
tech
.
bandwidth
=
xhr
.
bandwidth
;
tech
.
bytesReceived
+=
xhr
.
bytesReceived
||
0
;
};
videojs
.
Hls
.
prototype
.
loadSegment
=
function
(
segmentUri
,
offset
)
{
var
tech
=
this
,
player
=
this
.
player
(),
settings
=
player
.
options
().
hls
||
{},
startTime
=
+
new
Date
();
settings
=
player
.
options
().
hls
||
{};
// request the next segment
this
.
segmentXhr_
=
videojs
.
Hls
.
xhr
({
...
...
@@ -448,10 +502,7 @@ videojs.Hls.prototype.loadSegment = function(segmentUri, offset) {
return
;
}
// calculate the download bandwidth
tech
.
segmentXhrTime
=
(
+
new
Date
())
-
startTime
;
tech
.
bandwidth
=
(
this
.
response
.
byteLength
/
tech
.
segmentXhrTime
)
*
8
*
1000
;
tech
.
bytesReceived
+=
this
.
response
.
byteLength
;
tech
.
setBandwidth
(
this
);
// package up all the work to append the segment
// if the segment is the start of a timestamp discontinuity,
...
...
src/xhr.js
View file @
4b0e731
...
...
@@ -34,6 +34,7 @@
request
=
new
window
.
XMLHttpRequest
();
request
.
open
(
options
.
method
,
url
);
request
.
url
=
url
;
request
.
requestTime
=
new
Date
().
getTime
();
if
(
options
.
responseType
)
{
request
.
responseType
=
options
.
responseType
;
...
...
@@ -69,6 +70,13 @@
return
callback
.
call
(
this
,
true
,
url
);
}
if
(
this
.
response
)
{
this
.
responseTime
=
new
Date
().
getTime
();
this
.
roundTripTime
=
this
.
responseTime
-
this
.
requestTime
;
this
.
bytesReceived
=
this
.
response
.
byteLength
||
this
.
response
.
length
;
this
.
bandwidth
=
Math
.
floor
((
this
.
bytesReceived
/
this
.
roundTripTime
)
*
8
*
1000
);
}
return
callback
.
call
(
this
,
false
,
url
);
};
request
.
send
(
null
);
...
...
test/manifest/media.json
View file @
4b0e731
...
...
@@ -5,19 +5,19 @@
"segments"
:
[
{
"duration"
:
10
,
"uri"
:
"00001.ts"
"uri"
:
"
media-
00001.ts"
},
{
"duration"
:
10
,
"uri"
:
"00002.ts"
"uri"
:
"
media-
00002.ts"
},
{
"duration"
:
10
,
"uri"
:
"00003.ts"
"uri"
:
"
media-
00003.ts"
},
{
"duration"
:
10
,
"uri"
:
"00004.ts"
"uri"
:
"
media-
00004.ts"
}
],
"targetDuration"
:
10
,
...
...
test/manifest/media.m3u8
View file @
4b0e731
...
...
@@ -2,12 +2,12 @@
#EXT-X-PLAYLIST-TYPE:VOD
#EXT-X-TARGETDURATION:10
#EXTINF:10,
00001.ts
media-
00001.ts
#EXTINF:10,
00002.ts
media-
00002.ts
#EXTINF:10,
00003.ts
media-
00003.ts
#EXTINF:10,
00004.ts
media-
00004.ts
#ZEN-TOTAL-DURATION:57.9911
#EXT-X-ENDLIST
...
...
test/manifest/media1.m3u8
View file @
4b0e731
...
...
@@ -2,12 +2,12 @@
#EXT-X-PLAYLIST-TYPE:VOD
#EXT-X-TARGETDURATION:10
#EXTINF:10,
00001.ts
media1-
00001.ts
#EXTINF:10,
00002.ts
media1-
00002.ts
#EXTINF:10,
00003.ts
media1-
00003.ts
#EXTINF:10,
00004.ts
media1-
00004.ts
#ZEN-TOTAL-DURATION:57.9911
#EXT-X-ENDLIST
...
...
test/manifest/media3.m3u8
View file @
4b0e731
...
...
@@ -2,12 +2,12 @@
#EXT-X-PLAYLIST-TYPE:VOD
#EXT-X-TARGETDURATION:10
#EXTINF:10,
00001.ts
media3-
00001.ts
#EXTINF:10,
00002.ts
media3-
00002.ts
#EXTINF:10,
00003.ts
media3-
00003.ts
#EXTINF:10,
00004.ts
media3-
00004.ts
#ZEN-TOTAL-DURATION:57.9911
#EXT-X-ENDLIST
...
...
test/videojs-hls_test.js
View file @
4b0e731
...
...
@@ -260,7 +260,7 @@ test('starts downloading a segment on loadedmetadata', function() {
strictEqual
(
requests
[
1
].
url
,
window
.
location
.
origin
+
window
.
location
.
pathname
.
split
(
'/'
).
slice
(
0
,
-
1
).
join
(
'/'
)
+
'/manifest/00001.ts'
,
'/manifest/
media-
00001.ts'
,
'the first segment is requested'
);
});
...
...
@@ -349,8 +349,41 @@ test('downloads media playlists after loading the master', function() {
openMediaSource
(
player
);
standardXHRResponse
(
requests
[
0
]);
// set bandwidth to a high number, so, we don't switch;
player
.
hls
.
bandwidth
=
500000
;
standardXHRResponse
(
requests
[
1
]);
standardXHRResponse
(
requests
[
2
]);
strictEqual
(
requests
[
0
].
url
,
'manifest/master.m3u8'
,
'master playlist requested'
);
strictEqual
(
requests
[
1
].
url
,
window
.
location
.
origin
+
window
.
location
.
pathname
.
split
(
'/'
).
slice
(
0
,
-
1
).
join
(
'/'
)
+
'/manifest/media.m3u8'
,
'media playlist requested'
);
strictEqual
(
requests
[
2
].
url
,
window
.
location
.
origin
+
window
.
location
.
pathname
.
split
(
'/'
).
slice
(
0
,
-
1
).
join
(
'/'
)
+
'/manifest/media-00001.ts'
,
'first segment requested'
);
});
test
(
'downloads a second media playlist before playback, if bandwidth is high'
,
function
()
{
player
.
src
({
src
:
'manifest/master.m3u8'
,
type
:
'application/vnd.apple.mpegurl'
});
openMediaSource
(
player
);
standardXHRResponse
(
requests
[
0
]);
player
.
hls
.
playlists
.
setBandwidth
=
function
()
{
player
.
hls
.
playlists
.
bandwidth
=
100000
;
};
standardXHRResponse
(
requests
[
1
]);
standardXHRResponse
(
requests
[
2
]);
standardXHRResponse
(
requests
[
3
]);
strictEqual
(
requests
[
0
].
url
,
'manifest/master.m3u8'
,
'master playlist requested'
);
strictEqual
(
requests
[
1
].
url
,
...
...
@@ -361,7 +394,12 @@ test('downloads media playlists after loading the master', function() {
strictEqual
(
requests
[
2
].
url
,
window
.
location
.
origin
+
window
.
location
.
pathname
.
split
(
'/'
).
slice
(
0
,
-
1
).
join
(
'/'
)
+
'/manifest/00001.ts'
,
'/manifest/media1.m3u8'
,
'media playlist requested'
);
strictEqual
(
requests
[
3
].
url
,
window
.
location
.
origin
+
window
.
location
.
pathname
.
split
(
'/'
).
slice
(
0
,
-
1
).
join
(
'/'
)
+
'/manifest/media1-00001.ts'
,
'first segment requested'
);
});
...
...
@@ -385,6 +423,10 @@ test('calculates the bandwidth after downloading a segment', function() {
openMediaSource
(
player
);
standardXHRResponse
(
requests
[
0
]);
// set the request time to be a bit earlier so our bandwidth calculations are not NaN
requests
[
1
].
requestTime
=
(
new
Date
())
-
100
;
standardXHRResponse
(
requests
[
1
]);
ok
(
player
.
hls
.
bandwidth
,
'bandwidth is calculated'
);
...
...
@@ -407,10 +449,12 @@ test('selects a playlist after segment downloads', function() {
openMediaSource
(
player
);
standardXHRResponse
(
requests
[
0
]);
player
.
hls
.
bandwidth
=
3000000
;
standardXHRResponse
(
requests
[
1
]);
standardXHRResponse
(
requests
[
2
]);
strictEqual
(
calls
,
1
,
'selects after the initial segment'
);
strictEqual
(
calls
,
2
,
'selects after the initial segment'
);
player
.
currentTime
=
function
()
{
return
1
;
};
...
...
@@ -420,7 +464,8 @@ test('selects a playlist after segment downloads', function() {
player
.
trigger
(
'timeupdate'
);
standardXHRResponse
(
requests
[
3
]);
strictEqual
(
calls
,
2
,
'selects after additional segments'
);
strictEqual
(
calls
,
3
,
'selects after additional segments'
);
});
test
(
'moves to the next segment if there is a network error'
,
function
()
{
...
...
@@ -433,6 +478,8 @@ test('moves to the next segment if there is a network error', function() {
openMediaSource
(
player
);
standardXHRResponse
(
requests
[
0
]);
player
.
hls
.
bandwidth
=
3000000
;
standardXHRResponse
(
requests
[
1
]);
mediaIndex
=
player
.
hls
.
mediaIndex
;
...
...
@@ -486,6 +533,8 @@ test('downloads additional playlists if required', function() {
openMediaSource
(
player
);
standardXHRResponse
(
requests
[
0
]);
player
.
hls
.
bandwidth
=
3000000
;
standardXHRResponse
(
requests
[
1
]);
// before an m3u8 is downloaded, no segments are available
player
.
hls
.
selectPlaylist
=
function
()
{
...
...
@@ -661,7 +710,7 @@ test('downloads the next segment if the buffer is getting low', function() {
strictEqual
(
requests
[
2
].
url
,
window
.
location
.
origin
+
window
.
location
.
pathname
.
split
(
'/'
).
slice
(
0
,
-
1
).
join
(
'/'
)
+
'/manifest/00002.ts'
,
'/manifest/
media-
00002.ts'
,
'made segment request'
);
});
...
...
@@ -1161,6 +1210,8 @@ test('resets the switching algorithm if a request times out', function() {
});
openMediaSource
(
player
);
standardXHRResponse
(
requests
.
shift
());
// master
player
.
hls
.
bandwidth
=
3000000
;
standardXHRResponse
(
requests
.
shift
());
// media.m3u8
// simulate a segment timeout
requests
[
0
].
timedout
=
true
;
...
...
@@ -1207,7 +1258,10 @@ test('remove event handlers on dispose', function() {
oldOn
.
call
(
player
,
type
,
handler
);
};
player
.
off
=
function
(
type
,
handler
)
{
// ignore the top-level videojs removals that aren't relevant to HLS
if
(
type
&&
type
!==
'dispose'
)
{
offhandlers
++
;
}
oldOff
.
call
(
player
,
type
,
handler
);
};
player
.
src
({
...
...
@@ -1215,7 +1269,9 @@ test('remove event handlers on dispose', function() {
type
:
'application/vnd.apple.mpegurl'
});
openMediaSource
(
player
);
player
.
hls
.
playlists
.
trigger
(
'loadedmetadata'
);
standardXHRResponse
(
requests
[
0
]);
standardXHRResponse
(
requests
[
1
]);
player
.
dispose
();
...
...
Please
register
or
sign in
to post a comment