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-03-16 13:43:01 -04:00
parent 49ef08c64b
commit 07de1c52f9
11 changed files with 213 additions and 95 deletions

View file

@ -1,6 +1,6 @@
{ {
"name": "hls.js", "name": "hls.js",
"version": "0.5.11", "version": "0.5.13",
"license": "Apache-2.0", "license": "Apache-2.0",
"description": "Media Source Extension - HLS library, by/for Dailymotion", "description": "Media Source Extension - HLS library, by/for Dailymotion",
"homepage": "https://github.com/dailymotion/hls.js", "homepage": "https://github.com/dailymotion/hls.js",
@ -16,11 +16,11 @@
"test", "test",
"tests" "tests"
], ],
"_release": "0.5.11", "_release": "0.5.13",
"_resolution": { "_resolution": {
"type": "version", "type": "version",
"tag": "v0.5.11", "tag": "v0.5.13",
"commit": "8896e0062b3f8384379c7b990424e3fab9c1c41b" "commit": "ef9fae497dd6d6e80a6d471c55eef2310e883853"
}, },
"_source": "git://github.com/dailymotion/hls.js.git", "_source": "git://github.com/dailymotion/hls.js.git",
"_target": "~0.5.7", "_target": "~0.5.7",

View file

@ -190,6 +190,7 @@ configuration parameters could be provided to hls.js upon instantiation of Hls O
maxBufferSize : 60*1000*1000, maxBufferSize : 60*1000*1000,
maxBufferHole : 0.3, maxBufferHole : 0.3,
maxSeekHole : 2, maxSeekHole : 2,
maxFragLookUpTolerance : 0.2,
liveSyncDurationCount : 3, liveSyncDurationCount : 3,
liveMaxLatencyDurationCount: 10, liveMaxLatencyDurationCount: 10,
enableWorker : true, enableWorker : true,
@ -268,6 +269,25 @@ in case playback is stalled, and a buffered range is available upfront, less tha
hls.js will jump over this buffer hole to reach the beginning of this following buffered range. hls.js will jump over this buffer hole to reach the beginning of this following buffered range.
```maxSeekHole``` allows to configure this jumpable threshold. ```maxSeekHole``` allows to configure this jumpable threshold.
#### ```maxFragLookUpTolerance```
(default 0.2s)
this tolerance factor is used during fragment lookup.
instead of checking whether buffered.end is located within [start, end] range, frag lookup will be done by checking within [start-maxFragLookUpTolerance, end-maxFragLookUpTolerance] range
this tolerance factor is used to cope with situations like
buffered.end = 9.991
frag[Ø] : [0,10]
frag[1] : [10,20]
=> buffered.end is within frag[0] range, but as we are close to frag[1], frag[1] should be choosen instead
if maxFragLookUpTolerance=0.2,
this lookup will be adjusted to
frag[Ø] : [-0.2,9.8]
frag[1] : [9.8,19.8]
=> this time, buffered.end is within frag[1] range, and frag[1] will be the next fragment to be loaded, as expected.
#### ```maxMaxBufferLength``` #### ```maxMaxBufferLength```
(default 600s) (default 600s)
@ -495,6 +515,12 @@ get : return last loaded fragment quality level.
set : set quality level for next loaded fragment set : set quality level for next loaded fragment
set to -1 for automatic level selection set to -1 for automatic level selection
#### ```hls.nextLoadLevel```
get : return quality level that will be used to load next fragment
set : force quality level for next loaded fragment. quality level will be forced only for that fragment.
after a fragment at this quality level has been loaded, ```hls.loadLevel``` will prevail.
#### ```hls.firstLevel``` #### ```hls.firstLevel```
get : first level index (index of first level appearing in Manifest. it is usually defined as start level hint for player) get : first level index (index of first level appearing in Manifest. it is usually defined as start level hint for player)

View file

@ -1,6 +1,6 @@
{ {
"name": "hls.js", "name": "hls.js",
"version": "0.5.11", "version": "0.5.13",
"license": "Apache-2.0", "license": "Apache-2.0",
"description": "Media Source Extension - HLS library, by/for Dailymotion", "description": "Media Source Extension - HLS library, by/for Dailymotion",
"homepage": "https://github.com/dailymotion/hls.js", "homepage": "https://github.com/dailymotion/hls.js",

View file

@ -1125,15 +1125,6 @@ var LevelController = function (_EventHandler) {
this.hls.trigger(_events2.default.LEVEL_LOADING, { url: level.url[urlId], level: levelId, id: urlId }); this.hls.trigger(_events2.default.LEVEL_LOADING, { url: level.url[urlId], level: levelId, id: urlId });
} }
} }
}, {
key: 'nextLoadLevel',
value: function nextLoadLevel() {
if (this._manualLevel !== -1) {
return this._manualLevel;
} else {
return this.hls.abrController.nextAutoLevel;
}
}
}, { }, {
key: 'levels', key: 'levels',
get: function get() { get: function get() {
@ -1180,6 +1171,21 @@ var LevelController = function (_EventHandler) {
set: function set(newLevel) { set: function set(newLevel) {
this._startLevel = newLevel; this._startLevel = newLevel;
} }
}, {
key: 'nextLoadLevel',
get: function get() {
if (this._manualLevel !== -1) {
return this._manualLevel;
} else {
return this.hls.abrController.nextAutoLevel;
}
},
set: function set(nextLevel) {
this.level = nextLevel;
if (this._manualLevel === -1) {
this.hls.abrController.nextAutoLevel = nextLevel;
}
}
}]); }]);
return LevelController; return LevelController;
@ -1333,6 +1339,8 @@ var StreamController = function (_EventHandler) {
}, { }, {
key: 'doTick', key: 'doTick',
value: function doTick() { value: function doTick() {
var _this2 = this;
var pos, var pos,
level, level,
levelDetails, levelDetails,
@ -1453,14 +1461,30 @@ var StreamController = function (_EventHandler) {
} }
} }
if (!_frag) { if (!_frag) {
var foundFrag; (function () {
var foundFrag = undefined;
var maxFragLookUpTolerance = config.maxFragLookUpTolerance;
if (bufferEnd < end) { if (bufferEnd < end) {
if (bufferEnd > end - maxFragLookUpTolerance) {
maxFragLookUpTolerance = 0;
}
foundFrag = _binarySearch2.default.search(fragments, function (candidate) { foundFrag = _binarySearch2.default.search(fragments, function (candidate) {
// offset should be within fragment boundary - config.maxFragLookUpTolerance
// this is to cope with situations like
// bufferEnd = 9.991
// frag[Ø] : [0,10]
// frag[1] : [10,20]
// bufferEnd is within frag[0] range ... although what we are expecting is to return frag[1] here
// frag start frag start+duration
// |-----------------------------|
// <---> <--->
// ...--------><-----------------------------><---------....
// previous frag matching fragment next frag
// return -1 return 0 return 1
//logger.log(`level/sn/start/end/bufEnd:${level}/${candidate.sn}/${candidate.start}/${(candidate.start+candidate.duration)}/${bufferEnd}`); //logger.log(`level/sn/start/end/bufEnd:${level}/${candidate.sn}/${candidate.start}/${(candidate.start+candidate.duration)}/${bufferEnd}`);
// offset should be within fragment boundary if (candidate.start + candidate.duration - maxFragLookUpTolerance <= bufferEnd) {
if (candidate.start + candidate.duration <= bufferEnd) {
return 1; return 1;
} else if (candidate.start > bufferEnd) { } else if (candidate.start - maxFragLookUpTolerance > bufferEnd) {
return -1; return -1;
} }
return 0; return 0;
@ -1480,13 +1504,14 @@ var StreamController = function (_EventHandler) {
} else { } else {
// have we reached end of VOD playlist ? // have we reached end of VOD playlist ?
if (!levelDetails.live) { if (!levelDetails.live) {
this.hls.trigger(_events2.default.BUFFER_EOS); _this2.hls.trigger(_events2.default.BUFFER_EOS);
this.state = State.ENDED; _this2.state = State.ENDED;
} }
_frag = null; _frag = null;
} }
} }
} }
})();
} }
if (_frag) { if (_frag) {
//logger.log(' loading frag ' + i +',pos/bufEnd:' + pos.toFixed(3) + '/' + bufferEnd.toFixed(3)); //logger.log(' loading frag ' + i +',pos/bufEnd:' + pos.toFixed(3) + '/' + bufferEnd.toFixed(3));
@ -1548,28 +1573,50 @@ var StreamController = function (_EventHandler) {
var requestDelay = performance.now() - frag.trequest; var requestDelay = performance.now() - frag.trequest;
// monitor fragment load progress after half of expected fragment duration,to stabilize bitrate // monitor fragment load progress after half of expected fragment duration,to stabilize bitrate
if (requestDelay > 500 * frag.duration) { if (requestDelay > 500 * frag.duration) {
var loadRate = frag.loaded * 1000 / requestDelay; // byte/s var 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) { if (frag.expectedLen < frag.loaded) {
frag.expectedLen = frag.loaded; frag.expectedLen = frag.loaded;
} }
pos = v.currentTime; pos = v.currentTime;
var fragLoadedDelay = (frag.expectedLen - frag.loaded) / loadRate; var fragLoadedDelay = (frag.expectedLen - frag.loaded) / loadRate;
var bufferStarvationDelay = this.bufferInfo(pos, config.maxBufferHole).end - pos; var bufferStarvationDelay = this.bufferInfo(pos, config.maxBufferHole).end - pos;
var fragLevelNextLoadedDelay = frag.duration * this.levels[hls.nextLoadLevel].bitrate / (8 * loadRate); //bps/Bps // consider emergency switch down only if we have less than 2 frag buffered AND
/* if we have less than 2 frag duration in buffer and if frag loaded delay is greater than buffer starvation delay // time to finish loading current fragment is bigger than buffer starvation delay
... and also bigger than duration needed to load fragment at next level ...*/ // ie if we risk buffer starvation if bw does not increase quickly
if (bufferStarvationDelay < 2 * frag.duration && fragLoadedDelay > bufferStarvationDelay && fragLoadedDelay > fragLevelNextLoadedDelay) { if (bufferStarvationDelay < 2 * frag.duration && fragLoadedDelay > bufferStarvationDelay) {
var fragLevelNextLoadedDelay = undefined,
nextLoadLevel = undefined;
// lets iterate through lower level and try to find the biggest one that could avoid rebuffering
// we start from current level - 1 and we step down , until we find a matching level
for (nextLoadLevel = this.level - 1; nextLoadLevel >= 0; nextLoadLevel--) {
// 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 * this.levels[nextLoadLevel].bitrate / (8 * 0.8 * loadRate);
_logger.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 !
break;
}
}
// only emergency switch down if it takes less time to load new fragment at lowest level instead
// of finishing loading current one ...
if (fragLevelNextLoadedDelay < fragLoadedDelay) {
// ensure nextLoadLevel is not negative
nextLoadLevel = Math.max(0, nextLoadLevel);
// force next load level in auto mode
hls.nextLoadLevel = nextLoadLevel;
// abort fragment loading ... // abort fragment loading ...
_logger.logger.warn('loading too slow, abort fragment loading'); _logger.logger.warn('loading too slow, abort fragment loading and switch to level ' + nextLoadLevel);
_logger.logger.log('fragLoadedDelay/bufferStarvationDelay/fragLevelNextLoadedDelay :' + fragLoadedDelay.toFixed(1) + '/' + bufferStarvationDelay.toFixed(1) + '/' + fragLevelNextLoadedDelay.toFixed(1));
//abort fragment loading //abort fragment loading
frag.loader.abort(); frag.loader.abort();
hls.trigger(_events2.default.FRAG_LOAD_EMERGENCY_ABORTED, { frag: frag }); hls.trigger(_events2.default.FRAG_LOAD_EMERGENCY_ABORTED, { frag: frag });
// switch back to IDLE state to request new fragment at lowest level // switch back to IDLE state to request new fragment at lower level
this.state = State.IDLE; this.state = State.IDLE;
} }
} }
} }
}
break; break;
case State.FRAG_LOADING_WAITING_RETRY: case State.FRAG_LOADING_WAITING_RETRY:
var now = performance.now(); var now = performance.now();
@ -2151,7 +2198,7 @@ var StreamController = function (_EventHandler) {
}, { }, {
key: 'onFragParsingData', key: 'onFragParsingData',
value: function onFragParsingData(data) { value: function onFragParsingData(data) {
var _this2 = this; var _this3 = this;
if (this.state === State.PARSING) { if (this.state === State.PARSING) {
this.tparse2 = Date.now(); this.tparse2 = Date.now();
@ -2166,7 +2213,7 @@ var StreamController = function (_EventHandler) {
[data.data1, data.data2].forEach(function (buffer) { [data.data1, data.data2].forEach(function (buffer) {
if (buffer) { if (buffer) {
_this2.pendingAppending++; _this3.pendingAppending++;
hls.trigger(_events2.default.BUFFER_APPENDING, { type: data.type, data: buffer }); hls.trigger(_events2.default.BUFFER_APPENDING, { type: data.type, data: buffer });
} }
}); });
@ -5356,6 +5403,7 @@ var Hls = function () {
maxBufferSize: 60 * 1000 * 1000, maxBufferSize: 60 * 1000 * 1000,
maxBufferHole: 0.5, maxBufferHole: 0.5,
maxSeekHole: 2, maxSeekHole: 2,
maxFragLookUpTolerance: 0.2,
liveSyncDurationCount: 3, liveSyncDurationCount: 3,
liveMaxLatencyDurationCount: Infinity, liveMaxLatencyDurationCount: Infinity,
liveSyncDuration: undefined, liveSyncDuration: undefined,
@ -5574,13 +5622,13 @@ var Hls = function () {
}, { }, {
key: 'nextLoadLevel', key: 'nextLoadLevel',
get: function get() { get: function get() {
return this.levelController.nextLoadLevel(); return this.levelController.nextLoadLevel;
} }
/** set quality level of next loaded fragment **/ /** set quality level of next loaded fragment **/
, ,
set: function set(level) { set: function set(level) {
this.levelController.level = level; this.levelController.nextLoadLevel = level;
} }
/** Return first level (index of first level referenced in manifest) /** Return first level (index of first level referenced in manifest)

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1,6 +1,6 @@
{ {
"name": "hls.js", "name": "hls.js",
"version": "0.5.11", "version": "0.5.13",
"license": "Apache-2.0", "license": "Apache-2.0",
"description": "Media Source Extension - HLS library, by/for Dailymotion", "description": "Media Source Extension - HLS library, by/for Dailymotion",
"homepage": "https://github.com/dailymotion/hls.js", "homepage": "https://github.com/dailymotion/hls.js",

View file

@ -241,13 +241,20 @@ class LevelController extends EventHandler {
} }
} }
nextLoadLevel() { get nextLoadLevel() {
if (this._manualLevel !== -1) { if (this._manualLevel !== -1) {
return this._manualLevel; return this._manualLevel;
} else { } else {
return this.hls.abrController.nextAutoLevel; return this.hls.abrController.nextAutoLevel;
} }
} }
set nextLoadLevel(nextLevel) {
this.level = nextLevel;
if (this._manualLevel === -1) {
this.hls.abrController.nextAutoLevel = nextLevel;
}
}
} }
export default LevelController; export default LevelController;

View file

@ -231,15 +231,30 @@ class StreamController extends EventHandler {
} }
} }
if (!frag) { if (!frag) {
var foundFrag; let foundFrag;
let maxFragLookUpTolerance = config.maxFragLookUpTolerance;
if (bufferEnd < end) { if (bufferEnd < end) {
if (bufferEnd > end - maxFragLookUpTolerance) {
maxFragLookUpTolerance = 0;
}
foundFrag = BinarySearch.search(fragments, (candidate) => { foundFrag = BinarySearch.search(fragments, (candidate) => {
// offset should be within fragment boundary - config.maxFragLookUpTolerance
// this is to cope with situations like
// bufferEnd = 9.991
// frag[Ø] : [0,10]
// frag[1] : [10,20]
// bufferEnd is within frag[0] range ... although what we are expecting is to return frag[1] here
// frag start frag start+duration
// |-----------------------------|
// <---> <--->
// ...--------><-----------------------------><---------....
// previous frag matching fragment next frag
// return -1 return 0 return 1
//logger.log(`level/sn/start/end/bufEnd:${level}/${candidate.sn}/${candidate.start}/${(candidate.start+candidate.duration)}/${bufferEnd}`); //logger.log(`level/sn/start/end/bufEnd:${level}/${candidate.sn}/${candidate.start}/${(candidate.start+candidate.duration)}/${bufferEnd}`);
// offset should be within fragment boundary if ((candidate.start + candidate.duration - maxFragLookUpTolerance) <= bufferEnd) {
if ((candidate.start + candidate.duration) <= bufferEnd) {
return 1; return 1;
} }
else if (candidate.start > bufferEnd) { else if (candidate.start - maxFragLookUpTolerance > bufferEnd) {
return -1; return -1;
} }
return 0; return 0;
@ -323,31 +338,52 @@ class StreamController extends EventHandler {
/* only monitor frag retrieval time if /* only monitor frag retrieval time if
(video not paused OR first fragment being loaded) AND autoswitching enabled AND not lowest level AND multiple levels */ (video not paused OR first fragment being loaded) AND autoswitching enabled AND not lowest level AND multiple levels */
if (v && (!v.paused || this.loadedmetadata === false) && frag.autoLevel && this.level && this.levels.length > 1) { if (v && (!v.paused || this.loadedmetadata === false) && frag.autoLevel && this.level && this.levels.length > 1) {
var requestDelay = performance.now() - frag.trequest; let requestDelay = performance.now() - frag.trequest;
// monitor fragment load progress after half of expected fragment duration,to stabilize bitrate // monitor fragment load progress after half of expected fragment duration,to stabilize bitrate
if (requestDelay > (500 * frag.duration)) { if (requestDelay > (500 * frag.duration)) {
var loadRate = frag.loaded * 1000 / requestDelay; // byte/s 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) { if (frag.expectedLen < frag.loaded) {
frag.expectedLen = frag.loaded; frag.expectedLen = frag.loaded;
} }
pos = v.currentTime; pos = v.currentTime;
var fragLoadedDelay = (frag.expectedLen - frag.loaded) / loadRate; let fragLoadedDelay = (frag.expectedLen - frag.loaded) / loadRate;
var bufferStarvationDelay = this.bufferInfo(pos,config.maxBufferHole).end - pos; let bufferStarvationDelay = this.bufferInfo(pos,config.maxBufferHole).end - pos;
var fragLevelNextLoadedDelay = frag.duration * this.levels[hls.nextLoadLevel].bitrate / (8 * loadRate); //bps/Bps // consider emergency switch down only if we have less than 2 frag buffered AND
/* if we have less than 2 frag duration in buffer and if frag loaded delay is greater than buffer starvation delay // time to finish loading current fragment is bigger than buffer starvation delay
... and also bigger than duration needed to load fragment at next level ...*/ // ie if we risk buffer starvation if bw does not increase quickly
if (bufferStarvationDelay < (2 * frag.duration) && fragLoadedDelay > bufferStarvationDelay && fragLoadedDelay > fragLevelNextLoadedDelay) { if (bufferStarvationDelay < 2*frag.duration && fragLoadedDelay > bufferStarvationDelay) {
let fragLevelNextLoadedDelay, nextLoadLevel;
// lets iterate through lower level and try to find the biggest one that could avoid rebuffering
// we start from current level - 1 and we step down , until we find a matching level
for (nextLoadLevel = this.level - 1 ; nextLoadLevel >=0 ; nextLoadLevel--) {
// 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 * this.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 !
break;
}
}
// only emergency switch down if it takes less time to load new fragment at lowest level instead
// of finishing loading current one ...
if (fragLevelNextLoadedDelay < fragLoadedDelay) {
// ensure nextLoadLevel is not negative
nextLoadLevel = Math.max(0,nextLoadLevel);
// force next load level in auto mode
hls.nextLoadLevel = nextLoadLevel;
// abort fragment loading ... // abort fragment loading ...
logger.warn('loading too slow, abort fragment loading'); logger.warn(`loading too slow, abort fragment loading and switch to level ${nextLoadLevel}`);
logger.log(`fragLoadedDelay/bufferStarvationDelay/fragLevelNextLoadedDelay :${fragLoadedDelay.toFixed(1)}/${bufferStarvationDelay.toFixed(1)}/${fragLevelNextLoadedDelay.toFixed(1)}`);
//abort fragment loading //abort fragment loading
frag.loader.abort(); frag.loader.abort();
hls.trigger(Event.FRAG_LOAD_EMERGENCY_ABORTED, {frag: frag}); hls.trigger(Event.FRAG_LOAD_EMERGENCY_ABORTED, {frag: frag});
// switch back to IDLE state to request new fragment at lowest level // switch back to IDLE state to request new fragment at lower level
this.state = State.IDLE; this.state = State.IDLE;
} }
} }
} }
}
break; break;
case State.FRAG_LOADING_WAITING_RETRY: case State.FRAG_LOADING_WAITING_RETRY:
var now = performance.now(); var now = performance.now();

View file

@ -45,6 +45,7 @@ class Hls {
maxBufferSize: 60 * 1000 * 1000, maxBufferSize: 60 * 1000 * 1000,
maxBufferHole: 0.5, maxBufferHole: 0.5,
maxSeekHole: 2, maxSeekHole: 2,
maxFragLookUpTolerance : 0.2,
liveSyncDurationCount:3, liveSyncDurationCount:3,
liveMaxLatencyDurationCount: Infinity, liveMaxLatencyDurationCount: Infinity,
liveSyncDuration: undefined, liveSyncDuration: undefined,
@ -223,12 +224,12 @@ class Hls {
/** Return the quality level of next loaded fragment **/ /** Return the quality level of next loaded fragment **/
get nextLoadLevel() { get nextLoadLevel() {
return this.levelController.nextLoadLevel(); return this.levelController.nextLoadLevel;
} }
/** set quality level of next loaded fragment **/ /** set quality level of next loaded fragment **/
set nextLoadLevel(level) { set nextLoadLevel(level) {
this.levelController.level = level; this.levelController.nextLoadLevel = level;
} }
/** Return first level (index of first level referenced in manifest) /** Return first level (index of first level referenced in manifest)

View file

@ -36,7 +36,7 @@
"tag": "v1.3.0", "tag": "v1.3.0",
"commit": "1662093611cda3fd29125cdab94a61d3d88093da" "commit": "1662093611cda3fd29125cdab94a61d3d88093da"
}, },
"_source": "git://github.com/polymerelements/iron-selector.git", "_source": "git://github.com/PolymerElements/iron-selector.git",
"_target": "^1.0.0", "_target": "^1.0.0",
"_originalSource": "polymerelements/iron-selector" "_originalSource": "PolymerElements/iron-selector"
} }