@@ -70,7 +70,6 @@ export default class StreamController
70
70
private altAudio : AlternateAudio = AlternateAudio . DISABLED ;
71
71
private audioOnly : boolean = false ;
72
72
private fragPlaying : Fragment | null = null ;
73
- private fragLastKbps : number = 0 ;
74
73
private couldBacktrack : boolean = false ;
75
74
private backtrackFragment : Fragment | null = null ;
76
75
private audioCodecSwitch : boolean = false ;
@@ -436,43 +435,46 @@ export default class StreamController
436
435
* we should take into account new segment fetch time
437
436
*/
438
437
public nextLevelSwitch ( ) {
439
- const { levels, media } = this ;
438
+ const { levels, media, hls , config } = this ;
440
439
// ensure that media is defined and that metadata are available (to retrieve currentTime)
441
- if ( media ?. readyState ) {
442
- let fetchdelay ;
443
- const fragPlayingCurrent = this . getAppendedFrag ( media . currentTime ) ;
444
- if ( fragPlayingCurrent && fragPlayingCurrent . start > 1 ) {
445
- // flush buffer preceding current fragment (flush until current fragment start offset)
446
- // minus 1s to avoid video freezing, that could happen if we flush keyframe of current video ...
447
- this . flushMainBuffer ( 0 , fragPlayingCurrent . start - 1 ) ;
440
+ if ( media ?. readyState && levels && hls && config ) {
441
+ const bufferInfo = this . getMainFwdBufferInfo ( ) ;
442
+ if ( ! bufferInfo ) {
443
+ return ;
448
444
}
449
445
const levelDetails = this . getLevelDetails ( ) ;
450
- if ( levelDetails ?. live ) {
451
- const bufferInfo = this . getMainFwdBufferInfo ( ) ;
452
- // Do not flush in live stream with low buffer
453
- if ( ! bufferInfo || bufferInfo . len < levelDetails . targetduration * 2 ) {
454
- return ;
455
- }
456
- }
457
- if ( ! media . paused && levels ) {
446
+
447
+ let fetchdelay = 0 ;
448
+ if ( ! media . paused ) {
458
449
// add a safety delay of 1s
459
- const nextLevelId = this . hls . nextLoadLevel ;
450
+ const ttfbSec = 1 + hls . ttfbEstimate / 1000 ;
451
+ const bandwidth = hls . bandwidthEstimate * config . abrBandWidthUpFactor ;
452
+ const nextLevelId = hls . nextLoadLevel ;
460
453
const nextLevel = levels [ nextLevelId ] ;
461
- const fragLastKbps = this . fragLastKbps ;
462
- if ( fragLastKbps && this . fragCurrent ) {
463
- fetchdelay =
464
- ( this . fragCurrent . duration * nextLevel . maxBitrate ) /
465
- ( 1000 * fragLastKbps ) +
466
- 1 ;
467
- } else {
468
- fetchdelay = 0 ;
454
+ const fragDuration =
455
+ ( levelDetails &&
456
+ ( this . loadingParts
457
+ ? levelDetails . partTarget
458
+ : levelDetails . averagetargetduration ) ) ||
459
+ this . fragCurrent ?. duration ||
460
+ 6 ;
461
+ fetchdelay =
462
+ ttfbSec + ( nextLevel . maxBitrate * fragDuration ) / bandwidth ;
463
+ if ( ! nextLevel . details ) {
464
+ fetchdelay += ttfbSec ;
469
465
}
470
- } else {
471
- fetchdelay = 0 ;
472
466
}
473
- // this.log('fetchdelay:'+fetchdelay);
467
+
468
+ // Do not flush in live stream with low buffer
469
+
470
+ const okToFlushForwardBuffer =
471
+ ! levelDetails ?. live ||
472
+ ( bufferInfo . len || 0 ) > levelDetails . targetduration * 2 ;
473
+
474
474
// find buffer range that will be reached once new fragment will be fetched
475
- const bufferedFrag = this . getBufferedFrag ( media . currentTime + fetchdelay ) ;
475
+ const bufferedFrag = okToFlushForwardBuffer
476
+ ? this . getBufferedFrag ( media . currentTime + fetchdelay )
477
+ : null ;
476
478
if ( bufferedFrag ) {
477
479
// we can flush buffer range following this one without stalling playback
478
480
const nextBufferedFrag = this . followingBufferedFrag ( bufferedFrag ) ;
@@ -498,7 +500,15 @@ export default class StreamController
498
500
this . flushMainBuffer ( startPts , Number . POSITIVE_INFINITY ) ;
499
501
}
500
502
}
503
+ // remove back-buffer
504
+ const fragPlayingCurrent = this . getAppendedFrag ( media . currentTime ) ;
505
+ if ( fragPlayingCurrent && fragPlayingCurrent . start > 1 ) {
506
+ // flush buffer preceding current fragment (flush until current fragment start offset)
507
+ // minus 1s to avoid video freezing, that could happen if we flush keyframe of current video ...
508
+ this . flushMainBuffer ( 0 , fragPlayingCurrent . start - 1 ) ;
509
+ }
501
510
}
511
+ this . tickImmediate ( ) ;
502
512
}
503
513
504
514
private abortCurrentFrag ( ) {
@@ -601,7 +611,6 @@ export default class StreamController
601
611
this . log ( 'Trigger BUFFER_RESET' ) ;
602
612
this . hls . trigger ( Events . BUFFER_RESET , undefined ) ;
603
613
this . couldBacktrack = false ;
604
- this . fragLastKbps = 0 ;
605
614
this . fragPlaying = this . backtrackFragment = null ;
606
615
this . altAudio = AlternateAudio . DISABLED ;
607
616
this . audioOnly = false ;
@@ -987,10 +996,6 @@ export default class StreamController
987
996
}
988
997
return ;
989
998
}
990
- const stats = part ? part . stats : frag . stats ;
991
- this . fragLastKbps = Math . round (
992
- ( 8 * stats . total ) / ( stats . buffering . end - stats . loading . first ) ,
993
- ) ;
994
999
if ( isMediaFragment ( frag ) ) {
995
1000
this . fragPrevious = frag ;
996
1001
}
0 commit comments