@ -187,7 +187,7 @@ videojs.HlsHandler.prototype.src = function(src) {
} . bind ( this ) ) ;
this . playlists . on ( 'loadedplaylist' , function ( ) {
var updatedPlaylist = this . playlists . media ( ) ;
var updatedPlaylist = this . playlists . media ( ) , seekable ;
if ( ! updatedPlaylist ) {
// select the initial variant
@ -196,6 +196,14 @@ videojs.HlsHandler.prototype.src = function(src) {
}
this . updateDuration ( this . playlists . media ( ) ) ;
// update seekable
seekable = this . seekable ( ) ;
if ( this . duration ( ) === Infinity &&
seekable . length !== 0 ) {
this . mediaSource . addSeekableRange_ ( seekable . start ( 0 ) , seekable . end ( 0 ) ) ;
}
oldMediaPlaylist = updatedPlaylist ;
} . bind ( this ) ) ;
@ -291,7 +299,6 @@ videojs.Hls.bufferedAdditions_ = function(original, update) {
return result ;
} ;
var parseCodecs = function ( codecs ) {
var result = {
codecCount : 0 ,
@ -312,6 +319,7 @@ var parseCodecs = function(codecs) {
return result ;
} ;
/ * *
* Blacklist playlists that are known to be codec or
* stream - incompatible with the SourceBuffer configuration . For
@ -445,15 +453,15 @@ videojs.HlsHandler.prototype.play = function() {
// if the viewer has paused and we fell out of the live window,
// seek forward to the earliest available position
if ( this . duration ( ) === Infinity ) {
if ( this . tech_ . currentTime ( ) < this . tech_ . seekable ( ) . start ( 0 ) ) {
this . tech_ . setCurrentTime ( this . tech_ . seekable ( ) . start ( 0 ) ) ;
if ( this . tech_ . currentTime ( ) < this . seekable ( ) . start ( 0 ) ) {
this . tech_ . setCurrentTime ( this . seekable ( ) . start ( 0 ) ) ;
}
}
} ;
videojs . HlsHandler . prototype . setCurrentTime = function ( currentTime ) {
var
buffered = this . findCurrent Buffered_ ( ) ;
buffered = this . findBufferedRange _ ( ) ;
if ( ! ( this . playlists && this . playlists . media ( ) ) ) {
// return immediately if the metadata is not ready yet
@ -501,7 +509,7 @@ videojs.HlsHandler.prototype.duration = function() {
} ;
videojs . HlsHandler . prototype . seekable = function ( ) {
var media ;
var media , seekable ;
if ( ! this . playlists ) {
return videojs . createTimeRanges ( ) ;
@ -511,7 +519,25 @@ videojs.HlsHandler.prototype.seekable = function() {
return videojs . createTimeRanges ( ) ;
}
return videojs . Hls . Playlist . seekable ( media ) ;
seekable = videojs . Hls . Playlist . seekable ( media ) ;
if ( seekable . length === 0 ) {
return seekable ;
}
// if the seekable start is zero, it may be because the player has
// been paused for a long time and stopped buffering. in that case,
// fall back to the playlist loader's running estimate of expired
// time
if ( seekable . start ( 0 ) === 0 ) {
return videojs . createTimeRanges ( [ [
this . playlists . expired_ ,
this . playlists . expired_ + seekable . end ( 0 )
] ] ) ;
}
// seekable has been calculated based on buffering video data so it
// can be returned directly
return seekable ;
} ;
/ * *
@ -522,15 +548,10 @@ videojs.HlsHandler.prototype.updateDuration = function(playlist) {
newDuration = videojs . Hls . Playlist . duration ( playlist ) ,
setDuration = function ( ) {
this . mediaSource . duration = newDuration ;
// update seekable
if ( seekable . length !== 0 && newDuration === Infinity ) {
this . mediaSource . addSeekableRange_ ( seekable . start ( 0 ) , seekable . end ( 0 ) ) ;
}
this . tech_ . trigger ( 'durationchange' ) ;
this . mediaSource . removeEventListener ( 'sourceopen' , setDuration ) ;
} . bind ( this ) ,
seekable = this . seekable ( ) ;
} . bind ( this ) ;
// if the duration has changed, invalidate the cached value
if ( oldDuration !== newDuration ) {
@ -539,10 +560,6 @@ videojs.HlsHandler.prototype.updateDuration = function(playlist) {
this . mediaSource . addEventListener ( 'sourceopen' , setDuration ) ;
} else if ( ! this . sourceBuffer || ! this . sourceBuffer . updating ) {
this . mediaSource . duration = newDuration ;
// update seekable
if ( seekable . length !== 0 && newDuration === Infinity ) {
this . mediaSource . addSeekableRange_ ( seekable . start ( 0 ) , seekable . end ( 0 ) ) ;
}
this . tech_ . trigger ( 'durationchange' ) ;
}
}
@ -745,42 +762,62 @@ videojs.HlsHandler.prototype.stopCheckingBuffer_ = function() {
this . tech_ . off ( 'waiting' , this . drainBuffer ) ;
} ;
/ * *
* Attempts to find the buffered TimeRange where playback is currently
* happening . Returns a new TimeRange with one or zero ranges .
* /
videojs . HlsHandler . prototype . findCurrentBuffered_ = function ( ) {
var
ranges ,
i ,
tech = this . tech_ ,
// !!The order of the next two lines is important!!
// `currentTime` must be equal-to or greater-than the start of the
// buffered range. Flash executes out-of-process so, every value can
// change behind the scenes from line-to-line. By reading `currentTime`
// after `buffered`, we ensure that it is always a current or later
// value during playback.
buffered = tech . buffered ( ) ,
currentTime = tech . currentTime ( ) ;
var filterBufferedRanges = function ( predicate ) {
return function ( time ) {
var
i ,
ranges = [ ] ,
tech = this . tech_ ,
// !!The order of the next two assignments is important!!
// `currentTime` must be equal-to or greater-than the start of the
// buffered range. Flash executes out-of-process so, every value can
// change behind the scenes from line-to-line. By reading `currentTime`
// after `buffered`, we ensure that it is always a current or later
// value during playback.
buffered = tech . buffered ( ) ;
if ( time === undefined ) {
time = tech . currentTime ( ) ;
}
if ( buffered && buffered . length ) {
// Search for a range containing the play-head
for ( i = 0 ; i < buffered . length ; i ++ ) {
if ( buffered . start ( i ) - TIME_FUDGE_FACTOR <= currentTime &&
buffered . end ( i ) + TIME_FUDGE_FACTOR >= currentTime ) {
ranges = videojs . createTimeRanges ( buffered . start ( i ) , buffered . end ( i ) ) ;
ranges . indexOf = i ;
return ranges ;
if ( buffered && buffered . length ) {
// Search for a range containing the play-head
for ( i = 0 ; i < buffered . length ; i ++ ) {
if ( predicate ( buffered . start ( i ) , buffered . end ( i ) , time ) ) {
ranges . push ( [ buffered . start ( i ) , buffered . end ( i ) ] ) ;
}
}
}
}
// Return an empty range if no ranges exist
ranges = videojs . createTimeRanges ( ) ;
ranges . indexOf = - 1 ;
return ranges ;
return videojs . createTimeRanges ( ranges ) ;
} ;
} ;
/ * *
* Attempts to find the buffered TimeRange that contains the specified
* time , or where playback is currently happening if no specific time
* is specified .
* @ param time ( optional ) { number } the time to filter on . Defaults to
* currentTime .
* @ return a new TimeRanges object .
* /
videojs . HlsHandler . prototype . findBufferedRange_ = filterBufferedRanges ( function ( start , end , time ) {
return start - TIME_FUDGE_FACTOR <= time &&
end + TIME_FUDGE_FACTOR >= time ;
} ) ;
/ * *
* Returns the TimeRanges that begin at or later than the specified
* time .
* @ param time ( optional ) { number } the time to filter on . Defaults to
* currentTime .
* @ return a new TimeRanges object .
* /
videojs . HlsHandler . prototype . findNextBufferedRange_ = filterBufferedRanges ( function ( start , end , time ) {
return start - TIME_FUDGE_FACTOR >= time ;
} ) ;
/ * *
* Determines whether there is enough video data currently in the buffer
* and downloads a new segment if the buffered time is less than the goal .
@ -791,7 +828,7 @@ videojs.HlsHandler.prototype.fillBuffer = function(mediaIndex) {
var
tech = this . tech_ ,
currentTime = tech . currentTime ( ) ,
currentBuffered = this . findCurrent Buffered_ ( ) ,
currentBuffered = this . findBufferedRange _ ( ) ,
currentBufferedEnd = 0 ,
bufferedTime = 0 ,
segment ,
@ -1025,7 +1062,7 @@ videojs.HlsHandler.prototype.drainBuffer = function(event) {
segIv ,
segmentTimestampOffset = 0 ,
hasBufferedContent = ( this . tech_ . buffered ( ) . length !== 0 ) ,
currentBuffered = this . findCurrent Buffered_ ( ) ,
currentBuffered = this . findBufferedRange _ ( ) ,
outsideBufferedRanges = ! ( currentBuffered && currentBuffered . length ) ;
// if the buffer is empty or the source buffer hasn't been created
@ -1132,6 +1169,7 @@ videojs.HlsHandler.prototype.updateEndHandler_ = function () {
playlist ,
currentMediaIndex ,
currentBuffered ,
seekable ,
timelineUpdates ;
this . pendingSegment_ = null ;
@ -1144,7 +1182,7 @@ videojs.HlsHandler.prototype.updateEndHandler_ = function () {
playlist = this . playlists . media ( ) ;
segments = playlist . segments ;
currentMediaIndex = segmentInfo . mediaIndex + ( segmentInfo . mediaSequence - playlist . mediaSequence ) ;
currentBuffered = this . findCurrent Buffered_ ( ) ;
currentBuffered = this . findBufferedRange _ ( ) ;
// if we switched renditions don't try to add segment timeline
// information to the playlist
@ -1156,9 +1194,25 @@ videojs.HlsHandler.prototype.updateEndHandler_ = function () {
// 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
// in that case, seek again
seekable = this . seekable ( ) ;
if ( this . tech_ . seeking ( ) &&
currentBuffered . length === 0 ) {
if ( seekable . length &&
this . tech_ . currentTime ( ) < seekable . start ( 0 ) ) {
var next = this . findNextBufferedRange_ ( ) ;
if ( next . length ) {
videojs . log ( 'tried seeking to' , this . tech_ . currentTime ( ) , 'but that was too early, retrying at' , next . start ( 0 ) ) ;
this . tech_ . setCurrentTime ( next . start ( 0 ) ) ;
}
}
}
timelineUpdates = videojs . Hls . bufferedAdditions_ ( segmentInfo . buffered ,
this . tech_ . buffered ( ) ) ;
timelineUpdates . forEach ( function ( update ) {
if ( segment ) {
if ( update . end !== undefined ) {