mirror of
https://github.com/jellyfin/jellyfin-web
synced 2025-03-30 19:56:21 +00:00
update components
This commit is contained in:
parent
8df5412c5c
commit
8f05a7a53d
15 changed files with 131 additions and 159 deletions
|
@ -34,7 +34,9 @@ class AbrController extends EventHandler {
|
|||
if (!this.timer) {
|
||||
this.timer = setInterval(this.onCheck, 100);
|
||||
}
|
||||
this.fragCurrent = data.frag;
|
||||
let frag = data.frag;
|
||||
frag.trequest = performance.now();
|
||||
this.fragCurrent = frag;
|
||||
}
|
||||
|
||||
abandonRulesCheck() {
|
||||
|
@ -57,12 +59,13 @@ class AbrController extends EventHandler {
|
|||
let requestDelay = performance.now() - frag.trequest;
|
||||
// monitor fragment load progress after half of expected fragment duration,to stabilize bitrate
|
||||
if (requestDelay > (500 * frag.duration)) {
|
||||
let loadRate = Math.max(1,frag.loaded * 1000 / requestDelay); // byte/s; at least 1 byte/s to avoid division by zero
|
||||
if (frag.expectedLen < frag.loaded) {
|
||||
frag.expectedLen = frag.loaded;
|
||||
}
|
||||
let levels = hls.levels,
|
||||
loadRate = Math.max(1,frag.loaded * 1000 / requestDelay), // byte/s; at least 1 byte/s to avoid division by zero
|
||||
// compute expected fragment length using frag duration and level bitrate. also ensure that expected len is gte than already loaded size
|
||||
expectedLen = Math.max(frag.loaded, Math.round(frag.duration * levels[frag.level].bitrate / 8));
|
||||
|
||||
let pos = v.currentTime;
|
||||
let fragLoadedDelay = (frag.expectedLen - frag.loaded) / loadRate;
|
||||
let fragLoadedDelay = (expectedLen - frag.loaded) / loadRate;
|
||||
let bufferStarvationDelay = BufferHelper.bufferInfo(v,pos,hls.config.maxBufferHole).end - pos;
|
||||
// consider emergency switch down only if we have less than 2 frag buffered AND
|
||||
// time to finish loading current fragment is bigger than buffer starvation delay
|
||||
|
@ -75,7 +78,7 @@ class AbrController extends EventHandler {
|
|||
// compute time to load next fragment at lower level
|
||||
// 0.8 : consider only 80% of current bw to be conservative
|
||||
// 8 = bits per byte (bps/Bps)
|
||||
fragLevelNextLoadedDelay = frag.duration * hls.levels[nextLoadLevel].bitrate / (8 * 0.8 * loadRate);
|
||||
fragLevelNextLoadedDelay = frag.duration * levels[nextLoadLevel].bitrate / (8 * 0.8 * loadRate);
|
||||
logger.log(`fragLoadedDelay/bufferStarvationDelay/fragLevelNextLoadedDelay[${nextLoadLevel}] :${fragLoadedDelay.toFixed(1)}/${bufferStarvationDelay.toFixed(1)}/${fragLevelNextLoadedDelay.toFixed(1)}`);
|
||||
if (fragLevelNextLoadedDelay < bufferStarvationDelay) {
|
||||
// we found a lower level that be rebuffering free with current estimated bw !
|
||||
|
|
|
@ -38,7 +38,7 @@ class EwmaBandWidthEstimator {
|
|||
|
||||
|
||||
getEstimate() {
|
||||
if (this.fast_.getTotalWeight() < this.minWeight_) {
|
||||
if (!this.fast_ || this.fast_.getTotalWeight() < this.minWeight_) {
|
||||
return this.defaultEstimate_;
|
||||
}
|
||||
//console.log('slow estimate:'+ Math.round(this.slow_.getEstimate()));
|
||||
|
|
|
@ -199,9 +199,12 @@ class StreamController extends EventHandler {
|
|||
|
||||
if (bufferEnd < Math.max(start, end - maxLatency)) {
|
||||
let targetLatency = config.liveSyncDuration !== undefined ? config.liveSyncDuration : config.liveSyncDurationCount * levelDetails.targetduration;
|
||||
this.seekAfterBuffered = start + Math.max(0, levelDetails.totalduration - targetLatency);
|
||||
logger.log(`buffer end: ${bufferEnd} is located too far from the end of live sliding playlist, media position will be reseted to: ${this.seekAfterBuffered.toFixed(3)}`);
|
||||
bufferEnd = this.seekAfterBuffered;
|
||||
let liveSyncPosition = start + Math.max(0, levelDetails.totalduration - targetLatency);
|
||||
logger.log(`buffer end: ${bufferEnd} is located too far from the end of live sliding playlist, reset currentTime to : ${liveSyncPosition.toFixed(3)}`);
|
||||
bufferEnd = liveSyncPosition;
|
||||
if (media && media.readyState && media.duration > liveSyncPosition) {
|
||||
media.currentTime = liveSyncPosition;
|
||||
}
|
||||
}
|
||||
|
||||
// if end of buffer greater than live edge, don't load any fragment
|
||||
|
@ -306,11 +309,6 @@ class StreamController extends EventHandler {
|
|||
hls.trigger(Event.KEY_LOADING, {frag: frag});
|
||||
} else {
|
||||
logger.log(`Loading ${frag.sn} of [${levelDetails.startSN} ,${levelDetails.endSN}],level ${level}, currentTime:${pos},bufferEnd:${bufferEnd.toFixed(3)}`);
|
||||
frag.autoLevel = hls.autoLevelEnabled;
|
||||
if (this.levels.length > 1) {
|
||||
frag.expectedLen = Math.round(frag.duration * this.levels[level].bitrate / 8);
|
||||
frag.trequest = performance.now();
|
||||
}
|
||||
// ensure that we are not reloading the same fragments in loop ...
|
||||
if (this.fragLoadIdx !== undefined) {
|
||||
this.fragLoadIdx++;
|
||||
|
@ -331,6 +329,7 @@ class StreamController extends EventHandler {
|
|||
frag.loadIdx = this.fragLoadIdx;
|
||||
this.fragCurrent = frag;
|
||||
this.startFragRequested = true;
|
||||
frag.autoLevel = hls.autoLevelEnabled;
|
||||
hls.trigger(Event.FRAG_LOADING, {frag: frag});
|
||||
this.state = State.FRAG_LOADING;
|
||||
}
|
||||
|
@ -988,37 +987,33 @@ class StreamController extends EventHandler {
|
|||
|
||||
_checkBuffer() {
|
||||
var media = this.media;
|
||||
if(media) {
|
||||
// compare readyState
|
||||
var readyState = media.readyState;
|
||||
// if ready state different from HAVE_NOTHING (numeric value 0), we are allowed to seek
|
||||
if(readyState) {
|
||||
var targetSeekPosition, currentTime;
|
||||
// if seek after buffered defined, let's seek if within acceptable range
|
||||
var seekAfterBuffered = this.seekAfterBuffered;
|
||||
if(seekAfterBuffered) {
|
||||
if(media.duration >= seekAfterBuffered) {
|
||||
targetSeekPosition = seekAfterBuffered;
|
||||
this.seekAfterBuffered = undefined;
|
||||
}
|
||||
} else {
|
||||
currentTime = media.currentTime;
|
||||
var loadedmetadata = this.loadedmetadata;
|
||||
|
||||
// adjust currentTime to start position on loaded metadata
|
||||
if(!loadedmetadata && media.buffered.length) {
|
||||
this.loadedmetadata = true;
|
||||
// only adjust currentTime if not equal to 0
|
||||
if (!currentTime && currentTime !== this.startPosition) {
|
||||
targetSeekPosition = this.startPosition;
|
||||
// if ready state different from HAVE_NOTHING (numeric value 0), we are allowed to seek
|
||||
if(media && media.readyState) {
|
||||
let currentTime = media.currentTime,
|
||||
buffered = media.buffered;
|
||||
// adjust currentTime to start position on loaded metadata
|
||||
if(!this.loadedmetadata && buffered.length) {
|
||||
this.loadedmetadata = true;
|
||||
// only adjust currentTime if startPosition not equal to 0
|
||||
let startPosition = this.startPosition;
|
||||
// if currentTime === 0 AND not matching with expected startPosition
|
||||
if (!currentTime && currentTime !== startPosition) {
|
||||
if (startPosition) {
|
||||
logger.log(`target start position:${startPosition}`);
|
||||
// at that stage, there should be only one buffered range, as we reach that code after first fragment has been
|
||||
let bufferStart = buffered.start(0),
|
||||
bufferEnd = buffered.end(0);
|
||||
// if startPosition not buffered, let's seek to buffered.start(0)
|
||||
if(startPosition < bufferStart || startPosition > bufferEnd) {
|
||||
startPosition = bufferStart;
|
||||
logger.log(`target start position not buffered, seek to buffered.start(0) ${bufferStart}`);
|
||||
}
|
||||
logger.log(`adjust currentTime from ${currentTime} to ${startPosition}`);
|
||||
media.currentTime = startPosition;
|
||||
}
|
||||
}
|
||||
if (targetSeekPosition) {
|
||||
currentTime = targetSeekPosition;
|
||||
logger.log(`target seek position:${targetSeekPosition}`);
|
||||
}
|
||||
var bufferInfo = BufferHelper.bufferInfo(media,currentTime,0),
|
||||
} else {
|
||||
let bufferInfo = BufferHelper.bufferInfo(media,currentTime,0),
|
||||
expectedPlaying = !(media.paused || // not playing when media is paused
|
||||
media.ended || // not playing when media is ended
|
||||
media.buffered.length === 0), // not playing if nothing buffered
|
||||
|
@ -1030,8 +1025,7 @@ _checkBuffer() {
|
|||
logger.log(`playback not stuck anymore @${currentTime}`);
|
||||
}
|
||||
// check buffer upfront
|
||||
// if less than jumpThreshold second is buffered, and media is expected to play but playhead is not moving,
|
||||
// and we have a new buffer range available upfront, let's seek to that one
|
||||
// if less than jumpThreshold second is buffered, let's check in more details
|
||||
if(expectedPlaying && bufferInfo.len <= jumpThreshold) {
|
||||
if(playheadMoving) {
|
||||
// playhead moving
|
||||
|
@ -1048,7 +1042,7 @@ _checkBuffer() {
|
|||
this.seekHoleNudgeDuration += this.config.seekHoleNudgeDuration;
|
||||
}
|
||||
}
|
||||
// if we are below threshold, try to jump if next buffer range is close
|
||||
// if we are below threshold, try to jump to start of next buffer range if close
|
||||
if(bufferInfo.len <= jumpThreshold) {
|
||||
// no buffer available @ currentTime, check if next buffer is close (within a config.maxSeekHole second range)
|
||||
var nextBufferStart = bufferInfo.nextStart, delta = nextBufferStart-currentTime;
|
||||
|
@ -1063,20 +1057,6 @@ _checkBuffer() {
|
|||
this.hls.trigger(Event.ERROR, {type: ErrorTypes.MEDIA_ERROR, details: ErrorDetails.BUFFER_SEEK_OVER_HOLE, fatal: false, hole : hole});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let currentTime = media.currentTime;
|
||||
if (targetSeekPosition && currentTime !== targetSeekPosition) {
|
||||
if(bufferInfo.len === 0) {
|
||||
let nextStart = bufferInfo.nextStart;
|
||||
if (nextStart !== undefined &&
|
||||
(nextStart - targetSeekPosition) < this.config.maxSeekHole) {
|
||||
targetSeekPosition = nextStart;
|
||||
logger.log(`target seek position not buffered, seek to next buffered ${targetSeekPosition}`);
|
||||
}
|
||||
}
|
||||
logger.log(`adjust currentTime from ${currentTime} to ${targetSeekPosition}`);
|
||||
media.currentTime = targetSeekPosition;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,7 +22,9 @@ import KeyLoader from './loader/key-loader';
|
|||
class Hls {
|
||||
|
||||
static isSupported() {
|
||||
return (window.MediaSource && window.MediaSource.isTypeSupported('video/mp4; codecs="avc1.42E01E,mp4a.40.2"'));
|
||||
return (window.MediaSource &&
|
||||
typeof window.MediaSource.isTypeSupported === 'function' &&
|
||||
window.MediaSource.isTypeSupported('video/mp4; codecs="avc1.42E01E,mp4a.40.2"'));
|
||||
}
|
||||
|
||||
static get Events() {
|
||||
|
|
|
@ -63,8 +63,9 @@ class XhrLoader {
|
|||
xhr.setRequestHeader('Range', 'bytes=' + this.byteRange);
|
||||
}
|
||||
xhr.responseType = this.responseType;
|
||||
this.stats.tfirst = null;
|
||||
this.stats.loaded = 0;
|
||||
let stats = this.stats;
|
||||
stats.tfirst = 0;
|
||||
stats.loaded = 0;
|
||||
if (this.xhrSetup) {
|
||||
this.xhrSetup(xhr, this.url);
|
||||
}
|
||||
|
@ -81,7 +82,7 @@ class XhrLoader {
|
|||
// http status between 200 to 299 are all successful
|
||||
if (status >= 200 && status < 300) {
|
||||
window.clearTimeout(this.timeoutHandle);
|
||||
stats.tload = performance.now();
|
||||
stats.tload = Math.max(stats.tfirst,performance.now());
|
||||
this.onSuccess(event, stats);
|
||||
} else {
|
||||
// error ...
|
||||
|
@ -108,8 +109,8 @@ class XhrLoader {
|
|||
|
||||
loadprogress(event) {
|
||||
var stats = this.stats;
|
||||
if (stats.tfirst === null) {
|
||||
stats.tfirst = performance.now();
|
||||
if (stats.tfirst === 0) {
|
||||
stats.tfirst = Math.max(performance.now(), stats.trequest);
|
||||
}
|
||||
stats.loaded = event.loaded;
|
||||
if (this.onProgress) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue