mirror of
https://github.com/jellyfin/jellyfin-web
synced 2025-03-30 19:56:21 +00:00
get api libs from bower
This commit is contained in:
parent
def418714f
commit
f36e664503
97 changed files with 16860 additions and 197 deletions
84
dashboard-ui/bower_components/hls.js/src/controller/abr-controller.js
vendored
Normal file
84
dashboard-ui/bower_components/hls.js/src/controller/abr-controller.js
vendored
Normal file
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* simple ABR Controller
|
||||
*/
|
||||
|
||||
import Event from '../events';
|
||||
|
||||
class AbrController {
|
||||
|
||||
constructor(hls) {
|
||||
this.hls = hls;
|
||||
this.lastfetchlevel = 0;
|
||||
this._autoLevelCapping = -1;
|
||||
this._nextAutoLevel = -1;
|
||||
this.onflp = this.onFragmentLoadProgress.bind(this);
|
||||
hls.on(Event.FRAG_LOAD_PROGRESS, this.onflp);
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.hls.off(Event.FRAG_LOAD_PROGRESS, this.onflp);
|
||||
}
|
||||
|
||||
onFragmentLoadProgress(event, data) {
|
||||
var stats = data.stats;
|
||||
if (stats.aborted === undefined) {
|
||||
this.lastfetchduration = (performance.now() - stats.trequest) / 1000;
|
||||
this.lastfetchlevel = data.frag.level;
|
||||
this.lastbw = (stats.loaded * 8) / this.lastfetchduration;
|
||||
//console.log(`fetchDuration:${this.lastfetchduration},bw:${(this.lastbw/1000).toFixed(0)}/${stats.aborted}`);
|
||||
}
|
||||
}
|
||||
|
||||
/** Return the capping/max level value that could be used by automatic level selection algorithm **/
|
||||
get autoLevelCapping() {
|
||||
return this._autoLevelCapping;
|
||||
}
|
||||
|
||||
/** set the capping/max level value that could be used by automatic level selection algorithm **/
|
||||
set autoLevelCapping(newLevel) {
|
||||
this._autoLevelCapping = newLevel;
|
||||
}
|
||||
|
||||
get nextAutoLevel() {
|
||||
var lastbw = this.lastbw, hls = this.hls,adjustedbw, i, maxAutoLevel;
|
||||
if (this._autoLevelCapping === -1) {
|
||||
maxAutoLevel = hls.levels.length - 1;
|
||||
} else {
|
||||
maxAutoLevel = this._autoLevelCapping;
|
||||
}
|
||||
|
||||
if (this._nextAutoLevel !== -1) {
|
||||
var nextLevel = Math.min(this._nextAutoLevel,maxAutoLevel);
|
||||
if (nextLevel === this.lastfetchlevel) {
|
||||
this._nextAutoLevel = -1;
|
||||
} else {
|
||||
return nextLevel;
|
||||
}
|
||||
}
|
||||
|
||||
// follow algorithm captured from stagefright :
|
||||
// https://android.googlesource.com/platform/frameworks/av/+/master/media/libstagefright/httplive/LiveSession.cpp
|
||||
// Pick the highest bandwidth stream below or equal to estimated bandwidth.
|
||||
for (i = 0; i <= maxAutoLevel; i++) {
|
||||
// consider only 80% of the available bandwidth, but if we are switching up,
|
||||
// be even more conservative (70%) to avoid overestimating and immediately
|
||||
// switching back.
|
||||
if (i <= this.lastfetchlevel) {
|
||||
adjustedbw = 0.8 * lastbw;
|
||||
} else {
|
||||
adjustedbw = 0.7 * lastbw;
|
||||
}
|
||||
if (adjustedbw < hls.levels[i].bitrate) {
|
||||
return Math.max(0, i - 1);
|
||||
}
|
||||
}
|
||||
return i - 1;
|
||||
}
|
||||
|
||||
set nextAutoLevel(nextLevel) {
|
||||
this._nextAutoLevel = nextLevel;
|
||||
}
|
||||
}
|
||||
|
||||
export default AbrController;
|
||||
|
49
dashboard-ui/bower_components/hls.js/src/controller/fps-controller.js
vendored
Normal file
49
dashboard-ui/bower_components/hls.js/src/controller/fps-controller.js
vendored
Normal file
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* FPS Controller
|
||||
*/
|
||||
|
||||
import Event from '../events';
|
||||
import {logger} from '../utils/logger';
|
||||
|
||||
class FPSController {
|
||||
|
||||
constructor(hls) {
|
||||
this.hls = hls;
|
||||
this.timer = setInterval(this.checkFPS, hls.config.fpsDroppedMonitoringPeriod);
|
||||
}
|
||||
|
||||
destroy() {
|
||||
if (this.timer) {
|
||||
clearInterval(this.timer);
|
||||
}
|
||||
}
|
||||
|
||||
checkFPS() {
|
||||
var v = this.hls.video;
|
||||
if (v) {
|
||||
var decodedFrames = v.webkitDecodedFrameCount, droppedFrames = v.webkitDroppedFrameCount, currentTime = new Date();
|
||||
if (decodedFrames) {
|
||||
if (this.lastTime) {
|
||||
var currentPeriod = currentTime - this.lastTime;
|
||||
var currentDropped = droppedFrames - this.lastDroppedFrames;
|
||||
var currentDecoded = decodedFrames - this.lastDecodedFrames;
|
||||
var decodedFPS = 1000 * currentDecoded / currentPeriod;
|
||||
var droppedFPS = 1000 * currentDropped / currentPeriod;
|
||||
if (droppedFPS > 0) {
|
||||
logger.log(`checkFPS : droppedFPS/decodedFPS:${droppedFPS.toFixed(1)}/${decodedFPS.toFixed(1)}`);
|
||||
if (currentDropped > this.hls.config.fpsDroppedMonitoringThreshold * currentDecoded) {
|
||||
logger.warn('drop FPS ratio greater than max allowed value');
|
||||
this.hls.trigger(Event.FPS_DROP, {currentDropped: currentDropped, currentDecoded: currentDecoded, totalDroppedFrames: droppedFrames});
|
||||
}
|
||||
}
|
||||
}
|
||||
this.lastTime = currentTime;
|
||||
this.lastDroppedFrames = droppedFrames;
|
||||
this.lastDecodedFrames = decodedFrames;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default FPSController;
|
||||
|
254
dashboard-ui/bower_components/hls.js/src/controller/level-controller.js
vendored
Normal file
254
dashboard-ui/bower_components/hls.js/src/controller/level-controller.js
vendored
Normal file
|
@ -0,0 +1,254 @@
|
|||
/*
|
||||
* Level Controller
|
||||
*/
|
||||
|
||||
import Event from '../events';
|
||||
import {logger} from '../utils/logger';
|
||||
import {ErrorTypes, ErrorDetails} from '../errors';
|
||||
|
||||
class LevelController {
|
||||
|
||||
constructor(hls) {
|
||||
this.hls = hls;
|
||||
this.onml = this.onManifestLoaded.bind(this);
|
||||
this.onll = this.onLevelLoaded.bind(this);
|
||||
this.onerr = this.onError.bind(this);
|
||||
this.ontick = this.tick.bind(this);
|
||||
hls.on(Event.MANIFEST_LOADED, this.onml);
|
||||
hls.on(Event.LEVEL_LOADED, this.onll);
|
||||
hls.on(Event.ERROR, this.onerr);
|
||||
this._manualLevel = this._autoLevelCapping = -1;
|
||||
}
|
||||
|
||||
destroy() {
|
||||
var hls = this.hls;
|
||||
hls.off(Event.MANIFEST_LOADED, this.onml);
|
||||
hls.off(Event.LEVEL_LOADED, this.onll);
|
||||
hls.off(Event.ERROR, this.onerr);
|
||||
if (this.timer) {
|
||||
clearInterval(this.timer);
|
||||
}
|
||||
this._manualLevel = -1;
|
||||
}
|
||||
|
||||
onManifestLoaded(event, data) {
|
||||
var levels0 = [], levels = [], bitrateStart, i, bitrateSet = {}, videoCodecFound = false, audioCodecFound = false;
|
||||
|
||||
// regroup redundant level together
|
||||
data.levels.forEach(level => {
|
||||
if(level.videoCodec) {
|
||||
videoCodecFound = true;
|
||||
}
|
||||
if(level.audioCodec) {
|
||||
audioCodecFound = true;
|
||||
}
|
||||
var redundantLevelId = bitrateSet[level.bitrate];
|
||||
if (redundantLevelId === undefined) {
|
||||
bitrateSet[level.bitrate] = levels0.length;
|
||||
level.url = [level.url];
|
||||
level.urlId = 0;
|
||||
levels0.push(level);
|
||||
} else {
|
||||
levels0[redundantLevelId].url.push(level.url);
|
||||
}
|
||||
});
|
||||
|
||||
// remove audio-only level if we also have levels with audio+video codecs signalled
|
||||
if(videoCodecFound && audioCodecFound) {
|
||||
levels0.forEach(level => {
|
||||
if(level.videoCodec) {
|
||||
levels.push(level);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
levels = levels0;
|
||||
}
|
||||
|
||||
// only keep level with supported audio/video codecs
|
||||
levels0 = levels0.filter(function(level) {
|
||||
var checkSupported = function(codec) { return MediaSource.isTypeSupported(`video/mp4;codecs=${codec}`);};
|
||||
var audioCodec = level.audioCodec, videoCodec = level.videoCodec;
|
||||
|
||||
return ((audioCodec && checkSupported(audioCodec)) || !audioCodec) &&
|
||||
((videoCodec && checkSupported(videoCodec)) || !videoCodec);
|
||||
|
||||
});
|
||||
|
||||
// start bitrate is the first bitrate of the manifest
|
||||
bitrateStart = levels[0].bitrate;
|
||||
// sort level on bitrate
|
||||
levels.sort(function (a, b) {
|
||||
return a.bitrate - b.bitrate;
|
||||
});
|
||||
this._levels = levels;
|
||||
// find index of first level in sorted levels
|
||||
for (i = 0; i < levels.length; i++) {
|
||||
if (levels[i].bitrate === bitrateStart) {
|
||||
this._firstLevel = i;
|
||||
logger.log(`manifest loaded,${levels.length} level(s) found, first bitrate:${bitrateStart}`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.hls.trigger(Event.MANIFEST_PARSED, {levels: this._levels, firstLevel: this._firstLevel, stats: data.stats});
|
||||
return;
|
||||
}
|
||||
|
||||
get levels() {
|
||||
return this._levels;
|
||||
}
|
||||
|
||||
get level() {
|
||||
return this._level;
|
||||
}
|
||||
|
||||
set level(newLevel) {
|
||||
if (this._level !== newLevel || this._levels[newLevel].details === undefined) {
|
||||
this.setLevelInternal(newLevel);
|
||||
}
|
||||
}
|
||||
|
||||
setLevelInternal(newLevel) {
|
||||
// check if level idx is valid
|
||||
if (newLevel >= 0 && newLevel < this._levels.length) {
|
||||
// stopping live reloading timer if any
|
||||
if (this.timer) {
|
||||
clearInterval(this.timer);
|
||||
this.timer = null;
|
||||
}
|
||||
this._level = newLevel;
|
||||
logger.log(`switching to level ${newLevel}`);
|
||||
this.hls.trigger(Event.LEVEL_SWITCH, {level: newLevel});
|
||||
var level = this._levels[newLevel];
|
||||
// check if we need to load playlist for this level
|
||||
if (level.details === undefined || level.details.live === true) {
|
||||
// level not retrieved yet, or live playlist we need to (re)load it
|
||||
logger.log(`(re)loading playlist for level ${newLevel}`);
|
||||
var urlId = level.urlId;
|
||||
this.hls.trigger(Event.LEVEL_LOADING, {url: level.url[urlId], level: newLevel, id: urlId});
|
||||
}
|
||||
} else {
|
||||
// invalid level id given, trigger error
|
||||
this.hls.trigger(Event.ERROR, {type : ErrorTypes.OTHER_ERROR, details: ErrorDetails.LEVEL_SWITCH_ERROR, level: newLevel, fatal: false, reason: 'invalid level idx'});
|
||||
}
|
||||
}
|
||||
|
||||
get manualLevel() {
|
||||
return this._manualLevel;
|
||||
}
|
||||
|
||||
set manualLevel(newLevel) {
|
||||
this._manualLevel = newLevel;
|
||||
if (newLevel !== -1) {
|
||||
this.level = newLevel;
|
||||
}
|
||||
}
|
||||
|
||||
get firstLevel() {
|
||||
return this._firstLevel;
|
||||
}
|
||||
|
||||
set firstLevel(newLevel) {
|
||||
this._firstLevel = newLevel;
|
||||
}
|
||||
|
||||
get startLevel() {
|
||||
if (this._startLevel === undefined) {
|
||||
return this._firstLevel;
|
||||
} else {
|
||||
return this._startLevel;
|
||||
}
|
||||
}
|
||||
|
||||
set startLevel(newLevel) {
|
||||
this._startLevel = newLevel;
|
||||
}
|
||||
|
||||
onError(event, data) {
|
||||
if(data.fatal) {
|
||||
return;
|
||||
}
|
||||
|
||||
var details = data.details, hls = this.hls, levelId, level;
|
||||
// try to recover not fatal errors
|
||||
switch(details) {
|
||||
case ErrorDetails.FRAG_LOAD_ERROR:
|
||||
case ErrorDetails.FRAG_LOAD_TIMEOUT:
|
||||
case ErrorDetails.FRAG_LOOP_LOADING_ERROR:
|
||||
case ErrorDetails.KEY_LOAD_ERROR:
|
||||
case ErrorDetails.KEY_LOAD_TIMEOUT:
|
||||
levelId = data.frag.level;
|
||||
break;
|
||||
case ErrorDetails.LEVEL_LOAD_ERROR:
|
||||
case ErrorDetails.LEVEL_LOAD_TIMEOUT:
|
||||
levelId = data.level;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
/* try to switch to a redundant stream if any available.
|
||||
* if no redundant stream available, emergency switch down (if in auto mode and current level not 0)
|
||||
* otherwise, we cannot recover this network error ....
|
||||
*/
|
||||
if (levelId !== undefined) {
|
||||
level = this._levels[levelId];
|
||||
if (level.urlId < (level.url.length - 1)) {
|
||||
level.urlId++;
|
||||
level.details = undefined;
|
||||
logger.warn(`level controller,${details} for level ${levelId}: switching to redundant stream id ${level.urlId}`);
|
||||
} else {
|
||||
// we could try to recover if in auto mode and current level not lowest level (0)
|
||||
let recoverable = ((this._manualLevel === -1) && levelId);
|
||||
if (recoverable) {
|
||||
logger.warn(`level controller,${details}: emergency switch-down for next fragment`);
|
||||
hls.abrController.nextAutoLevel = 0;
|
||||
} else if(level && level.details && level.details.live) {
|
||||
logger.warn(`level controller,${details} on live stream, discard`);
|
||||
} else {
|
||||
logger.error(`cannot recover ${details} error`);
|
||||
this._level = undefined;
|
||||
// stopping live reloading timer if any
|
||||
if (this.timer) {
|
||||
clearInterval(this.timer);
|
||||
this.timer = null;
|
||||
}
|
||||
// redispatch same error but with fatal set to true
|
||||
data.fatal = true;
|
||||
hls.trigger(event, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onLevelLoaded(event, data) {
|
||||
// check if current playlist is a live playlist
|
||||
if (data.details.live && !this.timer) {
|
||||
// if live playlist we will have to reload it periodically
|
||||
// set reload period to playlist target duration
|
||||
this.timer = setInterval(this.ontick, 1000 * data.details.targetduration);
|
||||
}
|
||||
if (!data.details.live && this.timer) {
|
||||
// playlist is not live and timer is armed : stopping it
|
||||
clearInterval(this.timer);
|
||||
this.timer = null;
|
||||
}
|
||||
}
|
||||
|
||||
tick() {
|
||||
var levelId = this._level;
|
||||
if (levelId !== undefined) {
|
||||
var level = this._levels[levelId], urlId = level.urlId;
|
||||
this.hls.trigger(Event.LEVEL_LOADING, {url: level.url[urlId], level: levelId, id: urlId});
|
||||
}
|
||||
}
|
||||
|
||||
nextLoadLevel() {
|
||||
if (this._manualLevel !== -1) {
|
||||
return this._manualLevel;
|
||||
} else {
|
||||
return this.hls.abrController.nextAutoLevel;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default LevelController;
|
||||
|
1216
dashboard-ui/bower_components/hls.js/src/controller/mse-media-controller.js
vendored
Normal file
1216
dashboard-ui/bower_components/hls.js/src/controller/mse-media-controller.js
vendored
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue