1
0
Fork 0
mirror of https://github.com/jellyfin/jellyfin-web synced 2025-03-30 19:56:21 +00:00

update components

This commit is contained in:
Luke Pulverenti 2016-06-11 11:55:39 -04:00
parent 84945cabc4
commit ab2d2eaf94
111 changed files with 4302 additions and 3100 deletions

View file

@ -18,7 +18,14 @@ class BufferController extends EventHandler {
Event.BUFFER_APPENDING,
Event.BUFFER_CODECS,
Event.BUFFER_EOS,
Event.BUFFER_FLUSHING);
Event.BUFFER_FLUSHING,
Event.LEVEL_UPDATED);
// the value that we have set mediasource.duration to
// (the actual duration may be tweaked slighly by the browser)
this._msDuration = null;
// the value that we want to set mediaSource.duration to
this._levelDuration = null;
// Source Buffer listeners
this.onsbue = this.onSBUpdateEnd.bind(this);
@ -209,6 +216,46 @@ class BufferController extends EventHandler {
this.doFlush();
}
onLevelUpdated(event) {
let details = event.details;
if (details.fragments.length === 0) {
return;
}
this._levelDuration = details.totalduration + details.fragments[0].start;
this.updateMediaElementDuration();
}
// https://github.com/dailymotion/hls.js/issues/355
updateMediaElementDuration() {
if (this._levelDuration === null) {
return;
}
let media = this.media;
let mediaSource = this.mediaSource;
if (!media || !mediaSource || media.readyState === 0 || mediaSource.readyState !== 'open') {
return;
}
for (let type in mediaSource.sourceBuffers) {
if (mediaSource.sourceBuffers[type].updating) {
// can't set duration whilst a buffer is updating
return;
}
}
if (this._msDuration === null) {
// initialise to the value that the media source is reporting
this._msDuration = mediaSource.duration;
}
// this._levelDuration was the last value we set.
// not using mediaSource.duration as the browser may tweak this value
// only update mediasource duration if its value increase, this is to avoid
// flushing already buffered portion when switching between quality level, as they
if (this._levelDuration > this._msDuration) {
logger.log(`Updating mediasource duration to ${this._levelDuration}`);
mediaSource.duration = this._levelDuration;
this._msDuration = this._levelDuration;
}
}
doFlush() {
// loop through all buffer ranges to flush
while(this.flushRange.length) {

View file

@ -16,8 +16,6 @@ class EwmaBandWidthEstimator {
this.defaultEstimate_ = 5e5; // 500kbps
this.minWeight_ = 0.001;
this.minDelayMs_ = 50;
this.fast_ = new EWMA(hls.config.abrEwmaFast);
this.slow_ = new EWMA(hls.config.abrEwmaSlow);
}
sample(durationMs,numBytes) {
@ -26,6 +24,14 @@ class EwmaBandWidthEstimator {
//console.log('instant bw:'+ Math.round(bandwidth));
// we weight sample using loading duration....
var weigth = durationMs / 1000;
// lazy initialization. this allows to take into account config param changes that could happen after Hls instantiation,
// but before first fragment loading. this is useful to A/B tests those params
if(!this.fast_) {
let config = this.hls.config;
this.fast_ = new EWMA(config.abrEwmaFast);
this.slow_ = new EWMA(config.abrEwmaSlow);
}
this.fast_.sample(weigth,bandwidth);
this.slow_.sample(weigth,bandwidth);
}

View file

@ -118,7 +118,7 @@ class StreamController extends EventHandler {
}
doTick() {
var pos, level, levelDetails, hls = this.hls, config = hls.config;
var pos, level, levelDetails, hls = this.hls, config = hls.config, media = this.media, isSeeking = media && media.seeking;
//logger.log(this.state);
switch(this.state) {
case State.ERROR:
@ -145,7 +145,7 @@ class StreamController extends EventHandler {
// start fragment already requested OR start frag prefetch disable
// exit loop
// => if media not attached but start frag prefetch is enabled and start frag not requested yet, we will not exit loop
if (!this.media &&
if (!media &&
(this.startFragRequested || !config.startFragPrefetch)) {
break;
}
@ -154,12 +154,12 @@ class StreamController extends EventHandler {
// ensure 60s of buffer upfront
// if we have not yet loaded any fragment, start loading from start position
if (this.loadedmetadata) {
pos = this.media.currentTime;
pos = media.currentTime;
} else {
pos = this.nextLoadPosition;
}
level = hls.nextLoadLevel;
var bufferInfo = BufferHelper.bufferInfo(this.media,pos,config.maxBufferHole),
var bufferInfo = BufferHelper.bufferInfo(media,pos,config.maxBufferHole),
bufferLen = bufferInfo.len,
bufferEnd = bufferInfo.end,
fragPrevious = this.fragPrevious,
@ -194,7 +194,7 @@ class StreamController extends EventHandler {
// in case of live playlist we need to ensure that requested position is not located before playlist start
if (levelDetails.live) {
// check if requested position is within seekable boundaries :
//logger.log(`start/pos/bufEnd/seeking:${start.toFixed(3)}/${pos.toFixed(3)}/${bufferEnd.toFixed(3)}/${this.media.seeking}`);
//logger.log(`start/pos/bufEnd/seeking:${start.toFixed(3)}/${pos.toFixed(3)}/${bufferEnd.toFixed(3)}/${media.seeking}`);
let maxLatency = config.liveMaxLatencyDuration !== undefined ? config.liveMaxLatencyDuration : config.liveMaxLatencyDurationCount*levelDetails.targetduration;
if (bufferEnd < Math.max(start, end - maxLatency)) {
@ -244,13 +244,13 @@ class StreamController extends EventHandler {
}
}
if (!frag) {
let foundFrag;
let maxFragLookUpTolerance = config.maxFragLookUpTolerance;
if (bufferEnd < end) {
if (bufferEnd > end - maxFragLookUpTolerance) {
// no frag look up tolerance in case bufferEnd close to end, or media seeking
if (bufferEnd > end - maxFragLookUpTolerance || isSeeking) {
maxFragLookUpTolerance = 0;
}
foundFrag = BinarySearch.search(fragments, (candidate) => {
frag = BinarySearch.search(fragments, (candidate) => {
// offset should be within fragment boundary - config.maxFragLookUpTolerance
// this is to cope with situations like
// bufferEnd = 9.991
@ -274,34 +274,31 @@ class StreamController extends EventHandler {
});
} else {
// reach end of playlist
foundFrag = fragments[fragLen-1];
}
if (foundFrag) {
frag = foundFrag;
start = foundFrag.start;
//logger.log('find SN matching with pos:' + bufferEnd + ':' + frag.sn);
if (fragPrevious && frag.level === fragPrevious.level && frag.sn === fragPrevious.sn) {
if (frag.sn < levelDetails.endSN) {
frag = fragments[frag.sn + 1 - levelDetails.startSN];
logger.log(`SN just loaded, load next one: ${frag.sn}`);
} else {
// have we reached end of VOD playlist ?
if (!levelDetails.live) {
// Finalize the media stream
this.hls.trigger(Event.BUFFER_EOS);
// We might be loading the last fragment but actually the media
// is currently processing a seek command and waiting for new data to resume at another point.
// Going to ended state while media is seeking can spawn an infinite buffering broken state.
if (!this.media.seeking) {
this.state = State.ENDED;
}
}
frag = null;
}
}
frag = fragments[fragLen-1];
}
}
if(frag) {
start = frag.start;
//logger.log('find SN matching with pos:' + bufferEnd + ':' + frag.sn);
if (fragPrevious && frag.level === fragPrevious.level && frag.sn === fragPrevious.sn) {
if (frag.sn < levelDetails.endSN) {
frag = fragments[frag.sn + 1 - levelDetails.startSN];
logger.log(`SN just loaded, load next one: ${frag.sn}`);
} else {
// have we reached end of VOD playlist ?
if (!levelDetails.live) {
// Finalize the media stream
this.hls.trigger(Event.BUFFER_EOS);
// We might be loading the last fragment but actually the media
// is currently processing a seek command and waiting for new data to resume at another point.
// Going to ended state while media is seeking can spawn an infinite buffering broken state.
if (!isSeeking) {
this.state = State.ENDED;
}
}
return;
}
}
//logger.log(' loading frag ' + i +',pos/bufEnd:' + pos.toFixed(3) + '/' + bufferEnd.toFixed(3));
if ((frag.decryptdata.uri != null) && (frag.decryptdata.key == null)) {
logger.log(`Loading key for ${frag.sn} of [${levelDetails.startSN} ,${levelDetails.endSN}],level ${level}`);
@ -350,8 +347,6 @@ class StreamController extends EventHandler {
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`);
@ -1024,7 +1019,9 @@ _checkBuffer() {
logger.log(`target seek position:${targetSeekPosition}`);
}
var bufferInfo = BufferHelper.bufferInfo(media,currentTime,0),
expectedPlaying = !(media.paused || media.ended || media.seeking || media.buffered.length === 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
jumpThreshold = 0.4, // tolerance needed as some browsers stalls playback before reaching buffered range end
playheadMoving = currentTime > media.playbackRate*this.lastCurrentTime;
@ -1057,8 +1054,7 @@ _checkBuffer() {
var nextBufferStart = bufferInfo.nextStart, delta = nextBufferStart-currentTime;
if(nextBufferStart &&
(delta < this.config.maxSeekHole) &&
(delta > 0) &&
!media.seeking) {
(delta > 0)) {
// next buffer is close ! adjust currentTime to nextBufferStart
// this will ensure effective video decoding
logger.log(`adjust currentTime from ${media.currentTime} to next buffered @ ${nextBufferStart} + nudge ${this.seekHoleNudgeDuration}`);