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
c6b706f48a
commit
5de2e610ce
35 changed files with 1420 additions and 1130 deletions
|
@ -15,11 +15,12 @@ const State = {
|
|||
IDLE : 0,
|
||||
KEY_LOADING : 1,
|
||||
FRAG_LOADING : 2,
|
||||
WAITING_LEVEL : 3,
|
||||
PARSING : 4,
|
||||
PARSED : 5,
|
||||
APPENDING : 6,
|
||||
BUFFER_FLUSHING : 7
|
||||
FRAG_LOADING_WAITING_RETRY : 3,
|
||||
WAITING_LEVEL : 4,
|
||||
PARSING : 5,
|
||||
PARSED : 6,
|
||||
APPENDING : 7,
|
||||
BUFFER_FLUSHING : 8
|
||||
};
|
||||
|
||||
class MSEMediaController {
|
||||
|
@ -28,6 +29,7 @@ class MSEMediaController {
|
|||
this.config = hls.config;
|
||||
this.audioCodecSwap = false;
|
||||
this.hls = hls;
|
||||
this.ticks = 0;
|
||||
// Source Buffer listeners
|
||||
this.onsbue = this.onSBUpdateEnd.bind(this);
|
||||
this.onsbe = this.onSBUpdateError.bind(this);
|
||||
|
@ -84,6 +86,7 @@ class MSEMediaController {
|
|||
this.demuxer = new Demuxer(hls);
|
||||
this.timer = setInterval(this.ontick, 100);
|
||||
this.level = -1;
|
||||
this.fragLoadError = 0;
|
||||
hls.on(Event.FRAG_LOADED, this.onfl);
|
||||
hls.on(Event.FRAG_PARSING_INIT_SEGMENT, this.onis);
|
||||
hls.on(Event.FRAG_PARSING_DATA, this.onfpg);
|
||||
|
@ -136,6 +139,17 @@ class MSEMediaController {
|
|||
}
|
||||
|
||||
tick() {
|
||||
this.ticks++;
|
||||
if (this.ticks === 1) {
|
||||
this.doTick();
|
||||
if (this.ticks > 1) {
|
||||
setTimeout(this.tick, 1);
|
||||
}
|
||||
this.ticks = 0;
|
||||
}
|
||||
}
|
||||
|
||||
doTick() {
|
||||
var pos, level, levelDetails, hls = this.hls;
|
||||
switch(this.state) {
|
||||
case State.ERROR:
|
||||
|
@ -367,6 +381,17 @@ class MSEMediaController {
|
|||
}
|
||||
}
|
||||
break;
|
||||
case State.FRAG_LOADING_WAITING_RETRY:
|
||||
var now = performance.now();
|
||||
var retryDate = this.retryDate;
|
||||
var media = this.media;
|
||||
var isSeeking = media && media.seeking;
|
||||
// if current time is gt than retryDate, or if media seeking let's switch to IDLE state to retry loading
|
||||
if(!retryDate || (now >= retryDate) || isSeeking) {
|
||||
logger.log(`mediaController: retryDate reached, switch back to IDLE state`);
|
||||
this.state = State.IDLE;
|
||||
}
|
||||
break;
|
||||
case State.PARSING:
|
||||
// nothing to do, wait for fragment being parsed
|
||||
break;
|
||||
|
@ -454,10 +479,10 @@ class MSEMediaController {
|
|||
default:
|
||||
break;
|
||||
}
|
||||
// check/update current fragment
|
||||
this._checkFragmentChanged();
|
||||
// check buffer
|
||||
this._checkBuffer();
|
||||
// check/update current fragment
|
||||
this._checkFragmentChanged();
|
||||
}
|
||||
|
||||
|
||||
|
@ -876,8 +901,12 @@ class MSEMediaController {
|
|||
}
|
||||
|
||||
onMediaMetadata() {
|
||||
if (this.media.currentTime !== this.startPosition) {
|
||||
this.media.currentTime = this.startPosition;
|
||||
var media = this.media,
|
||||
currentTime = media.currentTime;
|
||||
// only adjust currentTime if not equal to 0
|
||||
if (!currentTime && currentTime !== this.startPosition) {
|
||||
logger.log('onMediaMetadata: adjust currentTime to startPosition');
|
||||
media.currentTime = this.startPosition;
|
||||
}
|
||||
this.loadedmetadata = true;
|
||||
this.tick();
|
||||
|
@ -992,8 +1021,11 @@ class MSEMediaController {
|
|||
level = fragCurrent.level,
|
||||
sn = fragCurrent.sn,
|
||||
audioCodec = currentLevel.audioCodec;
|
||||
if(audioCodec && this.audioCodecSwap) {
|
||||
if(this.audioCodecSwap) {
|
||||
logger.log('swapping playlist audio codec');
|
||||
if(audioCodec === undefined) {
|
||||
audioCodec = this.lastAudioCodec;
|
||||
}
|
||||
if(audioCodec.indexOf('mp4a.40.5') !==-1) {
|
||||
audioCodec = 'mp4a.40.2';
|
||||
} else {
|
||||
|
@ -1012,6 +1044,7 @@ class MSEMediaController {
|
|||
// check if codecs have been explicitely defined in the master playlist for this level;
|
||||
// if yes use these ones instead of the ones parsed from the demux
|
||||
var audioCodec = this.levels[this.level].audioCodec, videoCodec = this.levels[this.level].videoCodec, sb;
|
||||
this.lastAudioCodec = data.audioCodec;
|
||||
if(audioCodec && this.audioCodecSwap) {
|
||||
logger.log('swapping playlist audio codec');
|
||||
if(audioCodec.indexOf('mp4a.40.5') !==-1) {
|
||||
|
@ -1070,7 +1103,7 @@ class MSEMediaController {
|
|||
this.tparse2 = Date.now();
|
||||
var level = this.levels[this.level],
|
||||
frag = this.fragCurrent;
|
||||
logger.log(`parsed data, type/startPTS/endPTS/startDTS/endDTS/nb:${data.type}/${data.startPTS.toFixed(3)}/${data.endPTS.toFixed(3)}/${data.startDTS.toFixed(3)}/${data.endDTS.toFixed(3)}/${data.nb}`);
|
||||
logger.log(`parsed ${data.type},PTS:[${data.startPTS.toFixed(3)},${data.endPTS.toFixed(3)}],DTS:[${data.startDTS.toFixed(3)}/${data.endDTS.toFixed(3)}],nb:${data.nb}`);
|
||||
var drift = LevelHelper.updateFragPTS(level.details,frag.sn,data.startPTS,data.endPTS);
|
||||
this.hls.trigger(Event.LEVEL_PTS_UPDATED, {details: level.details, level: this.level, drift: drift});
|
||||
|
||||
|
@ -1097,25 +1130,32 @@ class MSEMediaController {
|
|||
|
||||
onError(event, data) {
|
||||
switch(data.details) {
|
||||
// abort fragment loading on errors
|
||||
case ErrorDetails.FRAG_LOAD_ERROR:
|
||||
case ErrorDetails.FRAG_LOAD_TIMEOUT:
|
||||
var loadError = this.fragLoadError;
|
||||
if(loadError) {
|
||||
loadError++;
|
||||
} else {
|
||||
loadError=1;
|
||||
}
|
||||
if (loadError <= this.config.fragLoadingMaxRetry) {
|
||||
this.fragLoadError = loadError;
|
||||
// retry loading
|
||||
this.state = State.IDLE;
|
||||
} else {
|
||||
logger.error(`mediaController: ${data.details} reaches max retry, redispatch as fatal ...`);
|
||||
// redispatch same error but with fatal set to true
|
||||
data.fatal = true;
|
||||
this.hls.trigger(event, data);
|
||||
this.state = State.ERROR;
|
||||
if(!data.fatal) {
|
||||
var loadError = this.fragLoadError;
|
||||
if(loadError) {
|
||||
loadError++;
|
||||
} else {
|
||||
loadError=1;
|
||||
}
|
||||
if (loadError <= this.config.fragLoadingMaxRetry) {
|
||||
this.fragLoadError = loadError;
|
||||
// reset load counter to avoid frag loop loading error
|
||||
data.frag.loadCounter = 0;
|
||||
// exponential backoff capped to 64s
|
||||
var delay = Math.min(Math.pow(2,loadError-1)*this.config.fragLoadingRetryDelay,64000);
|
||||
logger.warn(`mediaController: frag loading failed, retry in ${delay} ms`);
|
||||
this.retryDate = performance.now() + delay;
|
||||
// retry loading state
|
||||
this.state = State.FRAG_LOADING_WAITING_RETRY;
|
||||
} else {
|
||||
logger.error(`mediaController: ${data.details} reaches max retry, redispatch as fatal ...`);
|
||||
// redispatch same error but with fatal set to true
|
||||
data.fatal = true;
|
||||
this.hls.trigger(event, data);
|
||||
this.state = State.ERROR;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ErrorDetails.FRAG_LOOP_LOADING_ERROR:
|
||||
|
@ -1162,24 +1202,35 @@ _checkBuffer() {
|
|||
media.currentTime = seekAfterBuffered;
|
||||
this.seekAfterBuffered = undefined;
|
||||
}
|
||||
} else if(readyState < 3 ) {
|
||||
// readyState = 1 or 2
|
||||
// HAVE_METADATA (numeric value 1) Enough of the resource has been obtained that the duration of the resource is available.
|
||||
// The API will no longer throw an exception when seeking.
|
||||
// HAVE_CURRENT_DATA (numeric value 2) Data for the immediate current playback position is available,
|
||||
// but either not enough data is available that the user agent could
|
||||
// successfully advance the current playback position
|
||||
var currentTime = media.currentTime;
|
||||
var bufferInfo = this.bufferInfo(currentTime,0);
|
||||
// check if current time is buffered or not
|
||||
if(bufferInfo.len === 0) {
|
||||
// no buffer available @ currentTime, check if next buffer is close (in a 300 ms range)
|
||||
var nextBufferStart = bufferInfo.nextStart;
|
||||
if(nextBufferStart && (nextBufferStart - currentTime < 0.3)) {
|
||||
// next buffer is close ! adjust currentTime to nextBufferStart
|
||||
// this will ensure effective video decoding
|
||||
logger.log(`adjust currentTime from ${currentTime} to ${nextBufferStart}`);
|
||||
media.currentTime = nextBufferStart;
|
||||
} else {
|
||||
var currentTime = media.currentTime,
|
||||
bufferInfo = this.bufferInfo(currentTime,0),
|
||||
isPlaying = !(media.paused || media.ended || media.seeking || readyState < 3),
|
||||
jumpThreshold = 0.2;
|
||||
|
||||
// check buffer upfront
|
||||
// if less than 200ms is buffered, and media is playing but playhead is not moving,
|
||||
// and we have a new buffer range available upfront, let's seek to that one
|
||||
if(bufferInfo.len <= jumpThreshold) {
|
||||
if(currentTime > media.playbackRate*this.lastCurrentTime || !isPlaying) {
|
||||
// playhead moving or media not playing
|
||||
jumpThreshold = 0;
|
||||
} else {
|
||||
logger.trace('playback seems stuck');
|
||||
}
|
||||
// if we are below threshold, try to jump if next buffer range is close
|
||||
if(bufferInfo.len <= jumpThreshold) {
|
||||
// no buffer available @ currentTime, check if next buffer is close (more than 5ms diff but within a 300 ms range)
|
||||
var nextBufferStart = bufferInfo.nextStart, delta = nextBufferStart-currentTime;
|
||||
if(nextBufferStart &&
|
||||
(delta < 0.3) &&
|
||||
(delta > 0.005) &&
|
||||
!media.seeking) {
|
||||
// next buffer is close ! adjust currentTime to nextBufferStart
|
||||
// this will ensure effective video decoding
|
||||
logger.log(`adjust currentTime from ${currentTime} to ${nextBufferStart}`);
|
||||
media.currentTime = nextBufferStart;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue