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-01 14:04:22 -04:00
parent 1bd4890f44
commit e453a5b9f7
18 changed files with 387 additions and 128 deletions

View file

@ -15,12 +15,12 @@
},
"devDependencies": {},
"ignore": [],
"version": "1.4.13",
"_release": "1.4.13",
"version": "1.4.14",
"_release": "1.4.14",
"_resolution": {
"type": "version",
"tag": "1.4.13",
"commit": "401894f5ab418314a88790ed9f41c59aa95d70bb"
"tag": "1.4.14",
"commit": "fd21288c33a836206b3f6322cd3205f63c2db53f"
},
"_source": "https://github.com/MediaBrowser/emby-webcomponents.git",
"_target": "^1.2.0",

View file

@ -74,7 +74,7 @@ define(['visibleinviewport', 'imageFetcher', 'layoutManager', 'events', 'browser
function fadeIn(elem) {
var duration = layoutManager.tv ? 200 : 300;
var duration = layoutManager.tv ? 160 : 300;
var keyframes = [
{ opacity: '0', offset: 0 },
@ -114,9 +114,7 @@ define(['visibleinviewport', 'imageFetcher', 'layoutManager', 'events', 'browser
var options = {};
if (!layoutManager.desktop) {
options.rootMargin = "125%";
}
options.rootMargin = "150%";
var observer = new IntersectionObserver(function (entries) {
for (var j = 0, length2 = entries.length; j < length2; j++) {

View file

@ -1,6 +1,6 @@
{
"name": "hls.js",
"version": "0.5.32",
"version": "0.5.33",
"license": "Apache-2.0",
"description": "Media Source Extension - HLS library, by/for Dailymotion",
"homepage": "https://github.com/dailymotion/hls.js",
@ -16,11 +16,11 @@
"test",
"tests"
],
"_release": "0.5.32",
"_release": "0.5.33",
"_resolution": {
"type": "version",
"tag": "v0.5.32",
"commit": "bceb5b05fb906e6877e48778828378c05353d2b8"
"tag": "v0.5.33",
"commit": "bee1aea020de895904cef15962e71c062338e22e"
},
"_source": "git://github.com/dailymotion/hls.js.git",
"_target": "~0.5.7",

View file

@ -474,6 +474,33 @@ whether or not to enable CEA-708 captions
parameter should be a boolean
#### ```abrEwmaFast```
(default : 0.0)
Fast bitrate Exponential moving average half-life , used to compute average bitrate
Half of the estimate is based on the last abrEwmaFast seconds of sample history.
parameter should be a float greater than 0
#### ```abrEwmaSlow```
(default : 0.0)
Slow bitrate Exponential moving average half-life , used to compute average bitrate
Half of the estimate is based on the last abrEwmaFast seconds of sample history.
parameter should be a float greater than abrEwmaFast
#### ```abrBandWidthFactor```
(default : 0.8)
scale factor to be applied against measured bandwidth average, to determine whether we can stay on current or lower quality level
If ``` abrBandWidthFactor * bandwidth average < level.bitrate ``` then ABR can switch to that level providing that it is equal or less than current level
#### ```abrBandWidthUpFactor```
(default : 0.7)
scale factor to be applied against measured bandwidth average, to determine whether we can switch up to a higher quality level
If ``` abrBandWidthUpFactor * bandwidth average < level.bitrate ``` then ABR can switch up to that quality level
## Video Binding/Unbinding API
#### ```hls.attachMedia(videoElement)```

View file

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

View file

@ -390,6 +390,10 @@ var _errors = require('../errors');
var _logger = require('../utils/logger');
var _ewmaBandwidthEstimator = require('./ewma-bandwidth-estimator');
var _ewmaBandwidthEstimator2 = _interopRequireDefault(_ewmaBandwidthEstimator);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
@ -408,12 +412,13 @@ var AbrController = function (_EventHandler) {
function AbrController(hls) {
_classCallCheck(this, AbrController);
var _this = _possibleConstructorReturn(this, Object.getPrototypeOf(AbrController).call(this, hls, _events2.default.FRAG_LOADING, _events2.default.FRAG_LOAD_PROGRESS, _events2.default.FRAG_LOADED, _events2.default.ERROR));
var _this = _possibleConstructorReturn(this, Object.getPrototypeOf(AbrController).call(this, hls, _events2.default.FRAG_LOADING, _events2.default.FRAG_LOADED, _events2.default.ERROR));
_this.lastLoadedFragLevel = 0;
_this._autoLevelCapping = -1;
_this._nextAutoLevel = -1;
_this.hls = hls;
_this.bwEstimator = new _ewmaBandwidthEstimator2.default(hls);
_this.onCheck = _this.abandonRulesCheck.bind(_this);
return _this;
}
@ -432,19 +437,6 @@ var AbrController = function (_EventHandler) {
}
this.fragCurrent = data.frag;
}
}, {
key: 'onFragLoadProgress',
value: function onFragLoadProgress(data) {
var stats = data.stats;
// only update stats if first frag loading
// if same frag is loaded multiple times, it might be in browser cache, and loaded quickly
// and leading to wrong bw estimation
if (stats.aborted === undefined && data.frag.loadCounter === 1) {
this.lastfetchduration = (performance.now() - stats.trequest) / 1000;
this.lastbw = stats.loaded * 8 / this.lastfetchduration;
//console.log(`fetchDuration:${this.lastfetchduration},bw:${(this.lastbw/1000).toFixed(0)}/${stats.aborted}`);
}
}
}, {
key: 'abandonRulesCheck',
value: function abandonRulesCheck() {
@ -502,6 +494,8 @@ var AbrController = function (_EventHandler) {
nextLoadLevel = Math.max(0, nextLoadLevel);
// force next load level in auto mode
hls.nextLoadLevel = nextLoadLevel;
// update bw estimate for this fragment before cancelling load (this will help reducing the bw)
this.bwEstimator.sample(requestDelay, frag.loaded);
// abort fragment loading ...
_logger.logger.warn('loading too slow, abort fragment loading and switch to level ' + nextLoadLevel);
//abort fragment loading
@ -516,6 +510,14 @@ var AbrController = function (_EventHandler) {
}, {
key: 'onFragLoaded',
value: function onFragLoaded(data) {
var stats = data.stats;
// only update stats on first frag loading
// if same frag is loaded multiple times, it might be in browser cache, and loaded quickly
// and leading to wrong bw estimation
if (stats.aborted === undefined && data.frag.loadCounter === 1) {
this.bwEstimator.sample(performance.now() - stats.trequest, stats.loaded);
}
// stop monitoring bw once frag loaded
this.clearTimer();
// store level id after successful fragment load
@ -561,13 +563,13 @@ var AbrController = function (_EventHandler) {
}, {
key: 'nextAutoLevel',
get: function get() {
var lastbw = this.lastbw,
hls = this.hls,
adjustedbw,
var hls = this.hls,
i,
maxAutoLevel;
if (this._autoLevelCapping === -1 && hls.levels && hls.levels.length) {
maxAutoLevel = hls.levels.length - 1;
maxAutoLevel,
levels = hls.levels,
config = hls.config;
if (this._autoLevelCapping === -1 && levels && levels.length) {
maxAutoLevel = levels.length - 1;
} else {
maxAutoLevel = this._autoLevelCapping;
}
@ -577,6 +579,8 @@ var AbrController = function (_EventHandler) {
return Math.min(this._nextAutoLevel, maxAutoLevel);
}
var avgbw = this.bwEstimator.getEstimate(),
adjustedbw = void 0;
// 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.
@ -585,11 +589,11 @@ var AbrController = function (_EventHandler) {
// be even more conservative (70%) to avoid overestimating and immediately
// switching back.
if (i <= this.lastLoadedFragLevel) {
adjustedbw = 0.8 * lastbw;
adjustedbw = config.abrBandWidthFactor * avgbw;
} else {
adjustedbw = 0.7 * lastbw;
adjustedbw = config.abrBandWidthUpFactor * avgbw;
}
if (adjustedbw < hls.levels[i].bitrate) {
if (adjustedbw < levels[i].bitrate) {
return Math.max(0, i - 1);
}
}
@ -605,7 +609,7 @@ var AbrController = function (_EventHandler) {
exports.default = AbrController;
},{"../errors":20,"../event-handler":21,"../events":22,"../helper/buffer-helper":23,"../utils/logger":36}],4:[function(require,module,exports){
},{"../errors":21,"../event-handler":22,"../events":23,"../helper/buffer-helper":24,"../utils/logger":38,"./ewma-bandwidth-estimator":6}],4:[function(require,module,exports){
'use strict';
Object.defineProperty(exports, "__esModule", {
@ -1005,7 +1009,7 @@ var BufferController = function (_EventHandler) {
exports.default = BufferController;
},{"../errors":20,"../event-handler":21,"../events":22,"../utils/logger":36}],5:[function(require,module,exports){
},{"../errors":21,"../event-handler":22,"../events":23,"../utils/logger":38}],5:[function(require,module,exports){
'use strict';
Object.defineProperty(exports, "__esModule", {
@ -1148,7 +1152,75 @@ var CapLevelController = function (_EventHandler) {
exports.default = CapLevelController;
},{"../event-handler":21,"../events":22}],6:[function(require,module,exports){
},{"../event-handler":22,"../events":23}],6:[function(require,module,exports){
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); /*
* EWMA Bandwidth Estimator
* - heavily inspired from shaka-player
* Tracks bandwidth samples and estimates available bandwidth.
* Based on the minimum of two exponentially-weighted moving averages with
* different half-lives.
*/
var _ewma = require('../utils/ewma');
var _ewma2 = _interopRequireDefault(_ewma);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
var EwmaBandWidthEstimator = function () {
function EwmaBandWidthEstimator(hls) {
_classCallCheck(this, EwmaBandWidthEstimator);
this.hls = hls;
this.defaultEstimate_ = 5e5; // 500kbps
this.minWeight_ = 0.001;
this.minDelayMs_ = 50;
this.fast_ = new _ewma2.default(hls.config.abrEwmaFast);
this.slow_ = new _ewma2.default(hls.config.abrEwmaSlow);
}
_createClass(EwmaBandWidthEstimator, [{
key: 'sample',
value: function sample(durationMs, numBytes) {
durationMs = Math.max(durationMs, this.minDelayMs_);
var bandwidth = 8000 * numBytes / durationMs;
//console.log('instant bw:'+ Math.round(bandwidth));
// we weight sample using loading duration....
var weigth = durationMs / 1000;
this.fast_.sample(weigth, bandwidth);
this.slow_.sample(weigth, bandwidth);
}
}, {
key: 'getEstimate',
value: function getEstimate() {
if (this.fast_.getTotalWeight() < this.minWeight_) {
return this.defaultEstimate_;
}
//console.log('slow estimate:'+ Math.round(this.slow_.getEstimate()));
//console.log('fast estimate:'+ Math.round(this.fast_.getEstimate()));
// Take the minimum of these two estimates. This should have the effect of
// adapting down quickly, but up more slowly.
return Math.min(this.fast_.getEstimate(), this.slow_.getEstimate());
}
}, {
key: 'destroy',
value: function destroy() {}
}]);
return EwmaBandWidthEstimator;
}();
exports.default = EwmaBandWidthEstimator;
},{"../utils/ewma":37}],7:[function(require,module,exports){
'use strict';
Object.defineProperty(exports, "__esModule", {
@ -1501,7 +1573,7 @@ var LevelController = function (_EventHandler) {
exports.default = LevelController;
},{"../errors":20,"../event-handler":21,"../events":22,"../utils/logger":36}],7:[function(require,module,exports){
},{"../errors":21,"../event-handler":22,"../events":23,"../utils/logger":38}],8:[function(require,module,exports){
'use strict';
Object.defineProperty(exports, "__esModule", {
@ -2718,7 +2790,7 @@ var StreamController = function (_EventHandler) {
exports.default = StreamController;
},{"../demux/demuxer":16,"../errors":20,"../event-handler":21,"../events":22,"../helper/buffer-helper":23,"../helper/level-helper":24,"../utils/binary-search":34,"../utils/logger":36}],8:[function(require,module,exports){
},{"../demux/demuxer":17,"../errors":21,"../event-handler":22,"../events":23,"../helper/buffer-helper":24,"../helper/level-helper":25,"../utils/binary-search":35,"../utils/logger":38}],9:[function(require,module,exports){
'use strict';
Object.defineProperty(exports, "__esModule", {
@ -2816,7 +2888,7 @@ var TimelineController = function (_EventHandler) {
exports.default = TimelineController;
},{"../event-handler":21,"../events":22,"../utils/cea-708-interpreter":35}],9:[function(require,module,exports){
},{"../event-handler":22,"../events":23,"../utils/cea-708-interpreter":36}],10:[function(require,module,exports){
'use strict';
Object.defineProperty(exports, "__esModule", {
@ -3060,7 +3132,7 @@ var AES = function () {
exports.default = AES;
},{}],10:[function(require,module,exports){
},{}],11:[function(require,module,exports){
'use strict';
Object.defineProperty(exports, "__esModule", {
@ -3246,7 +3318,7 @@ var AES128Decrypter = function () {
exports.default = AES128Decrypter;
},{"./aes":9}],11:[function(require,module,exports){
},{"./aes":10}],12:[function(require,module,exports){
'use strict';
Object.defineProperty(exports, "__esModule", {
@ -3343,7 +3415,7 @@ var Decrypter = function () {
exports.default = Decrypter;
},{"../errors":20,"../utils/logger":36,"./aes128-decrypter":10}],12:[function(require,module,exports){
},{"../errors":21,"../utils/logger":38,"./aes128-decrypter":11}],13:[function(require,module,exports){
'use strict';
Object.defineProperty(exports, "__esModule", {
@ -3471,7 +3543,7 @@ var AACDemuxer = function () {
exports.default = AACDemuxer;
},{"../demux/id3":18,"../utils/logger":36,"./adts":13}],13:[function(require,module,exports){
},{"../demux/id3":19,"../utils/logger":38,"./adts":14}],14:[function(require,module,exports){
'use strict';
Object.defineProperty(exports, "__esModule", {
@ -3618,7 +3690,7 @@ var ADTS = function () {
exports.default = ADTS;
},{"../errors":20,"../utils/logger":36}],14:[function(require,module,exports){
},{"../errors":21,"../utils/logger":38}],15:[function(require,module,exports){
'use strict';
Object.defineProperty(exports, "__esModule", {
@ -3701,7 +3773,7 @@ var DemuxerInline = function () {
exports.default = DemuxerInline;
},{"../demux/aacdemuxer":12,"../demux/tsdemuxer":19,"../errors":20,"../events":22,"../remux/mp4-remuxer":31,"../remux/passthrough-remuxer":32}],15:[function(require,module,exports){
},{"../demux/aacdemuxer":13,"../demux/tsdemuxer":20,"../errors":21,"../events":23,"../remux/mp4-remuxer":32,"../remux/passthrough-remuxer":33}],16:[function(require,module,exports){
'use strict';
Object.defineProperty(exports, "__esModule", {
@ -3790,7 +3862,7 @@ var DemuxerWorker = function DemuxerWorker(self) {
exports.default = DemuxerWorker;
},{"../demux/demuxer-inline":14,"../events":22,"events":1}],16:[function(require,module,exports){
},{"../demux/demuxer-inline":15,"../events":23,"events":1}],17:[function(require,module,exports){
'use strict';
Object.defineProperty(exports, "__esModule", {
@ -3936,7 +4008,7 @@ var Demuxer = function () {
exports.default = Demuxer;
},{"../crypt/decrypter":11,"../demux/demuxer-inline":14,"../demux/demuxer-worker":15,"../events":22,"../utils/logger":36,"webworkify":2}],17:[function(require,module,exports){
},{"../crypt/decrypter":12,"../demux/demuxer-inline":15,"../demux/demuxer-worker":16,"../events":23,"../utils/logger":38,"webworkify":2}],18:[function(require,module,exports){
'use strict';
Object.defineProperty(exports, "__esModule", {
@ -4300,7 +4372,7 @@ var ExpGolomb = function () {
exports.default = ExpGolomb;
},{"../utils/logger":36}],18:[function(require,module,exports){
},{"../utils/logger":38}],19:[function(require,module,exports){
'use strict';
Object.defineProperty(exports, "__esModule", {
@ -4453,7 +4525,7 @@ var ID3 = function () {
exports.default = ID3;
},{"../utils/logger":36}],19:[function(require,module,exports){
},{"../utils/logger":38}],20:[function(require,module,exports){
'use strict';
Object.defineProperty(exports, "__esModule", {
@ -4515,6 +4587,8 @@ var TSDemuxer = function () {
this._aacTrack = { container: 'video/mp2t', type: 'audio', id: -1, sequenceNumber: 0, samples: [], len: 0 };
this._id3Track = { type: 'id3', id: -1, sequenceNumber: 0, samples: [], len: 0 };
this._txtTrack = { type: 'text', id: -1, sequenceNumber: 0, samples: [], len: 0 };
// flush any partial content
this.aacOverFlow = null;
this.remuxer.switchLevel();
}
}, {
@ -4550,7 +4624,8 @@ var TSDemuxer = function () {
_logger.logger.log('discontinuity detected');
this.insertDiscontinuity();
this.lastCC = cc;
} else if (level !== this.lastLevel) {
}
if (level !== this.lastLevel) {
_logger.logger.log('level switch detected');
this.switchLevel();
this.lastLevel = level;
@ -4559,11 +4634,6 @@ var TSDemuxer = function () {
}
this.lastSN = sn;
if (!this.contiguous) {
// flush any partial content
this.aacOverFlow = null;
}
var pmtParsed = this.pmtParsed,
avcId = this._avcTrack.id,
aacId = this._aacTrack.id,
@ -5193,7 +5263,7 @@ var TSDemuxer = function () {
exports.default = TSDemuxer;
},{"../errors":20,"../events":22,"../utils/logger":36,"./adts":13,"./exp-golomb":17}],20:[function(require,module,exports){
},{"../errors":21,"../events":23,"../utils/logger":38,"./adts":14,"./exp-golomb":18}],21:[function(require,module,exports){
'use strict';
Object.defineProperty(exports, "__esModule", {
@ -5251,7 +5321,7 @@ var ErrorDetails = exports.ErrorDetails = {
INTERNAL_EXCEPTION: 'internalException'
};
},{}],21:[function(require,module,exports){
},{}],22:[function(require,module,exports){
'use strict';
Object.defineProperty(exports, "__esModule", {
@ -5360,7 +5430,7 @@ var EventHandler = function () {
exports.default = EventHandler;
},{"./errors":20,"./events":22,"./utils/logger":36}],22:[function(require,module,exports){
},{"./errors":21,"./events":23,"./utils/logger":38}],23:[function(require,module,exports){
'use strict';
module.exports = {
@ -5436,7 +5506,7 @@ module.exports = {
KEY_LOADED: 'hlsKeyLoaded'
};
},{}],23:[function(require,module,exports){
},{}],24:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", {
@ -5539,7 +5609,7 @@ var BufferHelper = function () {
exports.default = BufferHelper;
},{}],24:[function(require,module,exports){
},{}],25:[function(require,module,exports){
'use strict';
Object.defineProperty(exports, "__esModule", {
@ -5687,7 +5757,7 @@ var LevelHelper = function () {
exports.default = LevelHelper;
},{"../utils/logger":36}],25:[function(require,module,exports){
},{"../utils/logger":38}],26:[function(require,module,exports){
/**
* HLS interface
*/
@ -5823,7 +5893,11 @@ var Hls = function () {
streamController: _streamController2.default,
timelineController: _timelineController2.default,
enableCEA708Captions: true,
enableMP2TPassThrough: false
enableMP2TPassThrough: false,
abrEwmaFast: 0,
abrEwmaSlow: 0,
abrBandWidthFactor: 0.8,
abrBandWidthUpFactor: 0.7
};
}
return Hls.defaultConfig;
@ -6109,7 +6183,7 @@ var Hls = function () {
exports.default = Hls;
},{"./controller/abr-controller":3,"./controller/buffer-controller":4,"./controller/cap-level-controller":5,"./controller/level-controller":6,"./controller/stream-controller":7,"./controller/timeline-controller":8,"./errors":20,"./events":22,"./loader/fragment-loader":27,"./loader/key-loader":28,"./loader/playlist-loader":29,"./utils/logger":36,"./utils/xhr-loader":38,"events":1}],26:[function(require,module,exports){
},{"./controller/abr-controller":3,"./controller/buffer-controller":4,"./controller/cap-level-controller":5,"./controller/level-controller":7,"./controller/stream-controller":8,"./controller/timeline-controller":9,"./errors":21,"./events":23,"./loader/fragment-loader":28,"./loader/key-loader":29,"./loader/playlist-loader":30,"./utils/logger":38,"./utils/xhr-loader":40,"events":1}],27:[function(require,module,exports){
'use strict';
// This is mostly for support of the es6 module export
@ -6117,7 +6191,7 @@ exports.default = Hls;
// function exports like we are used to in node/commonjs
module.exports = require('./hls.js').default;
},{"./hls.js":25}],27:[function(require,module,exports){
},{"./hls.js":26}],28:[function(require,module,exports){
'use strict';
Object.defineProperty(exports, "__esModule", {
@ -6212,7 +6286,7 @@ var FragmentLoader = function (_EventHandler) {
exports.default = FragmentLoader;
},{"../errors":20,"../event-handler":21,"../events":22}],28:[function(require,module,exports){
},{"../errors":21,"../event-handler":22,"../events":23}],29:[function(require,module,exports){
'use strict';
Object.defineProperty(exports, "__esModule", {
@ -6317,7 +6391,7 @@ var KeyLoader = function (_EventHandler) {
exports.default = KeyLoader;
},{"../errors":20,"../event-handler":21,"../events":22}],29:[function(require,module,exports){
},{"../errors":21,"../event-handler":22,"../events":23}],30:[function(require,module,exports){
'use strict';
Object.defineProperty(exports, "__esModule", {
@ -6497,7 +6571,7 @@ var PlaylistLoader = function (_EventHandler) {
byteRangeEndOffset,
byteRangeStartOffset;
regexp = /(?:#EXT-X-(MEDIA-SEQUENCE):(\d+))|(?:#EXT-X-(TARGETDURATION):(\d+))|(?:#EXT-X-(KEY):(.*))|(?:#EXT(INF):([\d\.]+)[^\r\n]*([\r\n]+[^#|\r\n]+)?)|(?:#EXT-X-(BYTERANGE):([\d]+[@[\d]*)]*[\r\n]+([^#|\r\n]+)?|(?:#EXT-X-(ENDLIST))|(?:#EXT-X-(DIS)CONTINUITY))|(?:#EXT-X-(PROGRAM-DATE-TIME):(.*))/g;
regexp = /(?:#EXT-X-(MEDIA-SEQUENCE):(\d+))|(?:#EXT-X-(TARGETDURATION):(\d+))|(?:#EXT-X-(KEY):(.*))|(?:#EXT(INF):([\d\.]+)[^\r\n]*([\r\n]+[^#|\r\n]+)?)|(?:#EXT-X-(BYTERANGE):([\d]+[@[\d]*)]*[\r\n]+([^#|\r\n]+)?|(?:#EXT-X-(ENDLIST))|(?:#EXT-X-(DIS)CONTINUITY))|(?:#EXT-X-(PROGRAM-DATE-TIME):(.*)[\r\n]+([^#|\r\n]+)?)/g;
while ((result = regexp.exec(string)) !== null) {
result.shift();
result = result.filter(function (n) {
@ -6574,6 +6648,10 @@ var PlaylistLoader = function (_EventHandler) {
break;
case 'PROGRAM-DATE-TIME':
programDateTime = new Date(Date.parse(result[1]));
if (frag && !frag.url && result.length >= 3) {
frag.url = this.resolve(result[2], baseurl);
frag.programDateTime = programDateTime;
}
break;
default:
break;
@ -6673,7 +6751,7 @@ var PlaylistLoader = function (_EventHandler) {
exports.default = PlaylistLoader;
},{"../errors":20,"../event-handler":21,"../events":22,"../utils/attr-list":33,"../utils/url":37}],30:[function(require,module,exports){
},{"../errors":21,"../event-handler":22,"../events":23,"../utils/attr-list":34,"../utils/url":39}],31:[function(require,module,exports){
'use strict';
Object.defineProperty(exports, "__esModule", {
@ -7182,7 +7260,7 @@ var MP4 = function () {
exports.default = MP4;
},{}],31:[function(require,module,exports){
},{}],32:[function(require,module,exports){
'use strict';
Object.defineProperty(exports, "__esModule", {
@ -7226,7 +7304,7 @@ var MP4Remuxer = function () {
}, {
key: 'insertDiscontinuity',
value: function insertDiscontinuity() {
this._initPTS = this._initDTS = this.nextAacPts = this.nextAvcDts = undefined;
this._initPTS = this._initDTS = undefined;
}
}, {
key: 'switchLevel',
@ -7526,7 +7604,7 @@ var MP4Remuxer = function () {
}
// always adjust sample duration to avoid av sync issue
mp4Sample.duration = expectedSampleDuration;
dtsnorm = expectedSampleDuration * pes2mp4ScaleFactor + lastDTS;
ptsnorm = dtsnorm = expectedSampleDuration * pes2mp4ScaleFactor + lastDTS;
} else {
var nextAacPts = void 0,
delta = void 0;
@ -7696,7 +7774,7 @@ var MP4Remuxer = function () {
exports.default = MP4Remuxer;
},{"../errors":20,"../events":22,"../remux/mp4-generator":30,"../utils/logger":36}],32:[function(require,module,exports){
},{"../errors":21,"../events":23,"../remux/mp4-generator":31,"../utils/logger":38}],33:[function(require,module,exports){
'use strict';
Object.defineProperty(exports, "__esModule", {
@ -7791,7 +7869,7 @@ var PassThroughRemuxer = function () {
exports.default = PassThroughRemuxer;
},{"../events":22}],33:[function(require,module,exports){
},{"../events":23}],34:[function(require,module,exports){
'use strict';
Object.defineProperty(exports, "__esModule", {
@ -7898,7 +7976,7 @@ var AttrList = function () {
exports.default = AttrList;
},{}],34:[function(require,module,exports){
},{}],35:[function(require,module,exports){
"use strict";
var BinarySearch = {
@ -7943,7 +8021,7 @@ var BinarySearch = {
module.exports = BinarySearch;
},{}],35:[function(require,module,exports){
},{}],36:[function(require,module,exports){
'use strict';
Object.defineProperty(exports, "__esModule", {
@ -8363,7 +8441,66 @@ var CEA708Interpreter = function () {
exports.default = CEA708Interpreter;
},{}],36:[function(require,module,exports){
},{}],37:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
/*
* compute an Exponential Weighted moving average
* - https://en.wikipedia.org/wiki/Moving_average#Exponential_moving_average
* - heavily inspired from shaka-player
*/
var EWMA = function () {
// About half of the estimated value will be from the last |halfLife| samples by weight.
function EWMA(halfLife) {
_classCallCheck(this, EWMA);
// Larger values of alpha expire historical data more slowly.
this.alpha_ = halfLife ? Math.exp(Math.log(0.5) / halfLife) : 0;
this.estimate_ = 0;
this.totalWeight_ = 0;
}
_createClass(EWMA, [{
key: "sample",
value: function sample(weight, value) {
var adjAlpha = Math.pow(this.alpha_, weight);
this.estimate_ = value * (1 - adjAlpha) + adjAlpha * this.estimate_;
this.totalWeight_ += weight;
}
}, {
key: "getTotalWeight",
value: function getTotalWeight() {
return this.totalWeight_;
}
}, {
key: "getEstimate",
value: function getEstimate() {
if (this.alpha_) {
var zeroFactor = 1 - Math.pow(this.alpha_, this.totalWeight_);
return this.estimate_ / zeroFactor;
} else {
return this.estimate_;
}
}
}]);
return EWMA;
}();
exports.default = EWMA;
},{}],38:[function(require,module,exports){
'use strict';
Object.defineProperty(exports, "__esModule", {
@ -8446,7 +8583,7 @@ var enableLogs = exports.enableLogs = function enableLogs(debugConfig) {
var logger = exports.logger = exportedLogger;
},{}],37:[function(require,module,exports){
},{}],39:[function(require,module,exports){
'use strict';
var URLHelper = {
@ -8534,7 +8671,7 @@ var URLHelper = {
module.exports = URLHelper;
},{}],38:[function(require,module,exports){
},{}],40:[function(require,module,exports){
'use strict';
Object.defineProperty(exports, "__esModule", {
@ -8680,6 +8817,6 @@ var XhrLoader = function () {
exports.default = XhrLoader;
},{"../utils/logger":36}]},{},[26])(26)
},{"../utils/logger":38}]},{},[27])(27)
});
//# sourceMappingURL=hls.js.map

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",
"version": "0.5.32",
"version": "0.5.33",
"license": "Apache-2.0",
"description": "Media Source Extension - HLS library, by/for Dailymotion",
"homepage": "https://github.com/dailymotion/hls.js",

View file

@ -9,18 +9,19 @@ import EventHandler from '../event-handler';
import BufferHelper from '../helper/buffer-helper';
import {ErrorDetails} from '../errors';
import {logger} from '../utils/logger';
import EwmaBandWidthEstimator from './ewma-bandwidth-estimator';
class AbrController extends EventHandler {
constructor(hls) {
super(hls, Event.FRAG_LOADING,
Event.FRAG_LOAD_PROGRESS,
Event.FRAG_LOADED,
Event.ERROR);
this.lastLoadedFragLevel = 0;
this._autoLevelCapping = -1;
this._nextAutoLevel = -1;
this.hls = hls;
this.bwEstimator = new EwmaBandWidthEstimator(hls);
this.onCheck = this.abandonRulesCheck.bind(this);
}
@ -36,18 +37,6 @@ class AbrController extends EventHandler {
this.fragCurrent = data.frag;
}
onFragLoadProgress(data) {
var stats = data.stats;
// only update stats if first frag loading
// if same frag is loaded multiple times, it might be in browser cache, and loaded quickly
// and leading to wrong bw estimation
if (stats.aborted === undefined && data.frag.loadCounter === 1) {
this.lastfetchduration = (performance.now() - stats.trequest) / 1000;
this.lastbw = (stats.loaded * 8) / this.lastfetchduration;
//console.log(`fetchDuration:${this.lastfetchduration},bw:${(this.lastbw/1000).toFixed(0)}/${stats.aborted}`);
}
}
abandonRulesCheck() {
/*
monitor fragment retrieval time...
@ -100,6 +89,8 @@ class AbrController extends EventHandler {
nextLoadLevel = Math.max(0,nextLoadLevel);
// force next load level in auto mode
hls.nextLoadLevel = nextLoadLevel;
// update bw estimate for this fragment before cancelling load (this will help reducing the bw)
this.bwEstimator.sample(requestDelay,frag.loaded);
// abort fragment loading ...
logger.warn(`loading too slow, abort fragment loading and switch to level ${nextLoadLevel}`);
//abort fragment loading
@ -113,6 +104,14 @@ class AbrController extends EventHandler {
}
onFragLoaded(data) {
var stats = data.stats;
// only update stats on first frag loading
// if same frag is loaded multiple times, it might be in browser cache, and loaded quickly
// and leading to wrong bw estimation
if (stats.aborted === undefined && data.frag.loadCounter === 1) {
this.bwEstimator.sample(performance.now() - stats.trequest,stats.loaded);
}
// stop monitoring bw once frag loaded
this.clearTimer();
// store level id after successful fragment load
@ -151,9 +150,9 @@ class AbrController extends EventHandler {
}
get nextAutoLevel() {
var lastbw = this.lastbw, hls = this.hls,adjustedbw, i, maxAutoLevel;
if (this._autoLevelCapping === -1 && hls.levels && hls.levels.length) {
maxAutoLevel = hls.levels.length - 1;
var hls = this.hls, i, maxAutoLevel, levels = hls.levels, config = hls.config;
if (this._autoLevelCapping === -1 && levels && levels.length) {
maxAutoLevel = levels.length - 1;
} else {
maxAutoLevel = this._autoLevelCapping;
}
@ -163,6 +162,7 @@ class AbrController extends EventHandler {
return Math.min(this._nextAutoLevel,maxAutoLevel);
}
let avgbw = this.bwEstimator.getEstimate(),adjustedbw;
// 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.
@ -171,11 +171,11 @@ class AbrController extends EventHandler {
// be even more conservative (70%) to avoid overestimating and immediately
// switching back.
if (i <= this.lastLoadedFragLevel) {
adjustedbw = 0.8 * lastbw;
adjustedbw = config.abrBandWidthFactor * avgbw;
} else {
adjustedbw = 0.7 * lastbw;
adjustedbw = config.abrBandWidthUpFactor * avgbw;
}
if (adjustedbw < hls.levels[i].bitrate) {
if (adjustedbw < levels[i].bitrate) {
return Math.max(0, i - 1);
}
}

View file

@ -0,0 +1,49 @@
/*
* EWMA Bandwidth Estimator
* - heavily inspired from shaka-player
* Tracks bandwidth samples and estimates available bandwidth.
* Based on the minimum of two exponentially-weighted moving averages with
* different half-lives.
*/
import EWMA from '../utils/ewma';
class EwmaBandWidthEstimator {
constructor(hls) {
this.hls = hls;
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) {
durationMs = Math.max(durationMs, this.minDelayMs_);
var bandwidth = 8000* numBytes / durationMs;
//console.log('instant bw:'+ Math.round(bandwidth));
// we weight sample using loading duration....
var weigth = durationMs / 1000;
this.fast_.sample(weigth,bandwidth);
this.slow_.sample(weigth,bandwidth);
}
getEstimate() {
if (this.fast_.getTotalWeight() < this.minWeight_) {
return this.defaultEstimate_;
}
//console.log('slow estimate:'+ Math.round(this.slow_.getEstimate()));
//console.log('fast estimate:'+ Math.round(this.fast_.getEstimate()));
// Take the minimum of these two estimates. This should have the effect of
// adapting down quickly, but up more slowly.
return Math.min(this.fast_.getEstimate(),this.slow_.getEstimate());
}
destroy() {
}
}
export default EwmaBandWidthEstimator;

View file

@ -43,6 +43,8 @@
this._aacTrack = {container : 'video/mp2t', type: 'audio', id :-1, sequenceNumber: 0, samples : [], len : 0};
this._id3Track = {type: 'id3', id :-1, sequenceNumber: 0, samples : [], len : 0};
this._txtTrack = {type: 'text', id: -1, sequenceNumber: 0, samples: [], len: 0};
// flush any partial content
this.aacOverFlow = null;
this.remuxer.switchLevel();
}
@ -67,7 +69,8 @@
logger.log('discontinuity detected');
this.insertDiscontinuity();
this.lastCC = cc;
} else if (level !== this.lastLevel) {
}
if (level !== this.lastLevel) {
logger.log('level switch detected');
this.switchLevel();
this.lastLevel = level;
@ -76,11 +79,6 @@
}
this.lastSN = sn;
if(!this.contiguous) {
// flush any partial content
this.aacOverFlow = null;
}
var pmtParsed = this.pmtParsed,
avcId = this._avcTrack.id,
aacId = this._aacTrack.id,

View file

@ -80,7 +80,11 @@ class Hls {
streamController: StreamController,
timelineController: TimelineController,
enableCEA708Captions: true,
enableMP2TPassThrough : false
enableMP2TPassThrough : false,
abrEwmaFast: 0,
abrEwmaSlow: 0,
abrBandWidthFactor : 0.8,
abrBandWidthUpFactor : 0.7
};
}
return Hls.defaultConfig;

View file

@ -137,7 +137,7 @@ class PlaylistLoader extends EventHandler {
byteRangeEndOffset,
byteRangeStartOffset;
regexp = /(?:#EXT-X-(MEDIA-SEQUENCE):(\d+))|(?:#EXT-X-(TARGETDURATION):(\d+))|(?:#EXT-X-(KEY):(.*))|(?:#EXT(INF):([\d\.]+)[^\r\n]*([\r\n]+[^#|\r\n]+)?)|(?:#EXT-X-(BYTERANGE):([\d]+[@[\d]*)]*[\r\n]+([^#|\r\n]+)?|(?:#EXT-X-(ENDLIST))|(?:#EXT-X-(DIS)CONTINUITY))|(?:#EXT-X-(PROGRAM-DATE-TIME):(.*))/g;
regexp = /(?:#EXT-X-(MEDIA-SEQUENCE):(\d+))|(?:#EXT-X-(TARGETDURATION):(\d+))|(?:#EXT-X-(KEY):(.*))|(?:#EXT(INF):([\d\.]+)[^\r\n]*([\r\n]+[^#|\r\n]+)?)|(?:#EXT-X-(BYTERANGE):([\d]+[@[\d]*)]*[\r\n]+([^#|\r\n]+)?|(?:#EXT-X-(ENDLIST))|(?:#EXT-X-(DIS)CONTINUITY))|(?:#EXT-X-(PROGRAM-DATE-TIME):(.*)[\r\n]+([^#|\r\n]+)?)/g;
while ((result = regexp.exec(string)) !== null) {
result.shift();
result = result.filter(function(n) { return (n !== undefined); });
@ -212,6 +212,10 @@ class PlaylistLoader extends EventHandler {
break;
case 'PROGRAM-DATE-TIME':
programDateTime = new Date(Date.parse(result[1]));
if (frag && !frag.url && result.length >= 3) {
frag.url = this.resolve(result[2], baseurl);
frag.programDateTime = programDateTime;
}
break;
default:
break;

View file

@ -25,7 +25,7 @@ class MP4Remuxer {
}
insertDiscontinuity() {
this._initPTS = this._initDTS = this.nextAacPts = this.nextAvcDts = undefined;
this._initPTS = this._initDTS = undefined;
}
switchLevel() {
@ -303,7 +303,7 @@ class MP4Remuxer {
}
// always adjust sample duration to avoid av sync issue
mp4Sample.duration = expectedSampleDuration;
dtsnorm = expectedSampleDuration * pes2mp4ScaleFactor + lastDTS;
ptsnorm = dtsnorm = expectedSampleDuration * pes2mp4ScaleFactor + lastDTS;
} else {
let nextAacPts, delta;
if (contiguous) {

View file

@ -0,0 +1,37 @@
/*
* compute an Exponential Weighted moving average
* - https://en.wikipedia.org/wiki/Moving_average#Exponential_moving_average
* - heavily inspired from shaka-player
*/
class EWMA {
// About half of the estimated value will be from the last |halfLife| samples by weight.
constructor(halfLife) {
// Larger values of alpha expire historical data more slowly.
this.alpha_ = halfLife ? Math.exp(Math.log(0.5) / halfLife) : 0;
this.estimate_ = 0;
this.totalWeight_ = 0;
}
sample(weight,value) {
var adjAlpha = Math.pow(this.alpha_, weight);
this.estimate_ = value * (1 - adjAlpha) + adjAlpha * this.estimate_;
this.totalWeight_ += weight;
}
getTotalWeight() {
return this.totalWeight_;
}
getEstimate() {
if (this.alpha_) {
var zeroFactor = 1 - Math.pow(this.alpha_, this.totalWeight_);
return this.estimate_ / zeroFactor;
} else {
return this.estimate_;
}
}
}
export default EWMA;

View file

@ -30,14 +30,14 @@
"webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
},
"ignore": [],
"homepage": "https://github.com/polymerelements/iron-a11y-keys-behavior",
"homepage": "https://github.com/PolymerElements/iron-a11y-keys-behavior",
"_release": "1.1.2",
"_resolution": {
"type": "version",
"tag": "v1.1.2",
"commit": "0c2330c229a6fd3d200e2b84147ec6f94f17c22d"
},
"_source": "git://github.com/polymerelements/iron-a11y-keys-behavior.git",
"_source": "git://github.com/PolymerElements/iron-a11y-keys-behavior.git",
"_target": "^1.0.0",
"_originalSource": "polymerelements/iron-a11y-keys-behavior"
"_originalSource": "PolymerElements/iron-a11y-keys-behavior"
}

View file

@ -32,14 +32,14 @@
"web-component-tester": "^4.0.0",
"webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
},
"homepage": "https://github.com/PolymerElements/iron-icon",
"homepage": "https://github.com/polymerelements/iron-icon",
"_release": "1.0.8",
"_resolution": {
"type": "version",
"tag": "v1.0.8",
"commit": "f36b38928849ef3853db727faa8c9ef104d611eb"
},
"_source": "git://github.com/PolymerElements/iron-icon.git",
"_source": "git://github.com/polymerelements/iron-icon.git",
"_target": "^1.0.0",
"_originalSource": "PolymerElements/iron-icon"
"_originalSource": "polymerelements/iron-icon"
}