@@ -22,8 +22,7 @@ import {
2222import { gopsSafeToAlignWith , removeGopBuffer , updateGopBuffer } from './util/gops' ;
2323import shallowEqual from './util/shallow-equal.js' ;
2424import { QUOTA_EXCEEDED_ERR } from './error-codes' ;
25- import { timeRangesToArray } from './ranges' ;
26- import { lastBufferedEnd } from './ranges.js' ;
25+ import { timeRangesToArray , lastBufferedEnd , timeAheadOf } from './ranges.js' ;
2726import { getKnownPartCount } from './playlist.js' ;
2827
2928/**
@@ -135,7 +134,7 @@ export const safeBackBufferTrimTime = (seekable, currentTime, targetDuration) =>
135134 return Math . min ( maxTrimTime , trimTime ) ;
136135} ;
137136
138- const segmentInfoString = ( segmentInfo ) => {
137+ export const segmentInfoString = ( segmentInfo ) => {
139138 const {
140139 startOfSegment,
141140 duration,
@@ -160,6 +159,10 @@ const segmentInfoString = (segmentInfo) => {
160159 selection = 'getSyncSegmentCandidate (isSyncRequest)' ;
161160 }
162161
162+ if ( segmentInfo . independent ) {
163+ selection += ` with independent ${ segmentInfo . independent } ` ;
164+ }
165+
163166 const hasPartIndex = typeof partIndex === 'number' ;
164167 const name = segmentInfo . segment . uri ? 'segment' : 'pre-segment' ;
165168 const zeroBasedPartCount = hasPartIndex ? getKnownPartCount ( { preloadSegment : segment } ) - 1 : 0 ;
@@ -1024,9 +1027,20 @@ export default class SegmentLoader extends videojs.EventTarget {
10241027
10251028 if ( ! oldPlaylist || oldPlaylist . uri !== newPlaylist . uri ) {
10261029 if ( this . mediaIndex !== null ) {
1027- // we must " resync" the segment loader when we switch renditions and
1030+ // we must reset/ resync the segment loader when we switch renditions and
10281031 // the segment loader is already synced to the previous rendition
1029- this . resyncLoader ( ) ;
1032+
1033+ // on playlist changes we want it to be possible to fetch
1034+ // at the buffer for vod but not for live. So we use resetLoader
1035+ // for live and resyncLoader for vod. We want this because
1036+ // if a playlist uses independent and non-independent segments/parts the
1037+ // buffer may not accurately reflect the next segment that we should try
1038+ // downloading.
1039+ if ( ! newPlaylist . endList ) {
1040+ this . resetLoader ( ) ;
1041+ } else {
1042+ this . resyncLoader ( ) ;
1043+ }
10301044 }
10311045 this . currentMediaInfo_ = void 0 ;
10321046 this . trigger ( 'playlistupdate' ) ;
@@ -1366,8 +1380,9 @@ export default class SegmentLoader extends videojs.EventTarget {
13661380 * @return {Object } a request object that describes the segment/part to load
13671381 */
13681382 chooseNextRequest_ ( ) {
1369- const bufferedEnd = lastBufferedEnd ( this . buffered_ ( ) ) || 0 ;
1370- const bufferedTime = Math . max ( 0 , bufferedEnd - this . currentTime_ ( ) ) ;
1383+ const buffered = this . buffered_ ( ) ;
1384+ const bufferedEnd = lastBufferedEnd ( buffered ) || 0 ;
1385+ const bufferedTime = timeAheadOf ( buffered , this . currentTime_ ( ) ) ;
13711386 const preloaded = ! this . hasPlayed_ ( ) && bufferedTime >= 1 ;
13721387 const haveEnoughBuffer = bufferedTime >= this . goalBufferLength_ ( ) ;
13731388 const segments = this . playlist_ . segments ;
@@ -1420,14 +1435,15 @@ export default class SegmentLoader extends videojs.EventTarget {
14201435 startTime : this . syncPoint_ . time
14211436 } ) ;
14221437
1423- next . getMediaInfoForTime = this . fetchAtBuffer_ ? 'bufferedEnd' : 'currentTime' ;
1438+ next . getMediaInfoForTime = this . fetchAtBuffer_ ?
1439+ `bufferedEnd ${ bufferedEnd } ` : `currentTime ${ this . currentTime_ ( ) } ` ;
14241440 next . mediaIndex = segmentIndex ;
14251441 next . startOfSegment = startTime ;
14261442 next . partIndex = partIndex ;
14271443 }
14281444
14291445 const nextSegment = segments [ next . mediaIndex ] ;
1430- const nextPart = nextSegment &&
1446+ let nextPart = nextSegment &&
14311447 typeof next . partIndex === 'number' &&
14321448 nextSegment . parts &&
14331449 nextSegment . parts [ next . partIndex ] ;
@@ -1442,6 +1458,28 @@ export default class SegmentLoader extends videojs.EventTarget {
14421458 // Set partIndex to 0
14431459 if ( typeof next . partIndex !== 'number' && nextSegment . parts ) {
14441460 next . partIndex = 0 ;
1461+ nextPart = nextSegment . parts [ 0 ] ;
1462+ }
1463+
1464+ // if we have no buffered data then we need to make sure
1465+ // that the next part we append is "independent" if possible.
1466+ // So we check if the previous part is independent, and request
1467+ // it if it is.
1468+ if ( ! bufferedTime && nextPart && ! nextPart . independent ) {
1469+
1470+ if ( next . partIndex === 0 ) {
1471+ const lastSegment = segments [ next . mediaIndex - 1 ] ;
1472+ const lastSegmentLastPart = lastSegment . parts && lastSegment . parts . length && lastSegment . parts [ lastSegment . parts . length - 1 ] ;
1473+
1474+ if ( lastSegmentLastPart && lastSegmentLastPart . independent ) {
1475+ next . mediaIndex -= 1 ;
1476+ next . partIndex = lastSegment . parts . length - 1 ;
1477+ next . independent = 'previous segment' ;
1478+ }
1479+ } else if ( nextSegment . parts [ next . partIndex - 1 ] . independent ) {
1480+ next . partIndex -= 1 ;
1481+ next . independent = 'previous part' ;
1482+ }
14451483 }
14461484
14471485 const ended = this . mediaSource_ && this . mediaSource_ . readyState === 'ended' ;
@@ -1459,6 +1497,7 @@ export default class SegmentLoader extends videojs.EventTarget {
14591497
14601498 generateSegmentInfo_ ( options ) {
14611499 const {
1500+ independent,
14621501 playlist,
14631502 mediaIndex,
14641503 startOfSegment,
@@ -1499,7 +1538,8 @@ export default class SegmentLoader extends videojs.EventTarget {
14991538 byteLength : 0 ,
15001539 transmuxer : this . transmuxer_ ,
15011540 // type of getMediaInfoForTime that was used to get this segment
1502- getMediaInfoForTime
1541+ getMediaInfoForTime,
1542+ independent
15031543 } ;
15041544
15051545 const overrideCheck =
@@ -1991,7 +2031,7 @@ export default class SegmentLoader extends videojs.EventTarget {
19912031 this . setTimeMapping_ ( segmentInfo . timeline ) ;
19922032
19932033 // for tracking overall stats
1994- this . updateMediaSecondsLoaded_ ( segmentInfo . segment ) ;
2034+ this . updateMediaSecondsLoaded_ ( segmentInfo . part || segmentInfo . segment ) ;
19952035
19962036 // Note that the state isn't changed from loading to appending. This is because abort
19972037 // logic may change behavior depending on the state, and changing state too early may
@@ -2995,15 +3035,19 @@ export default class SegmentLoader extends videojs.EventTarget {
29953035 // and attempt to resync when the post-update seekable window and live
29963036 // point would mean that this was the perfect segment to fetch
29973037 this . trigger ( 'syncinfoupdate' ) ;
2998-
29993038 const segment = segmentInfo . segment ;
3039+ const part = segmentInfo . part ;
3040+ const badSegmentGuess = segment . end &&
3041+ this . currentTime_ ( ) - segment . end > segmentInfo . playlist . targetDuration * 3 ;
3042+ const badPartGuess = part &&
3043+ part . end && this . currentTime_ ( ) - part . end > segmentInfo . playlist . partTargetDuration * 3 ;
30003044
3001- // If we previously appended a segment that ends more than 3 targetDurations before
3045+ // If we previously appended a segment/part that ends more than 3 part/ targetDurations before
30023046 // the currentTime_ that means that our conservative guess was too conservative.
30033047 // In that case, reset the loader state so that we try to use any information gained
30043048 // from the previous request to create a new, more accurate, sync-point.
3005- if ( segment . end &&
3006- this . currentTime_ ( ) - segment . end > segmentInfo . playlist . targetDuration * 3 ) {
3049+ if ( badSegmentGuess || badPartGuess ) {
3050+ this . logger_ ( `bad ${ badSegmentGuess ? ' segment' : 'part' } ${ segmentInfoString ( segmentInfo ) } ` ) ;
30073051 this . resetEverything ( ) ;
30083052 return ;
30093053 }
0 commit comments